diff --git a/DEPS b/DEPS
index a86196f..d6cdcdb4 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'c637865d5cb133882ef8c9b7eec4a6dca22dc4d5',
+  'v8_revision': 'd2c38959a71552b4e39e4d9ee3a7cae2c84e5843',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/base/logging.h b/base/logging.h
index b1075a4..8e448aba 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -496,9 +496,60 @@
   std::string* message_;
 };
 
-// Crashes in the fastest, simplest possible way with no attempt at logging.
+// Crashes in the fastest possible way with no attempt at logging.
+// There are different constraints to satisfy here, see http://crbug.com/664209
+// for more context:
+// - The trap instructions, and hence the PC value at crash time, have to be
+//   distinct and not get folded into the same opcode by the compiler.
+//   On Linux/Android this is tricky because GCC still folds identical
+//   asm volatile blocks. The workaround is generating distinct opcodes for
+//   each CHECK using the __COUNTER__ macro.
+// - The debug info for the trap instruction has to be attributed to the source
+//   line that has the CHECK(), to make crash reports actionable. This rules
+//   out the ability of using a inline function, at least as long as clang
+//   doesn't support attribute(artificial).
+// - Failed CHECKs should produce a signal that is distinguishable from an
+//   invalid memory access, to improve the actionability of crash reports.
+// - The compiler should treat the CHECK as no-return instructions, so that the
+//   trap code can be efficiently packed in the prologue of the function and
+//   doesn't interfere with the main execution flow.
+// - When debugging, developers shouldn't be able to accidentally step over a
+//   CHECK. This is achieved by putting opcodes that will cause a non
+//   continuable exception after the actual trap instruction.
+// - Don't cause too much binary bloat.
 #if defined(COMPILER_GCC)
-#define IMMEDIATE_CRASH() __builtin_trap()
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
+// int 3 will generate a SIGTRAP.
+#define TRAP_SEQUENCE() \
+  asm volatile(         \
+      "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
+
+#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+#define TRAP_SEQUENCE() \
+  asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
+
+#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
+// This will always generate a SIGTRAP on arm64.
+#define TRAP_SEQUENCE() \
+  asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
+
+#else
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE() __builtin_trap()
+#endif  // ARCH_CPU_*
+
+#define IMMEDIATE_CRASH()    \
+  ({                         \
+    TRAP_SEQUENCE();         \
+    __builtin_unreachable(); \
+  })
+
 #elif defined(COMPILER_MSVC)
 
 // Clang is cleverer about coalescing int3s, so we need to add a unique-ish
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 9fe718c..ac920515 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -9,6 +9,16 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_POSIX)
+#include <signal.h>
+#include <unistd.h>
+#include "base/posix/eintr_wrapper.h"
+#endif  // OS_POSIX
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <ucontext.h>
+#endif
+
 #if defined(OS_WIN)
 #include <excpt.h>
 #include <windows.h>
@@ -246,7 +256,102 @@
   EXPECT_NE(addr1, addr3);
   EXPECT_NE(addr2, addr3);
 }
-#endif  // OFFICIAL_BUILD && OS_WIN
+
+#elif defined(OS_POSIX) && !defined(OS_NACL) && \
+    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY))
+
+int g_child_crash_pipe;
+
+void CheckCrashTestSighandler(int, siginfo_t* info, void* context_ptr) {
+  // Conversely to what clearly stated in "man 2 sigaction", some Linux kernels
+  // do NOT populate the |info->si_addr| in the case of a SIGTRAP. Hence we
+  // need the arch-specific boilerplate below, which is inspired by breakpad.
+  // At the same time, on OSX, ucontext.h is deprecated but si_addr works fine.
+  uintptr_t crash_addr = 0;
+#if defined(OS_MACOSX)
+  crash_addr = reinterpret_cast<uintptr_t>(info->si_addr);
+#else  // OS_POSIX && !OS_MACOSX
+  struct ucontext* context = reinterpret_cast<struct ucontext*>(context_ptr);
+#if defined(ARCH_CPU_X86)
+  crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_EIP]);
+#elif defined(ARCH_CPU_X86_64)
+  crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_RIP]);
+#elif defined(ARCH_CPU_ARMEL)
+  crash_addr = static_cast<uintptr_t>(context->uc_mcontext.arm_pc);
+#elif defined(ARCH_CPU_ARM64)
+  crash_addr = static_cast<uintptr_t>(context->uc_mcontext.pc);
+#endif  // ARCH_*
+#endif  // OS_POSIX && !OS_MACOSX
+  HANDLE_EINTR(write(g_child_crash_pipe, &crash_addr, sizeof(uintptr_t)));
+  _exit(0);
+}
+
+// CHECK causes a direct crash (without jumping to another function) only in
+// official builds. Unfortunately, continuous test coverage on official builds
+// is lower. DO_CHECK here falls back on a home-brewed implementation in
+// non-official builds, to catch regressions earlier in the CQ.
+#if defined(OFFICIAL_BUILD)
+#define DO_CHECK CHECK
+#else
+#define DO_CHECK(cond) \
+  if (!(cond))         \
+  IMMEDIATE_CRASH()
+#endif
+
+void CrashChildMain(int death_location) {
+  struct sigaction act = {};
+  act.sa_sigaction = CheckCrashTestSighandler;
+  act.sa_flags = SA_SIGINFO;
+  ASSERT_EQ(0, sigaction(SIGTRAP, &act, NULL));
+  ASSERT_EQ(0, sigaction(SIGBUS, &act, NULL));
+  ASSERT_EQ(0, sigaction(SIGILL, &act, NULL));
+  DO_CHECK(death_location != 1);
+  DO_CHECK(death_location != 2);
+  printf("\n");
+  DO_CHECK(death_location != 3);
+
+  // Should never reach this point.
+  const uintptr_t failed = 0;
+  HANDLE_EINTR(write(g_child_crash_pipe, &failed, sizeof(uintptr_t)));
+};
+
+void SpawnChildAndCrash(int death_location, uintptr_t* child_crash_addr) {
+  int pipefd[2];
+  ASSERT_EQ(0, pipe(pipefd));
+
+  int pid = fork();
+  ASSERT_GE(pid, 0);
+
+  if (pid == 0) {      // child process.
+    close(pipefd[0]);  // Close reader (parent) end.
+    g_child_crash_pipe = pipefd[1];
+    CrashChildMain(death_location);
+    FAIL() << "The child process was supposed to crash. It didn't.";
+  }
+
+  close(pipefd[1]);  // Close writer (child) end.
+  DCHECK(child_crash_addr);
+  int res = HANDLE_EINTR(read(pipefd[0], child_crash_addr, sizeof(uintptr_t)));
+  ASSERT_EQ(static_cast<int>(sizeof(uintptr_t)), res);
+}
+
+TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) {
+  uintptr_t child_crash_addr_1 = 0;
+  uintptr_t child_crash_addr_2 = 0;
+  uintptr_t child_crash_addr_3 = 0;
+
+  SpawnChildAndCrash(1, &child_crash_addr_1);
+  SpawnChildAndCrash(2, &child_crash_addr_2);
+  SpawnChildAndCrash(3, &child_crash_addr_3);
+
+  ASSERT_NE(0u, child_crash_addr_1);
+  ASSERT_NE(0u, child_crash_addr_2);
+  ASSERT_NE(0u, child_crash_addr_3);
+  ASSERT_NE(child_crash_addr_1, child_crash_addr_2);
+  ASSERT_NE(child_crash_addr_1, child_crash_addr_3);
+  ASSERT_NE(child_crash_addr_2, child_crash_addr_3);
+}
+#endif  // OS_POSIX
 
 TEST_F(LoggingTest, DebugLoggingReleaseBehavior) {
 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAAccountChangeListener.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAAccountChangeListener.java
index a9fc25986..12bc7c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAAccountChangeListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAAccountChangeListener.java
@@ -57,6 +57,17 @@
         return sInstance;
     }
 
+    /**
+     * Returns whether the permission {@link ACCOUNT_UPDATE_BROADCAST_PERMISSION} is granted by the
+     * system.
+     */
+    static boolean holdsAccountUpdatePermission() {
+        Context context = ContextUtils.getApplicationContext();
+        int result = ApiCompatibilityUtils.checkPermission(
+                context, ACCOUNT_UPDATE_BROADCAST_PERMISSION, Process.myPid(), Process.myUid());
+        return result == PackageManager.PERMISSION_GRANTED;
+    }
+
     private GSAAccountChangeListener(Context context) {
         Context applicationContext = context.getApplicationContext();
         BroadcastReceiver accountChangeReceiver = new BroadcastReceiver() {
@@ -123,12 +134,12 @@
                     // is not the same one as GSA's, then the broadcasts will never arrive.
                     // Query the package manager to know whether the permission was granted, and
                     // only switch to the broadcast mechanism if that's the case.
-                    if (ApiCompatibilityUtils.checkPermission(context,
-                                ACCOUNT_UPDATE_BROADCAST_PERMISSION, Process.myPid(),
-                                Process.myUid())
-                            == PackageManager.PERMISSION_GRANTED) {
-                        notifyGsaBroadcastsAccountChanges();
-                    }
+                    //
+                    // Note that this is technically not required, since Chrome tells GSA whether
+                    // it holds the permission when connecting to it in GSAServiceClient, but this
+                    // extra bit of paranoia protects from old versions of GSA that don't check
+                    // what Chrome sends.
+                    if (holdsAccountUpdatePermission()) notifyGsaBroadcastsAccountChanges();
                 }
 
                 // If GSA doesn't support the broadcast, we connect several times to the service per
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java
index 6983bca..34ad75aa7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java
@@ -49,6 +49,8 @@
     public static final String KEY_GSA_STATE = "ssb_service:ssb_state";
     public static final String KEY_GSA_CONTEXT = "ssb_service:ssb_context";
     public static final String KEY_GSA_PACKAGE_NAME = "ssb_service:ssb_package_name";
+    public static final String KEY_GSA_SUPPORTS_BROADCAST =
+            "ssb_service:chrome_holds_account_update_permission";
 
     @VisibleForTesting
     static final int INVALID_PSS = -1;
@@ -241,9 +243,11 @@
                         null, REQUEST_REGISTER_CLIENT);
                 registerClientMessage.replyTo = mMessenger;
                 Bundle b = mGsaHelper.getBundleForRegisteringGSAClient(mContext);
+                if (b == null) b = new Bundle();
+                b.putString(KEY_GSA_PACKAGE_NAME, mContext.getPackageName());
+                b.putBoolean(KEY_GSA_SUPPORTS_BROADCAST,
+                        GSAAccountChangeListener.holdsAccountUpdatePermission());
                 registerClientMessage.setData(b);
-                registerClientMessage.getData().putString(
-                        KEY_GSA_PACKAGE_NAME, mContext.getPackageName());
                 mService.send(registerClientMessage);
                 // Send prepare overlay message if there is a pending GSA context.
             } catch (RemoteException e) {
diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
index 04f44b5..32e04b8 100644
--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "net/base/load_flags.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
@@ -251,7 +252,31 @@
   for (Observer& observer : observers_)
     observer.OnHunspellDictionaryDownloadBegin(language_);
 
-  fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this);
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("spellcheck_hunspell_dictionary", R"(
+        semantics {
+          sender: "Spellcheck Dictionary Downloader"
+          description:
+            "When user selects a new language for spell checking in Google "
+            "Chrome, a new dictionary is downloaded for it."
+          trigger: "User selects a new language for spell checking."
+          data:
+            "The spell checking language identifier. No user identifier is "
+            "sent."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: false
+          setting:
+            "You can prevent downloading dictionaries by not selecting 'Use "
+            "this language for spell checking.' in Chrome's settings under "
+            "Lanugagues -> 'Language and input settings...'."
+          policy_exception_justification:
+            "Not implemented, considered not useful."
+        })");
+
+  fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this,
+                                     traffic_annotation);
   data_use_measurement::DataUseUserData::AttachToFetcher(
       fetcher_.get(), data_use_measurement::DataUseUserData::SPELL_CHECKER);
   fetcher_->SetRequestContext(request_context_getter_);
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index c80828b..5eb8f0df 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -6,6 +6,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/search/search.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/search/instant_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
@@ -20,6 +21,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -32,6 +34,8 @@
  public:
   LocalNTPTest() {}
 
+  GURL other_url() { return https_test_server().GetURL("/simple.html"); }
+
  protected:
   void SetUpInProcessBrowserTestFixture() override {
     ASSERT_TRUE(https_test_server().Start());
@@ -62,6 +66,63 @@
   EXPECT_TRUE(success);
 }
 
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, EmbeddedSearchAPIOnlyAvailableOnNTP) {
+  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
+  FocusOmnibox();
+
+  // Open an NTP.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), ntp_url(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+          ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  content::WebContents* active_tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  // Check that the embeddedSearch API is available.
+  bool result = false;
+  ASSERT_TRUE(
+      GetBoolFromJS(active_tab, "!!window.chrome.embeddedSearch", &result));
+  EXPECT_TRUE(result);
+
+  // Navigate somewhere else in the same tab.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), other_url(), WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  ASSERT_FALSE(search::IsInstantNTP(active_tab));
+  // Now the embeddedSearch API should have gone away.
+  ASSERT_TRUE(
+      GetBoolFromJS(active_tab, "!!window.chrome.embeddedSearch", &result));
+  EXPECT_FALSE(result);
+
+  // Navigate back to the NTP.
+  content::TestNavigationObserver back_observer(active_tab);
+  chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+  back_observer.Wait();
+  // The API should be back.
+  ASSERT_TRUE(
+      GetBoolFromJS(active_tab, "!!window.chrome.embeddedSearch", &result));
+  EXPECT_TRUE(result);
+
+  // Navigate forward to the non-NTP page.
+  content::TestNavigationObserver fwd_observer(active_tab);
+  chrome::GoForward(browser(), WindowOpenDisposition::CURRENT_TAB);
+  fwd_observer.Wait();
+  // The API should be gone.
+  ASSERT_TRUE(
+      GetBoolFromJS(active_tab, "!!window.chrome.embeddedSearch", &result));
+  EXPECT_FALSE(result);
+
+  // Navigate to a new NTP instance.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), ntp_url(), WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  // Now the API should be available again.
+  ASSERT_TRUE(
+      GetBoolFromJS(active_tab, "!!window.chrome.embeddedSearch", &result));
+  EXPECT_TRUE(result);
+}
+
 IN_PROC_BROWSER_TEST_F(LocalNTPTest, FakeboxRedirectsToOmnibox) {
   ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
   FocusOmnibox();
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
index eb423601..211ffa8 100644
--- a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -272,8 +272,7 @@
       request_throttler_active_suggestions_consumer_(
           pref_service,
           RequestThrottler::RequestType::
-              CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER),
-      weak_ptr_factory_(this) {}
+              CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER) {}
 
 RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() {
   if (waiting_for_refresh_token_) {
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher.h b/components/ntp_snippets/remote/remote_suggestions_fetcher.h
index a5f27c1..dc023dc7 100644
--- a/components/ntp_snippets/remote/remote_suggestions_fetcher.h
+++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.h
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/time/clock.h"
 #include "base/time/tick_clock.h"
@@ -209,8 +208,6 @@
   std::string last_status_;
   std::string last_fetch_json_;
 
-  base::WeakPtrFactory<RemoteSuggestionsFetcher> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcher);
 };
 
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 629fc51..1f1b6e1 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -866,10 +866,8 @@
 void EmbeddedWorkerInstance::AddMessageToConsole(
     blink::WebConsoleMessage::Level level,
     const std::string& message) {
-  if (status_ != EmbeddedWorkerStatus::RUNNING &&
-      status_ != EmbeddedWorkerStatus::STARTING) {
+  if (process_id() == ChildProcessHost::kInvalidUniqueID)
     return;
-  }
   DCHECK(client_.is_bound());
   client_->AddMessageToConsole(level, message);
 }
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 55398ed9..f276f7cf 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -746,4 +746,75 @@
   EXPECT_EQ(EmbeddedWorkerStatus::STARTING, events_[2].status);
 }
 
+class StoreMessageInstanceClient
+    : public EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient {
+ public:
+  explicit StoreMessageInstanceClient(
+      base::WeakPtr<EmbeddedWorkerTestHelper> helper)
+      : EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient(helper) {}
+
+  const std::vector<std::pair<blink::WebConsoleMessage::Level, std::string>>&
+  message() {
+    return messages_;
+  }
+
+ private:
+  void AddMessageToConsole(blink::WebConsoleMessage::Level level,
+                           const std::string& message) override {
+    messages_.push_back(std::make_pair(level, message));
+  }
+
+  std::vector<std::pair<blink::WebConsoleMessage::Level, std::string>>
+      messages_;
+};
+
+TEST_F(EmbeddedWorkerInstanceTest, AddMessageToConsole) {
+  const int64_t version_id = 55L;
+  const GURL pattern("http://example.com/");
+  const GURL url("http://example.com/worker.js");
+  std::unique_ptr<StoreMessageInstanceClient> instance_client =
+      base::MakeUnique<StoreMessageInstanceClient>(helper_->AsWeakPtr());
+  StoreMessageInstanceClient* instance_client_rawptr = instance_client.get();
+  helper_->RegisterMockInstanceClient(std::move(instance_client));
+  ASSERT_EQ(mock_instance_clients()->size(), 1UL);
+
+  std::unique_ptr<EmbeddedWorkerInstance> worker =
+      embedded_worker_registry()->CreateWorker();
+  helper_->SimulateAddProcessToPattern(pattern,
+                                       helper_->mock_render_process_id());
+  worker->AddListener(this);
+
+  // Attempt to start the worker and immediate AddMessageToConsole should not
+  // cause a crash.
+  std::pair<blink::WebConsoleMessage::Level, std::string> test_message =
+      std::make_pair(blink::WebConsoleMessage::LevelVerbose, "");
+  std::unique_ptr<EmbeddedWorkerStartParams> params =
+      CreateStartParams(version_id, pattern, url);
+  worker->Start(std::move(params), CreateEventDispatcher(),
+                base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+  worker->AddMessageToConsole(test_message.first, test_message.second);
+  base::RunLoop().RunUntilIdle();
+
+  // Messages sent before sending StartWorker message won't be dispatched.
+  ASSERT_EQ(0UL, instance_client_rawptr->message().size());
+  ASSERT_EQ(3UL, events_.size());
+  EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type);
+  EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type);
+  EXPECT_EQ(STARTED, events_[2].type);
+  EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, worker->status());
+
+  worker->AddMessageToConsole(test_message.first, test_message.second);
+  base::RunLoop().RunUntilIdle();
+
+  // Messages sent after sending StartWorker message should be reached to
+  // the renderer.
+  ASSERT_EQ(1UL, instance_client_rawptr->message().size());
+  EXPECT_EQ(test_message, instance_client_rawptr->message()[0]);
+
+  // Ensure the worker is stopped.
+  worker->Stop();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_registry.cc b/content/browser/service_worker/embedded_worker_registry.cc
index fbc629d..88a24dd2 100644
--- a/content/browser/service_worker/embedded_worker_registry.cc
+++ b/content/browser/service_worker/embedded_worker_registry.cc
@@ -11,6 +11,7 @@
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_dispatcher_host.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "ipc/ipc_message.h"
@@ -32,7 +33,6 @@
       new EmbeddedWorkerRegistry(
           context,
           old_registry->next_embedded_worker_id_);
-  registry->process_sender_map_.swap(old_registry->process_sender_map_);
   return registry;
 }
 
@@ -179,14 +179,7 @@
                                  line_number, source_url);
 }
 
-void EmbeddedWorkerRegistry::AddChildProcessSender(int process_id,
-                                                   IPC::Sender* sender) {
-  process_sender_map_[process_id] = sender;
-  DCHECK(!base::ContainsKey(worker_process_map_, process_id));
-}
-
-void EmbeddedWorkerRegistry::RemoveChildProcessSender(int process_id) {
-  process_sender_map_.erase(process_id);
+void EmbeddedWorkerRegistry::RemoveProcess(int process_id) {
   std::map<int, std::set<int> >::iterator found =
       worker_process_map_.find(process_id);
   if (found != worker_process_map_.end()) {
@@ -196,10 +189,13 @@
          ++it) {
       int embedded_worker_id = *it;
       DCHECK(base::ContainsKey(worker_map_, embedded_worker_id));
-      // Somehow the worker thread has lost contact with the browser process.
-      // The renderer may have been killed.  Set the worker's status to STOPPED
-      // so a new thread can be created for this version. Use OnDetached rather
-      // than OnStopped so UMA doesn't record it as a normal stoppage.
+      // RemoveProcess is typically called after the running workers on the
+      // process have been stopped, so if there is a running worker at this
+      // point somehow the worker thread has lost contact with the browser
+      // process.
+      // Set the worker's status to STOPPED so a new thread can be created for
+      // this version. Use OnDetached rather than OnStopped so UMA doesn't
+      // record it as a normal stoppage.
       worker_map_[embedded_worker_id]->OnDetached();
     }
     worker_process_map_.erase(found);
@@ -236,10 +232,6 @@
 
 void EmbeddedWorkerRegistry::BindWorkerToProcess(int process_id,
                                                  int embedded_worker_id) {
-  // The ServiceWorkerDispatcherHost is supposed to be created when the process
-  // is created, and keep an entry in process_sender_map_ for its whole
-  // lifetime.
-  DCHECK(base::ContainsKey(process_sender_map_, process_id));
   DCHECK(GetWorker(embedded_worker_id));
   DCHECK_EQ(GetWorker(embedded_worker_id)->process_id(), process_id);
   DCHECK(
@@ -254,10 +246,10 @@
   std::unique_ptr<IPC::Message> message(message_ptr);
   if (!context_)
     return SERVICE_WORKER_ERROR_ABORT;
-  ProcessToSenderMap::iterator found = process_sender_map_.find(process_id);
-  if (found == process_sender_map_.end())
+  IPC::Sender* sender = context_->GetDispatcherHost(process_id);
+  if (!sender)
     return SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND;
-  if (!found->second->Send(message.release()))
+  if (!sender->Send(message.release()))
     return SERVICE_WORKER_ERROR_IPC_FAILED;
   return SERVICE_WORKER_OK;
 }
diff --git a/content/browser/service_worker/embedded_worker_registry.h b/content/browser/service_worker/embedded_worker_registry.h
index 8561ce4..75b7666 100644
--- a/content/browser/service_worker/embedded_worker_registry.h
+++ b/content/browser/service_worker/embedded_worker_registry.h
@@ -10,7 +10,6 @@
 #include <set>
 #include <vector>
 
-#include "base/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -23,7 +22,6 @@
 
 namespace IPC {
 class Message;
-class Sender;
 }
 
 namespace content {
@@ -39,8 +37,6 @@
 class CONTENT_EXPORT EmbeddedWorkerRegistry
     : public NON_EXPORTED_BASE(base::RefCounted<EmbeddedWorkerRegistry>) {
  public:
-  typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;
-
   static scoped_refptr<EmbeddedWorkerRegistry> Create(
       const base::WeakPtr<ServiceWorkerContextCore>& contxet);
 
@@ -88,9 +84,13 @@
                               int line_number,
                               const GURL& source_url);
 
-  // Keeps a map from process_id to sender information.
-  void AddChildProcessSender(int process_id, IPC::Sender* sender);
-  void RemoveChildProcessSender(int process_id);
+  // Removes information about the service workers running on the process and
+  // calls ServiceWorkerVersion::OnDetached() on each. Called when the process
+  // is terminated. Under normal operation, the workers should already have
+  // been stopped before the process is terminated, in which case this function
+  // does nothing. But in some cases the process can be terminated unexpectedly
+  // or the workers can fail to stop cleanly.
+  void RemoveProcess(int process_id);
 
   // Returns an embedded worker instance for given |embedded_worker_id|.
   EmbeddedWorkerInstance* GetWorker(int embedded_worker_id);
@@ -107,7 +107,6 @@
                            RemoveWorkerInSharedProcess);
 
   using WorkerInstanceMap = std::map<int, EmbeddedWorkerInstance*>;
-  using ProcessToSenderMap = std::map<int, IPC::Sender*>;
 
   EmbeddedWorkerRegistry(
       const base::WeakPtr<ServiceWorkerContextCore>& context,
@@ -136,7 +135,6 @@
   base::WeakPtr<ServiceWorkerContextCore> context_;
 
   WorkerInstanceMap worker_map_;
-  ProcessToSenderMap process_sender_map_;
 
   // Map from process_id to embedded_worker_id.
   // This map only contains starting and running workers.
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 3c027df..c379c3a 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -20,6 +20,7 @@
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_dispatcher_host.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
 #include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/common/service_worker/service_worker_messages.h"
@@ -36,6 +37,29 @@
 
 namespace content {
 
+namespace {
+
+class MockServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost {
+ public:
+  MockServiceWorkerDispatcherHost(int process_id,
+                                  ResourceContext* resource_context,
+                                  IPC::Sender* sender)
+      : ServiceWorkerDispatcherHost(process_id, resource_context),
+        sender_(sender) {}
+
+  bool Send(IPC::Message* message) override { return sender_->Send(message); }
+
+ protected:
+  ~MockServiceWorkerDispatcherHost() override {}
+
+ private:
+  IPC::Sender* sender_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerDispatcherHost);
+};
+
+}  // namespace
+
 EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::
     MockEmbeddedWorkerInstanceClient(
         base::WeakPtr<EmbeddedWorkerTestHelper> helper)
@@ -219,7 +243,14 @@
                          base::ThreadTaskRunnerHandle::Get(), nullptr, nullptr);
   wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id());
   wrapper_->process_manager()->SetNewProcessIdForTest(new_render_process_id());
-  registry()->AddChildProcessSender(mock_render_process_id_, this);
+
+  scoped_refptr<ServiceWorkerDispatcherHost> dispatcher_host(
+      new MockServiceWorkerDispatcherHost(
+          mock_render_process_id_, browser_context_->GetResourceContext(),
+          this));
+  wrapper_->context()->AddDispatcherHost(mock_render_process_id_,
+                                         dispatcher_host.get());
+  dispatcher_hosts_[mock_render_process_id_] = std::move(dispatcher_host);
 
   // Setup process level interface registry.
   render_process_interface_registry_ =
@@ -235,7 +266,13 @@
 
 void EmbeddedWorkerTestHelper::SimulateAddProcessToPattern(const GURL& pattern,
                                                            int process_id) {
-  registry()->AddChildProcessSender(process_id, this);
+  if (!context()->GetDispatcherHost(process_id)) {
+    scoped_refptr<ServiceWorkerDispatcherHost> dispatcher_host(
+        new MockServiceWorkerDispatcherHost(
+            process_id, browser_context_->GetResourceContext(), this));
+    wrapper_->context()->AddDispatcherHost(process_id, dispatcher_host.get());
+    dispatcher_hosts_[process_id] = std::move(dispatcher_host);
+  }
   wrapper_->process_manager()->AddProcessReferenceToPattern(pattern,
                                                             process_id);
 }
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index 96dacef..9a8af65 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -40,6 +40,7 @@
 class MockRenderProcessHost;
 class ServiceWorkerContextCore;
 class ServiceWorkerContextWrapper;
+class ServiceWorkerDispatcherHost;
 class TestBrowserContext;
 struct EmbeddedWorkerStartParams;
 struct PlatformNotificationData;
@@ -297,6 +298,9 @@
   int mock_render_process_id_;
   int new_mock_render_process_id_;
 
+  std::map<int /* process_id */, scoped_refptr<ServiceWorkerDispatcherHost>>
+      dispatcher_hosts_;
+
   std::unique_ptr<service_manager::InterfaceRegistry>
       render_process_interface_registry_;
   std::unique_ptr<service_manager::InterfaceRegistry>
diff --git a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
index 5727b00..1045db1 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
+++ b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
@@ -68,27 +68,19 @@
     const int64_t kRegistrationId = 0;
     const int64_t kVersionId = 0;
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
+
+    // Create a registration for the worker which has foreign fetch event
+    // handler.
     registration_ = new ServiceWorkerRegistration(kScope, kRegistrationId,
                                                   context()->AsWeakPtr());
     version_ = new ServiceWorkerVersion(registration_.get(), kResource1,
                                         kVersionId, context()->AsWeakPtr());
-
     version_->set_foreign_fetch_scopes({kScope});
 
-    // An empty host.
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), nullptr));
-    host->SetDocumentUrl(GURL("https://host/scope/"));
-    provider_host_ = host->AsWeakPtr();
-    context()->AddProviderHost(std::move(host));
-
     context()->storage()->LazyInitialize(base::Bind(&EmptyCallback));
     base::RunLoop().RunUntilIdle();
 
+    // Persist the registration data.
     std::vector<ServiceWorkerDatabase::ResourceRecord> records;
     records.push_back(
         ServiceWorkerDatabase::ResourceRecord(10, version_->script_url(), 100));
@@ -162,17 +154,30 @@
     return ForeignFetchRequestHandler::GetHandler(request_.get());
   }
 
-  void CreateServiceWorkerTypeProviderHost() {
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-            ServiceWorkerProviderHost::FrameSecurityLevel::UNINITIALIZED,
-            context()->AsWeakPtr(), nullptr));
+  void CreateWindowTypeProviderHost() {
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(
+            helper_->mock_render_process_id(), kMockProviderId,
+            true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
+    EXPECT_FALSE(
+        context()->GetProviderHost(host->process_id(), host->provider_id()));
+    host->SetDocumentUrl(GURL("https://host/scope/"));
     provider_host_ = host->AsWeakPtr();
-    context()->RemoveProviderHost(host->process_id(), host->provider_id());
+    context()->AddProviderHost(std::move(host));
+  }
+
+  void CreateServiceWorkerTypeProviderHost() {
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForServiceWorkerContext(
+            helper_->mock_render_process_id(), kMockProviderId,
+            true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
+    EXPECT_FALSE(
+        context()->GetProviderHost(host->process_id(), host->provider_id()));
+    provider_host_ = host->AsWeakPtr();
     context()->AddProviderHost(std::move(host));
 
+    // Create another worker whose requests will be intercepted by the foreign
+    // fetch event handler.
     scoped_refptr<ServiceWorkerRegistration> registration =
         new ServiceWorkerRegistration(GURL("https://host/scope"), 1L,
                                       context()->AsWeakPtr());
@@ -236,6 +241,7 @@
 };
 
 TEST_F(ForeignFetchRequestHandlerTest, CheckOriginTrialToken_NoToken) {
+  CreateWindowTypeProviderHost();
   EXPECT_TRUE(CheckOriginTrialToken(version()));
   std::unique_ptr<net::HttpResponseInfo> http_info(
       CreateTestHttpResponseInfo());
@@ -244,6 +250,7 @@
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, CheckOriginTrialToken_ValidToken) {
+  CreateWindowTypeProviderHost();
   EXPECT_TRUE(CheckOriginTrialToken(version()));
   std::unique_ptr<net::HttpResponseInfo> http_info(
       CreateTestHttpResponseInfo());
@@ -263,6 +270,7 @@
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, CheckOriginTrialToken_InvalidToken) {
+  CreateWindowTypeProviderHost();
   EXPECT_TRUE(CheckOriginTrialToken(version()));
   std::unique_ptr<net::HttpResponseInfo> http_info(
       CreateTestHttpResponseInfo());
@@ -281,6 +289,7 @@
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, CheckOriginTrialToken_ExpiredToken) {
+  CreateWindowTypeProviderHost();
   EXPECT_TRUE(CheckOriginTrialToken(version()));
   std::unique_ptr<net::HttpResponseInfo> http_info(
       CreateTestHttpResponseInfo());
@@ -299,11 +308,13 @@
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_Success) {
+  CreateWindowTypeProviderHost();
   EXPECT_TRUE(InitializeHandler(kValidUrl, RESOURCE_TYPE_IMAGE,
                                 nullptr /* initiator */));
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_WrongResourceType) {
+  CreateWindowTypeProviderHost();
   EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_MAIN_FRAME,
                                  nullptr /* initiator */));
   EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_SUB_FRAME,
@@ -317,23 +328,30 @@
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_SameOriginRequest) {
+  CreateWindowTypeProviderHost();
   EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_IMAGE,
                                  kValidUrl /* initiator */));
 }
 
 TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_NoRegisteredHandlers) {
+  CreateWindowTypeProviderHost();
   EXPECT_FALSE(InitializeHandler("https://invalid.example.com/foo",
                                  RESOURCE_TYPE_IMAGE, nullptr /* initiator */));
 }
 
-TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_TimeoutBehavior) {
+TEST_F(ForeignFetchRequestHandlerTest,
+       InitializeHandler_TimeoutBehaviorForWindow) {
+  CreateWindowTypeProviderHost();
   ForeignFetchRequestHandler* handler =
       InitializeHandler("https://valid.example.com/foo", RESOURCE_TYPE_IMAGE,
                         nullptr /* initiator */);
   ASSERT_TRUE(handler);
 
   EXPECT_EQ(base::nullopt, timeout_for_request(handler));
+}
 
+TEST_F(ForeignFetchRequestHandlerTest,
+       InitializeHandler_TimeoutBehaviorForServiceWorker) {
   CreateServiceWorkerTypeProviderHost();
   ServiceWorkerVersion* version = provider_host()->running_hosted_version();
   std::unique_ptr<net::HttpResponseInfo> http_info(
@@ -360,10 +378,12 @@
   EXPECT_EQ(base::TimeDelta::FromSeconds(6), remaining_time);
 
   // Make sure new request only gets remaining timeout.
-  handler = InitializeHandler("https://valid.example.com/foo",
-                              RESOURCE_TYPE_IMAGE, nullptr /* initiator */);
+  ForeignFetchRequestHandler* handler =
+      InitializeHandler("https://valid.example.com/foo", RESOURCE_TYPE_IMAGE,
+                        nullptr /* initiator */);
   ASSERT_TRUE(handler);
-  EXPECT_EQ(remaining_time, timeout_for_request(handler));
+  ASSERT_TRUE(timeout_for_request(handler).has_value());
+  EXPECT_EQ(remaining_time, timeout_for_request(handler).value());
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/link_header_support_unittest.cc b/content/browser/service_worker/link_header_support_unittest.cc
index ba66b30d..869ea41 100644
--- a/content/browser/service_worker/link_header_support_unittest.cc
+++ b/content/browser/service_worker/link_header_support_unittest.cc
@@ -65,16 +65,6 @@
 
   void SetUp() override {
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
-
-    // An empty host.
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            render_process_id(), MSG_ROUTING_NONE, kMockProviderId,
-            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::UNINITIALIZED,
-            context()->AsWeakPtr(), nullptr));
-    provider_host_ = host->AsWeakPtr();
-    context()->AddProviderHost(std::move(host));
   }
 
   void TearDown() override { helper_.reset(); }
@@ -86,15 +76,38 @@
   ServiceWorkerProviderHost* provider_host() { return provider_host_.get(); }
   int render_process_id() const { return helper_->mock_render_process_id(); }
 
-  void CreateServiceWorkerProviderHost() {
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            render_process_id(), MSG_ROUTING_NONE, kMockProviderId,
-            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-            ServiceWorkerProviderHost::FrameSecurityLevel::UNINITIALIZED,
-            context()->AsWeakPtr(), nullptr));
+  void CreateDocumentProviderHost() {
+    // An empty host.
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(render_process_id(), kMockProviderId,
+                                    true /* is_parent_frame_secure */,
+                                    context()->AsWeakPtr());
     provider_host_ = host->AsWeakPtr();
-    context()->RemoveProviderHost(host->process_id(), host->provider_id());
+    EXPECT_FALSE(
+        context()->GetProviderHost(host->process_id(), host->provider_id()));
+    context()->AddProviderHost(std::move(host));
+  }
+
+  void CreateInsecureDocumentProviderHost() {
+    // An empty host.
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(render_process_id(), kMockProviderId,
+                                    false /* is_parent_frame_secure */,
+                                    context()->AsWeakPtr());
+    provider_host_ = host->AsWeakPtr();
+    EXPECT_FALSE(
+        context()->GetProviderHost(host->process_id(), host->provider_id()));
+    context()->AddProviderHost(std::move(host));
+  }
+
+  void CreateServiceWorkerProviderHost() {
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForServiceWorkerContext(
+            render_process_id(), kMockProviderId,
+            true /* is_parent_frame_secure */, context()->AsWeakPtr());
+    provider_host_ = host->AsWeakPtr();
+    EXPECT_FALSE(
+        context()->GetProviderHost(host->process_id(), host->provider_id()));
     context()->AddProviderHost(std::move(host));
 
     scoped_refptr<ServiceWorkerRegistration> registration =
@@ -157,6 +170,7 @@
 };
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_Basic) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foo/bar/")).get(),
       "<../foo.js>; rel=serviceworker", context_wrapper());
@@ -170,6 +184,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_ScopeWithFragment) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foo/bar/")).get(),
       "<../bar.js>; rel=serviceworker; scope=\"scope#ref\"", context_wrapper());
@@ -184,6 +199,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_ScopeAbsoluteUrl) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foo/bar/")).get(),
       "<bar.js>; rel=serviceworker; "
@@ -200,6 +216,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_ScopeDifferentOrigin) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<bar.js>; rel=serviceworker; scope=\"https://google.com/scope\"",
@@ -211,6 +228,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_ScopeUrlEncodedSlash) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<bar.js>; rel=serviceworker; scope=\"./foo%2Fbar\"", context_wrapper());
@@ -222,6 +240,7 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_ScriptUrlEncodedSlash) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<foo%2Fbar.js>; rel=serviceworker", context_wrapper());
@@ -232,6 +251,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_ScriptAbsoluteUrl) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<https://example.com/bar.js>; rel=serviceworker; scope=foo",
@@ -247,6 +267,7 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_ScriptDifferentOrigin) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<https://google.com/bar.js>; rel=serviceworker; scope=foo",
@@ -258,6 +279,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_MultipleWorkers) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<bar.js>; rel=serviceworker; scope=foo, <baz.js>; "
@@ -277,6 +299,7 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_ValidAndInvalidValues) {
+  CreateDocumentProviderHost();
   ProcessLinkHeaderForRequest(
       CreateSubresourceRequest(GURL("https://example.com/foobar/")).get(),
       "<https://google.com/bar.js>; rel=serviceworker; scope=foo, <baz.js>; "
@@ -292,6 +315,7 @@
 }
 
 TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_InsecureContext) {
+  CreateDocumentProviderHost();
   std::unique_ptr<net::URLRequest> request =
       CreateSubresourceRequest(GURL("https://example.com/foo/bar/"));
   ResourceRequestInfoImpl::ForRequest(request.get())
@@ -306,13 +330,13 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_NavigationFromInsecureContextToSecureContext) {
+  CreateDocumentProviderHost();
   std::unique_ptr<net::URLRequest> request = CreateRequest(
       GURL("https://example.com/foo/bar/"), RESOURCE_TYPE_MAIN_FRAME);
   ResourceRequestInfoImpl::ForRequest(request.get())
       ->set_initiated_in_secure_context_for_testing(false);
 
   provider_host()->SetDocumentUrl(GURL("https://example.com/foo/bar/"));
-  provider_host()->set_parent_frame_secure(true);
 
   ProcessLinkHeaderForRequest(request.get(), "<../foo.js>; rel=serviceworker",
                               context_wrapper());
@@ -327,8 +351,8 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_NavigationToInsecureContext) {
+  CreateDocumentProviderHost();
   provider_host()->SetDocumentUrl(GURL("http://example.com/foo/bar/"));
-  provider_host()->set_parent_frame_secure(true);
   ProcessLinkHeaderForRequest(CreateRequest(GURL("http://example.com/foo/bar/"),
                                             RESOURCE_TYPE_MAIN_FRAME)
                                   .get(),
@@ -342,8 +366,8 @@
 
 TEST_F(LinkHeaderServiceWorkerTest,
        InstallServiceWorker_NavigationToInsecureHttpsContext) {
+  CreateInsecureDocumentProviderHost();
   provider_host()->SetDocumentUrl(GURL("https://example.com/foo/bar/"));
-  provider_host()->set_parent_frame_secure(false);
   ProcessLinkHeaderForRequest(
       CreateRequest(GURL("https://example.com/foo/bar/"),
                     RESOURCE_TYPE_MAIN_FRAME)
@@ -371,12 +395,10 @@
        InstallServiceWorker_FromWorkerWithControllees) {
   CreateServiceWorkerProviderHost();
 
-  std::unique_ptr<ServiceWorkerProviderHost> controllee(
-      new ServiceWorkerProviderHost(
-          render_process_id(), MSG_ROUTING_NONE, kMockProviderId,
-          SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-          ServiceWorkerProviderHost::FrameSecurityLevel::UNINITIALIZED,
-          context()->AsWeakPtr(), nullptr));
+  std::unique_ptr<ServiceWorkerProviderHost> controllee =
+      CreateProviderHostForWindow(render_process_id(), kMockProviderId,
+                                  true /* is_parent_frame_secure */,
+                                  context()->AsWeakPtr());
   provider_host()->running_hosted_version()->AddControllee(controllee.get());
 
   ProcessLinkHeaderForRequest(
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 3460d4e..1fdc638 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -598,13 +598,11 @@
 
   void AddControlleeOnIOThread() {
     ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            33 /* dummy render process id */,
-            MSG_ROUTING_NONE /* render_frame_id */, 1 /* dummy provider_id */,
-            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            wrapper()->context()->AsWeakPtr(), NULL));
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(33 /* dummy render process id */,
+                                    1 /* dummy provider_id */,
+                                    true /* is_parent_frame_secure */,
+                                    wrapper()->context()->AsWeakPtr());
     host->SetDocumentUrl(
         embedded_test_server()->GetURL("/service_worker/host"));
     host->AssociateRegistration(registration_.get(),
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 55d93d4..a0bbc66 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -24,6 +24,7 @@
 #include "content/browser/service_worker/service_worker_context_observer.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_database_task_manager.h"
+#include "content/browser/service_worker/service_worker_dispatcher_host.h"
 #include "content/browser/service_worker/service_worker_info.h"
 #include "content/browser/service_worker/service_worker_job_coordinator.h"
 #include "content/browser/service_worker/service_worker_process_manager.h"
@@ -220,8 +221,8 @@
     base::ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list,
     ServiceWorkerContextWrapper* wrapper)
     : wrapper_(wrapper),
-      providers_(new ProcessToProviderMap),
-      provider_by_uuid_(new ProviderByClientUUIDMap),
+      providers_(base::MakeUnique<ProcessToProviderMap>()),
+      provider_by_uuid_(base::MakeUnique<ProviderByClientUUIDMap>()),
       force_update_on_page_load_(false),
       next_handle_id_(0),
       next_registration_handle_id_(0),
@@ -241,6 +242,7 @@
     ServiceWorkerContextCore* old_context,
     ServiceWorkerContextWrapper* wrapper)
     : wrapper_(wrapper),
+      dispatcher_hosts_(std::move(old_context->dispatcher_hosts_)),
       providers_(old_context->providers_.release()),
       provider_by_uuid_(old_context->provider_by_uuid_.release()),
       next_handle_id_(old_context->next_handle_id_),
@@ -268,12 +270,26 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-ServiceWorkerProviderHost* ServiceWorkerContextCore::GetProviderHost(
-    int process_id, int provider_id) {
-  ProviderMap* map = GetProviderMapForProcess(process_id);
-  if (!map)
-    return NULL;
-  return map->Lookup(provider_id);
+void ServiceWorkerContextCore::AddDispatcherHost(
+    int process_id,
+    content::ServiceWorkerDispatcherHost* dispatcher_host) {
+  DCHECK(dispatcher_hosts_.find(process_id) == dispatcher_hosts_.end());
+  dispatcher_hosts_[process_id] = dispatcher_host;
+}
+
+ServiceWorkerDispatcherHost* ServiceWorkerContextCore::GetDispatcherHost(
+    int process_id) {
+  auto it = dispatcher_hosts_.find(process_id);
+  if (it == dispatcher_hosts_.end())
+    return nullptr;
+  return it->second;
+}
+
+void ServiceWorkerContextCore::RemoveDispatcherHost(int process_id) {
+  DCHECK(dispatcher_hosts_.find(process_id) != dispatcher_hosts_.end());
+  RemoveAllProviderHostsForProcess(process_id);
+  embedded_worker_registry_->RemoveProcess(process_id);
+  dispatcher_hosts_.erase(process_id);
 }
 
 void ServiceWorkerContextCore::AddProviderHost(
@@ -288,6 +304,15 @@
   map->AddWithID(std::move(host), provider_id);
 }
 
+ServiceWorkerProviderHost* ServiceWorkerContextCore::GetProviderHost(
+    int process_id,
+    int provider_id) {
+  ProviderMap* map = GetProviderMapForProcess(process_id);
+  if (!map)
+    return nullptr;
+  return map->Lookup(provider_id);
+}
+
 void ServiceWorkerContextCore::RemoveProviderHost(
     int process_id, int provider_id) {
   ProviderMap* map = GetProviderMapForProcess(process_id);
@@ -505,7 +530,7 @@
 ServiceWorkerRegistration* ServiceWorkerContextCore::GetLiveRegistration(
     int64_t id) {
   RegistrationsMap::iterator it = live_registrations_.find(id);
-  return (it != live_registrations_.end()) ? it->second : NULL;
+  return (it != live_registrations_.end()) ? it->second : nullptr;
 }
 
 void ServiceWorkerContextCore::AddLiveRegistration(
@@ -527,7 +552,7 @@
 
 ServiceWorkerVersion* ServiceWorkerContextCore::GetLiveVersion(int64_t id) {
   VersionMap::iterator it = live_versions_.find(id);
-  return (it != live_versions_.end()) ? it->second : NULL;
+  return (it != live_versions_.end()) ? it->second : nullptr;
 }
 
 // PlzNavigate
@@ -636,14 +661,7 @@
   ProviderMap* map = GetProviderMapForProcess(process_id);
   ServiceWorkerProviderHost* transferee = map->Lookup(provider_id);
   std::unique_ptr<ServiceWorkerProviderHost> replacement =
-      base::MakeUnique<ServiceWorkerProviderHost>(
-          process_id, transferee->frame_id(), provider_id,
-          transferee->provider_type(),
-          transferee->is_parent_frame_secure()
-              ? ServiceWorkerProviderHost::FrameSecurityLevel::SECURE
-              : ServiceWorkerProviderHost::FrameSecurityLevel::INSECURE,
-          AsWeakPtr(), transferee->dispatcher_host());
-  transferee->PrepareForCrossSiteTransfer();
+      transferee->PrepareForCrossSiteTransfer();
   return map->Replace(provider_id, std::move(replacement));
 }
 
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 4832b8c4..bb1fc899 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -44,6 +44,7 @@
 class ServiceWorkerContextObserver;
 class ServiceWorkerContextWrapper;
 class ServiceWorkerDatabaseTaskManager;
+class ServiceWorkerDispatcherHost;
 class ServiceWorkerJobCoordinator;
 class ServiceWorkerNavigationHandleCore;
 class ServiceWorkerProviderHost;
@@ -154,10 +155,17 @@
     return job_coordinator_.get();
   }
 
+  // Maintains DispatcherHosts to exchange service worker related messages
+  // through them. The DispatcherHosts are not owned by this class.
+  void AddDispatcherHost(int process_id,
+                         ServiceWorkerDispatcherHost* dispatcher_host);
+  ServiceWorkerDispatcherHost* GetDispatcherHost(int process_id);
+  void RemoveDispatcherHost(int process_id);
+
   // The context class owns the set of ProviderHosts.
-  ServiceWorkerProviderHost* GetProviderHost(int process_id, int provider_id);
   void AddProviderHost(
       std::unique_ptr<ServiceWorkerProviderHost> provider_host);
+  ServiceWorkerProviderHost* GetProviderHost(int process_id, int provider_id);
   void RemoveProviderHost(int process_id, int provider_id);
   void RemoveAllProviderHostsForProcess(int process_id);
   std::unique_ptr<ProviderHostIterator> GetProviderHostIterator();
@@ -342,6 +350,8 @@
   // because the Wrapper::Shutdown call that hops threads to destroy |this| uses
   // Bind() to hold a reference to |wrapper_| until |this| is fully destroyed.
   ServiceWorkerContextWrapper* wrapper_;
+  std::map<int /* process_id */, ServiceWorkerDispatcherHost*>
+      dispatcher_hosts_;
   std::unique_ptr<ProcessToProviderMap> providers_;
   std::unique_ptr<ProviderByClientUUIDMap> provider_by_uuid_;
   std::unique_ptr<ServiceWorkerStorage> storage_;
diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
index c6ed19f..cae7d7e 100644
--- a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
@@ -87,13 +87,10 @@
   ServiceWorkerContextCore* context() const { return helper_->context(); }
 
   void SetUpProvider() {
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(),
-            MSG_ROUTING_NONE /* render_frame_id */, 1 /* provider_id */,
-            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), nullptr));
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForServiceWorkerContext(
+            helper_->mock_render_process_id(), 1 /* provider_id */,
+            true /* is_parent_frame_secure */, context()->AsWeakPtr());
     provider_host_ = host->AsWeakPtr();
     context()->AddProviderHost(std::move(host));
     provider_host_->running_hosted_version_ = version_;
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 761edd79..1a49d8b 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -586,41 +586,41 @@
   int provider_id = 1;
 
   // Host1 (provider_id=1): process_id=1, origin1.
-  ServiceWorkerProviderHost* host1(new ServiceWorkerProviderHost(
-      kRenderProcessId1, MSG_ROUTING_NONE, provider_id++,
-      SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), nullptr));
+  std::unique_ptr<ServiceWorkerProviderHost> host1 =
+      CreateProviderHostForWindow(kRenderProcessId1, provider_id++,
+                                  true /* is_parent_frame_secure */,
+                                  context()->AsWeakPtr());
   host1->SetDocumentUrl(kOrigin1);
 
   // Host2 (provider_id=2): process_id=2, origin2.
-  ServiceWorkerProviderHost* host2(new ServiceWorkerProviderHost(
-      kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
-      SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), nullptr));
+  std::unique_ptr<ServiceWorkerProviderHost> host2 =
+      CreateProviderHostForWindow(kRenderProcessId2, provider_id++,
+                                  true /* is_parent_frame_secure */,
+                                  context()->AsWeakPtr());
   host2->SetDocumentUrl(kOrigin2);
 
   // Host3 (provider_id=3): process_id=2, origin1.
-  ServiceWorkerProviderHost* host3(new ServiceWorkerProviderHost(
-      kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
-      SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), nullptr));
+  std::unique_ptr<ServiceWorkerProviderHost> host3 =
+      CreateProviderHostForWindow(kRenderProcessId2, provider_id++,
+                                  true /* is_parent_frame_secure */,
+                                  context()->AsWeakPtr());
   host3->SetDocumentUrl(kOrigin1);
 
   // Host4 (provider_id=4): process_id=2, origin2, for ServiceWorker.
-  ServiceWorkerProviderHost* host4(new ServiceWorkerProviderHost(
-      kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
-      SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), nullptr));
+  std::unique_ptr<ServiceWorkerProviderHost> host4 =
+      CreateProviderHostForServiceWorkerContext(
+          kRenderProcessId2, provider_id++, true /* is_parent_frame_secure */,
+          context()->AsWeakPtr());
   host4->SetDocumentUrl(kOrigin2);
 
-  context()->AddProviderHost(base::WrapUnique(host1));
-  context()->AddProviderHost(base::WrapUnique(host2));
-  context()->AddProviderHost(base::WrapUnique(host3));
-  context()->AddProviderHost(base::WrapUnique(host4));
+  ServiceWorkerProviderHost* host1_raw = host1.get();
+  ServiceWorkerProviderHost* host2_raw = host2.get();
+  ServiceWorkerProviderHost* host3_raw = host3.get();
+  ServiceWorkerProviderHost* host4_raw = host4.get();
+  context()->AddProviderHost(std::move(host1));
+  context()->AddProviderHost(std::move(host2));
+  context()->AddProviderHost(std::move(host3));
+  context()->AddProviderHost(std::move(host4));
 
   // Iterate over all provider hosts.
   std::set<ServiceWorkerProviderHost*> results;
@@ -629,10 +629,10 @@
     results.insert(it->GetProviderHost());
   }
   EXPECT_EQ(4u, results.size());
-  EXPECT_TRUE(ContainsKey(results, host1));
-  EXPECT_TRUE(ContainsKey(results, host2));
-  EXPECT_TRUE(ContainsKey(results, host3));
-  EXPECT_TRUE(ContainsKey(results, host4));
+  EXPECT_TRUE(ContainsKey(results, host1_raw));
+  EXPECT_TRUE(ContainsKey(results, host2_raw));
+  EXPECT_TRUE(ContainsKey(results, host3_raw));
+  EXPECT_TRUE(ContainsKey(results, host4_raw));
 
   // Iterate over the client provider hosts that belong to kOrigin1.
   results.clear();
@@ -641,8 +641,8 @@
     results.insert(it->GetProviderHost());
   }
   EXPECT_EQ(2u, results.size());
-  EXPECT_TRUE(ContainsKey(results, host1));
-  EXPECT_TRUE(ContainsKey(results, host3));
+  EXPECT_TRUE(ContainsKey(results, host1_raw));
+  EXPECT_TRUE(ContainsKey(results, host3_raw));
 
   // Iterate over the provider hosts that belong to kOrigin2.
   // (This should not include host4 as it's not for controllee.)
@@ -652,7 +652,7 @@
     results.insert(it->GetProviderHost());
   }
   EXPECT_EQ(1u, results.size());
-  EXPECT_TRUE(ContainsKey(results, host2));
+  EXPECT_TRUE(ContainsKey(results, host2_raw));
 
   context()->RemoveProviderHost(kRenderProcessId1, 1);
   context()->RemoveProviderHost(kRenderProcessId2, 2);
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index 249e254..560416b 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -125,12 +125,10 @@
         EmbeddedWorkerTestHelper::CreateHttpResponseInfo());
 
     // An empty host.
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), NULL));
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(
+            helper_->mock_render_process_id(), kMockProviderId,
+            true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
     provider_host_ = host->AsWeakPtr();
     context()->AddProviderHost(std::move(host));
 
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index 96f7899..1fb1130 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -119,11 +119,8 @@
 }
 
 ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() {
-  if (GetContext()) {
-    GetContext()->RemoveAllProviderHostsForProcess(render_process_id_);
-    GetContext()->embedded_worker_registry()->RemoveChildProcessSender(
-        render_process_id_);
-  }
+  if (GetContext())
+    GetContext()->RemoveDispatcherHost(render_process_id_);
 }
 
 void ServiceWorkerDispatcherHost::Init(
@@ -138,8 +135,7 @@
   context_wrapper_ = context_wrapper;
   if (!GetContext())
     return;
-  GetContext()->embedded_worker_registry()->AddChildProcessSender(
-      render_process_id_, this);
+  GetContext()->AddDispatcherHost(render_process_id_, this);
 }
 
 void ServiceWorkerDispatcherHost::OnFilterAdded(IPC::Channel* channel) {
@@ -156,11 +152,8 @@
 void ServiceWorkerDispatcherHost::OnFilterRemoved() {
   // Don't wait until the destructor to teardown since a new dispatcher host
   // for this process might be created before then.
-  if (GetContext()) {
-    GetContext()->RemoveAllProviderHostsForProcess(render_process_id_);
-    GetContext()->embedded_worker_registry()->RemoveChildProcessSender(
-        render_process_id_);
-  }
+  if (GetContext())
+    GetContext()->RemoveDispatcherHost(render_process_id_);
   context_wrapper_ = nullptr;
   channel_ready_ = false;
 }
@@ -185,12 +178,6 @@
                         OnGetRegistrations)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetRegistrationForReady,
                         OnGetRegistrationForReady)
-    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ProviderCreated,
-                        OnProviderCreated)
-    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ProviderDestroyed,
-                        OnProviderDestroyed)
-    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetVersionId,
-                        OnSetHostedVersionId)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToWorker,
                         OnPostMessageToWorker)
     IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerReadyForInspection,
@@ -1004,10 +991,7 @@
 }
 
 void ServiceWorkerDispatcherHost::OnProviderCreated(
-    int provider_id,
-    int route_id,
-    ServiceWorkerProviderType provider_type,
-    bool is_parent_frame_secure) {
+    ServiceWorkerProviderHostInfo info) {
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
@@ -1016,19 +1000,19 @@
                "ServiceWorkerDispatcherHost::OnProviderCreated");
   if (!GetContext())
     return;
-  if (GetContext()->GetProviderHost(render_process_id_, provider_id)) {
+  if (GetContext()->GetProviderHost(render_process_id_, info.provider_id)) {
     bad_message::ReceivedBadMessage(this,
                                     bad_message::SWDH_PROVIDER_CREATED_NO_HOST);
     return;
   }
 
-  std::unique_ptr<ServiceWorkerProviderHost> provider_host;
   if (IsBrowserSideNavigationEnabled() &&
-      ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id)) {
+      ServiceWorkerUtils::IsBrowserAssignedProviderId(info.provider_id)) {
+    std::unique_ptr<ServiceWorkerProviderHost> provider_host;
     // PlzNavigate
     // Retrieve the provider host previously created for navigation requests.
     ServiceWorkerNavigationHandleCore* navigation_handle_core =
-        GetContext()->GetNavigationHandleCore(provider_id);
+        GetContext()->GetNavigationHandleCore(info.provider_id);
     if (navigation_handle_core != nullptr)
       provider_host = navigation_handle_core->RetrievePreCreatedHost();
 
@@ -1036,25 +1020,19 @@
     // Just return as the navigation will be stopped in the renderer as well.
     if (provider_host == nullptr)
       return;
-    DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_WINDOW, provider_type);
-    provider_host->CompleteNavigationInitialized(render_process_id_, route_id,
-                                                 this);
+    DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_WINDOW, info.type);
+    provider_host->CompleteNavigationInitialized(render_process_id_,
+                                                 info.route_id, this);
+    GetContext()->AddProviderHost(std::move(provider_host));
   } else {
-    if (ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id)) {
+    if (ServiceWorkerUtils::IsBrowserAssignedProviderId(info.provider_id)) {
       bad_message::ReceivedBadMessage(
           this, bad_message::SWDH_PROVIDER_CREATED_NO_HOST);
       return;
     }
-    ServiceWorkerProviderHost::FrameSecurityLevel parent_frame_security_level =
-        is_parent_frame_secure
-            ? ServiceWorkerProviderHost::FrameSecurityLevel::SECURE
-            : ServiceWorkerProviderHost::FrameSecurityLevel::INSECURE;
-    provider_host = std::unique_ptr<ServiceWorkerProviderHost>(
-        new ServiceWorkerProviderHost(
-            render_process_id_, route_id, provider_id, provider_type,
-            parent_frame_security_level, GetContext()->AsWeakPtr(), this));
+    GetContext()->AddProviderHost(ServiceWorkerProviderHost::Create(
+        render_process_id_, std::move(info), GetContext()->AsWeakPtr(), this));
   }
-  GetContext()->AddProviderHost(std::move(provider_host));
 }
 
 void ServiceWorkerDispatcherHost::OnProviderDestroyed(int provider_id) {
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index e33a7e8..cfa7045 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -88,6 +88,10 @@
   friend class base::DeleteHelper<ServiceWorkerDispatcherHost>;
   friend class ServiceWorkerDispatcherHostTest;
   friend class TestingServiceWorkerDispatcherHost;
+  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDispatcherHostTest,
+                           ProviderCreatedAndDestroyed);
+  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDispatcherHostTest,
+                           CleanupOnRendererCrash);
 
   using StatusCallback = base::Callback<void(ServiceWorkerStatusCode status)>;
   enum class ProviderStatus { OK, NO_CONTEXT, DEAD_HOST, NO_HOST, NO_URL };
@@ -97,10 +101,7 @@
   void AddMojoBinding(mojo::ScopedInterfaceEndpointHandle handle);
 
   // mojom::ServiceWorkerDispatcherHost implementation
-  void OnProviderCreated(int provider_id,
-                         int route_id,
-                         ServiceWorkerProviderType provider_type,
-                         bool is_parent_frame_secure) override;
+  void OnProviderCreated(ServiceWorkerProviderHostInfo info) override;
   void OnProviderDestroyed(int provider_id) override;
   void OnSetHostedVersionId(int provider_id,
                             int64_t version_id,
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index 46bcb77..0c3fdb6 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -118,9 +118,11 @@
 
   void Initialize(std::unique_ptr<EmbeddedWorkerTestHelper> helper) {
     helper_.reset(helper.release());
+    // Replace the default dispatcher host.
+    int process_id = helper_->mock_render_process_id();
+    context()->RemoveDispatcherHost(process_id);
     dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
-        helper_->mock_render_process_id(), context_wrapper(),
-        &resource_context_, helper_.get());
+        process_id, context_wrapper(), &resource_context_, helper_.get());
   }
 
   void SetUpRegistration(const GURL& scope, const GURL& script_url) {
@@ -154,16 +156,16 @@
   void SendSetHostedVersionId(int provider_id,
                               int64_t version_id,
                               int embedded_worker_id) {
-    dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_SetVersionId(
-        provider_id, version_id, embedded_worker_id));
+    dispatcher_host_->OnSetHostedVersionId(provider_id, version_id,
+                                           embedded_worker_id);
   }
 
   void SendProviderCreated(ServiceWorkerProviderType type,
                            const GURL& pattern) {
     const int64_t kProviderId = 99;
-    dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
-        kProviderId, MSG_ROUTING_NONE, type,
-        true /* is_parent_frame_secure */));
+    ServiceWorkerProviderHostInfo info(kProviderId, MSG_ROUTING_NONE, type,
+                                       true /* is_parent_frame_secure */);
+    dispatcher_host_->OnProviderCreated(std::move(info));
     helper_->SimulateAddProcessToPattern(pattern,
                                          helper_->mock_render_process_id());
     provider_host_ = context()->GetProviderHost(
@@ -244,12 +246,11 @@
         sender_provider_host, callback);
   }
 
-  ServiceWorkerProviderHost* CreateServiceWorkerProviderHost(int provider_id) {
-    return new ServiceWorkerProviderHost(
-        helper_->mock_render_process_id(), kRenderFrameId, provider_id,
-        SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-        ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-        context()->AsWeakPtr(), dispatcher_host_.get());
+  std::unique_ptr<ServiceWorkerProviderHost> CreateServiceWorkerProviderHost(
+      int provider_id) {
+    return CreateProviderHostWithDispatcherHost(
+        helper_->mock_render_process_id(), provider_id, context()->AsWeakPtr(),
+        kRenderFrameId, dispatcher_host_.get());
   }
 
   TestBrowserThreadBundle browser_thread_bundle_;
@@ -508,34 +509,32 @@
   const int kProviderId = 1001;
   int process_id = helper_->mock_render_process_id();
 
-  dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
+  dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo(
       kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
       true /* is_parent_frame_secure */));
   EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId));
 
   // Two with the same ID should be seen as a bad message.
-  dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
+  dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo(
       kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
       true /* is_parent_frame_secure */));
   EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
 
-  dispatcher_host_->OnMessageReceived(
-      ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
+  dispatcher_host_->OnProviderDestroyed(kProviderId);
   EXPECT_FALSE(context()->GetProviderHost(process_id, kProviderId));
 
   // Destroying an ID that does not exist warrants a bad message.
-  dispatcher_host_->OnMessageReceived(
-      ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
+  dispatcher_host_->OnProviderDestroyed(kProviderId);
   EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
 
   // Deletion of the dispatcher_host should cause providers for that
   // process to get deleted as well.
-  dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
+  dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo(
       kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
       true /* is_parent_frame_secure */));
   EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId));
   EXPECT_TRUE(dispatcher_host_->HasOneRef());
-  dispatcher_host_ = NULL;
+  dispatcher_host_ = nullptr;
   EXPECT_FALSE(context()->GetProviderHost(process_id, kProviderId));
 }
 
@@ -667,7 +666,7 @@
   // To show the new dispatcher can operate, simulate provider creation. Since
   // the old dispatcher cleaned up the old provider host, the new one won't
   // complain.
-  new_dispatcher_host->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
+  new_dispatcher_host->OnProviderCreated(ServiceWorkerProviderHostInfo(
       provider_id, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
       true /* is_parent_frame_secure */));
   EXPECT_EQ(0, new_dispatcher_host->bad_messages_received_count_);
diff --git a/content/browser/service_worker/service_worker_handle_unittest.cc b/content/browser/service_worker/service_worker_handle_unittest.cc
index 1b6e7832..24d01a17 100644
--- a/content/browser/service_worker/service_worker_handle_unittest.cc
+++ b/content/browser/service_worker/service_worker_handle_unittest.cc
@@ -76,6 +76,7 @@
   void SetUp() override {
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
 
+    helper_->context()->RemoveDispatcherHost(helper_->mock_render_process_id());
     dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
         helper_->mock_render_process_id(), helper_->context_wrapper(),
         &resource_context_, helper_.get());
@@ -110,12 +111,10 @@
     base::RunLoop().RunUntilIdle();
     ASSERT_EQ(SERVICE_WORKER_OK, status);
 
-    provider_host_.reset(new ServiceWorkerProviderHost(
-        helper_->mock_render_process_id(), kRenderFrameId, 1,
-        SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-        ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-        helper_->context()->AsWeakPtr(), dispatcher_host_.get()));
-
+    provider_host_ = CreateProviderHostWithDispatcherHost(
+        helper_->mock_render_process_id(), 1 /* provider_id */,
+        helper_->context()->AsWeakPtr(), kRenderFrameId,
+        dispatcher_host_.get());
     helper_->SimulateAddProcessToPattern(pattern,
                                          helper_->mock_render_process_id());
   }
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 96c014a..0d235ec 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -199,13 +199,10 @@
 
 std::unique_ptr<ServiceWorkerProviderHost>
 ServiceWorkerJobTest::CreateControllee() {
-  return std::unique_ptr<ServiceWorkerProviderHost>(
-      new ServiceWorkerProviderHost(
-          33 /* dummy render_process id */,
-          MSG_ROUTING_NONE /* render_frame_id */, 1 /* dummy provider_id */,
-          SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-          ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-          helper_->context()->AsWeakPtr(), NULL));
+  std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
+      33 /* dummy render process id */, 1 /* dummy provider_id */,
+      true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
+  return host;
 }
 
 TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) {
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index f15022e..40a0037 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -90,22 +90,31 @@
   CHECK(IsBrowserSideNavigationEnabled());
   // Generate a new browser-assigned id for the host.
   int provider_id = g_next_navigation_provider_id--;
-  auto host = base::MakeUnique<ServiceWorkerProviderHost>(
+  auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE, provider_id,
-      SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      are_ancestors_secure ? FrameSecurityLevel::SECURE
-                           : FrameSecurityLevel::INSECURE,
-      context, nullptr);
+      SERVICE_WORKER_PROVIDER_FOR_WINDOW, are_ancestors_secure, context,
+      nullptr));
   host->web_contents_getter_ = web_contents_getter;
   return host;
 }
 
+// static
+std::unique_ptr<ServiceWorkerProviderHost> ServiceWorkerProviderHost::Create(
+    int process_id,
+    ServiceWorkerProviderHostInfo info,
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    ServiceWorkerDispatcherHost* dispatcher_host) {
+  return base::WrapUnique(new ServiceWorkerProviderHost(
+      process_id, info.route_id, info.provider_id, info.type,
+      info.is_parent_frame_secure, context, dispatcher_host));
+}
+
 ServiceWorkerProviderHost::ServiceWorkerProviderHost(
     int render_process_id,
     int route_id,
     int provider_id,
     ServiceWorkerProviderType provider_type,
-    FrameSecurityLevel parent_frame_security_level,
+    bool is_parent_frame_secure,
     base::WeakPtr<ServiceWorkerContextCore> context,
     ServiceWorkerDispatcherHost* dispatcher_host)
     : client_uuid_(base::GenerateGUID()),
@@ -114,7 +123,7 @@
       render_thread_id_(kDocumentMainThreadId),
       provider_id_(provider_id),
       provider_type_(provider_type),
-      parent_frame_security_level_(parent_frame_security_level),
+      is_parent_frame_secure_(is_parent_frame_secure),
       context_(context),
       dispatcher_host_(dispatcher_host),
       allow_association_(true) {
@@ -490,12 +499,18 @@
   return true;
 }
 
-void ServiceWorkerProviderHost::PrepareForCrossSiteTransfer() {
+std::unique_ptr<ServiceWorkerProviderHost>
+ServiceWorkerProviderHost::PrepareForCrossSiteTransfer() {
   DCHECK_NE(ChildProcessHost::kInvalidUniqueID, render_process_id_);
   DCHECK_NE(MSG_ROUTING_NONE, route_id_);
   DCHECK_EQ(kDocumentMainThreadId, render_thread_id_);
   DCHECK_NE(SERVICE_WORKER_PROVIDER_UNKNOWN, provider_type_);
 
+  std::unique_ptr<ServiceWorkerProviderHost> new_provider_host =
+      base::WrapUnique(new ServiceWorkerProviderHost(
+          process_id(), frame_id(), provider_id(), provider_type(),
+          is_parent_frame_secure(), context_, dispatcher_host()));
+
   for (const GURL& pattern : associated_patterns_)
     DecreaseProcessReference(pattern);
 
@@ -515,6 +530,7 @@
   provider_id_ = kInvalidServiceWorkerProviderId;
   provider_type_ = SERVICE_WORKER_PROVIDER_UNKNOWN;
   dispatcher_host_ = nullptr;
+  return new_provider_host;
 }
 
 void ServiceWorkerProviderHost::CompleteCrossSiteTransfer(
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index e42c5a46..585bc59 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -20,6 +20,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_provider_host_info.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/request_context_frame_type.h"
 #include "content/public/common/request_context_type.h"
@@ -72,22 +73,15 @@
       bool are_ancestors_secure,
       const WebContentsGetter& web_contents_getter);
 
-  enum class FrameSecurityLevel { UNINITIALIZED, INSECURE, SECURE };
+  // Used to create a ServiceWorkerProviderHost when the renderer-side provider
+  // is created. This ProviderHost will be created for the process specified by
+  // |process_id|.
+  static std::unique_ptr<ServiceWorkerProviderHost> Create(
+      int process_id,
+      ServiceWorkerProviderHostInfo info,
+      base::WeakPtr<ServiceWorkerContextCore> context,
+      ServiceWorkerDispatcherHost* dispatcher_host);
 
-  // When this provider host is for a Service Worker context, |route_id| is
-  // MSG_ROUTING_NONE. When this provider host is for a Document,
-  // |route_id| is the frame ID of the Document. When this provider host is for
-  // a Shared Worker, |route_id| is the Shared Worker route ID.
-  // |provider_type| gives additional information whether the provider is
-  // created for controller (ServiceWorker) or controllee (Document or
-  // SharedWorker).
-  ServiceWorkerProviderHost(int render_process_id,
-                            int route_id,
-                            int provider_id,
-                            ServiceWorkerProviderType provider_type,
-                            FrameSecurityLevel parent_frame_security_level,
-                            base::WeakPtr<ServiceWorkerContextCore> context,
-                            ServiceWorkerDispatcherHost* dispatcher_host);
   virtual ~ServiceWorkerProviderHost();
 
   const std::string& client_uuid() const { return client_uuid_; }
@@ -99,15 +93,7 @@
     return web_contents_getter_;
   }
 
-  bool is_parent_frame_secure() const {
-    return parent_frame_security_level_ == FrameSecurityLevel::SECURE;
-  }
-  void set_parent_frame_secure(bool is_parent_frame_secure) {
-    CHECK_EQ(parent_frame_security_level_, FrameSecurityLevel::UNINITIALIZED);
-    parent_frame_security_level_ = is_parent_frame_secure
-                                       ? FrameSecurityLevel::SECURE
-                                       : FrameSecurityLevel::INSECURE;
-  }
+  bool is_parent_frame_secure() const { return is_parent_frame_secure_; }
 
   // Returns whether this provider host is secure enough to have a service
   // worker controller.
@@ -252,7 +238,7 @@
   bool GetRegistrationForReady(const GetRegistrationForReadyCallback& callback);
 
   // Methods to support cross site navigations.
-  void PrepareForCrossSiteTransfer();
+  std::unique_ptr<ServiceWorkerProviderHost> PrepareForCrossSiteTransfer();
   void CompleteCrossSiteTransfer(
       int new_process_id,
       int new_frame_id,
@@ -329,6 +315,14 @@
     ~OneShotGetReadyCallback();
   };
 
+  ServiceWorkerProviderHost(int render_process_id,
+                            int route_id,
+                            int provider_id,
+                            ServiceWorkerProviderType provider_type,
+                            bool is_parent_frame_secure,
+                            base::WeakPtr<ServiceWorkerContextCore> context,
+                            ServiceWorkerDispatcherHost* dispatcher_host);
+
   // ServiceWorkerRegistration::Listener overrides.
   void OnVersionAttributesChanged(
       ServiceWorkerRegistration* registration,
@@ -388,7 +382,7 @@
   WebContentsGetter web_contents_getter_;
 
   ServiceWorkerProviderType provider_type_;
-  FrameSecurityLevel parent_frame_security_level_;
+  const bool is_parent_frame_secure_;
   GURL document_url_;
   GURL topmost_frame_url_;
 
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 36381aff..ce0560b 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "content/browser/service_worker/service_worker_provider_host.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
@@ -36,7 +39,8 @@
 class ServiceWorkerProviderHostTest : public testing::Test {
  protected:
   ServiceWorkerProviderHostTest()
-      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
+      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+        next_provider_id_(1) {
     SetContentClient(&test_content_client_);
   }
   ~ServiceWorkerProviderHostTest() override {}
@@ -55,26 +59,6 @@
         GURL("https://www.example.com/example"), 2L, context_->AsWeakPtr());
     registration3_ = new ServiceWorkerRegistration(
         GURL("https://other.example.com/"), 3L, context_->AsWeakPtr());
-
-    // Prepare provider hosts (for the same process).
-    std::unique_ptr<ServiceWorkerProviderHost> host1(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            1 /* provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context_->AsWeakPtr(), NULL));
-    host1->SetDocumentUrl(GURL("https://www.example.com/example1.html"));
-    std::unique_ptr<ServiceWorkerProviderHost> host2(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            2 /* provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context_->AsWeakPtr(), NULL));
-    host2->SetDocumentUrl(GURL("https://www.example.com/example2.html"));
-    provider_host1_ = host1->AsWeakPtr();
-    provider_host2_ = host2->AsWeakPtr();
-    context_->AddProviderHost(base::WrapUnique(host1.release()));
-    context_->AddProviderHost(base::WrapUnique(host2.release()));
   }
 
   void TearDown() override {
@@ -90,24 +74,52 @@
     return context_->process_manager()->PatternHasProcessToRun(pattern);
   }
 
+  ServiceWorkerProviderHost* CreateProviderHost(const GURL& document_url) {
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(
+            helper_->mock_render_process_id(), next_provider_id_++,
+            true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
+    ServiceWorkerProviderHost* host_raw = host.get();
+    host->SetDocumentUrl(document_url);
+    context_->AddProviderHost(std::move(host));
+    return host_raw;
+  }
+
+  ServiceWorkerProviderHost* CreateProviderHostWithInsecureParentFrame(
+      const GURL& document_url) {
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(helper_->mock_render_process_id(),
+                                    next_provider_id_++,
+                                    false /* is_parent_frame_secure */,
+                                    helper_->context()->AsWeakPtr());
+    ServiceWorkerProviderHost* host_raw = host.get();
+    host->SetDocumentUrl(document_url);
+    context_->AddProviderHost(std::move(host));
+    return host_raw;
+  }
+
   TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
   ServiceWorkerContextCore* context_;
   scoped_refptr<ServiceWorkerRegistration> registration1_;
   scoped_refptr<ServiceWorkerRegistration> registration2_;
   scoped_refptr<ServiceWorkerRegistration> registration3_;
-  base::WeakPtr<ServiceWorkerProviderHost> provider_host1_;
-  base::WeakPtr<ServiceWorkerProviderHost> provider_host2_;
   GURL script_url_;
   ServiceWorkerTestContentClient test_content_client_;
   TestContentBrowserClient test_content_browser_client_;
   ContentBrowserClient* old_content_browser_client_;
+  int next_provider_id_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostTest);
 };
 
 TEST_F(ServiceWorkerProviderHostTest, PotentialRegistration_ProcessStatus) {
+  ServiceWorkerProviderHost* provider_host1 =
+      CreateProviderHost(GURL("https://www.example.com/example1.html"));
+  ServiceWorkerProviderHost* provider_host2 =
+      CreateProviderHost(GURL("https://www.example.com/example2.html"));
+
   // Matching registrations have already been set by SetDocumentUrl.
   ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
 
@@ -115,93 +127,101 @@
   ASSERT_TRUE(PatternHasProcessToRun(registration2_->pattern()));
 
   // Adding the same registration twice has no effect.
-  provider_host1_->AddMatchingRegistration(registration1_.get());
+  provider_host1->AddMatchingRegistration(registration1_.get());
   ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
 
   // Removing a matching registration will decrease the process refs for its
   // pattern.
-  provider_host1_->RemoveMatchingRegistration(registration1_.get());
+  provider_host1->RemoveMatchingRegistration(registration1_.get());
   ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
-  provider_host2_->RemoveMatchingRegistration(registration1_.get());
+  provider_host2->RemoveMatchingRegistration(registration1_.get());
   ASSERT_FALSE(PatternHasProcessToRun(registration1_->pattern()));
 
   // Matching registration will be removed when moving out of scope
   ASSERT_TRUE(PatternHasProcessToRun(registration2_->pattern()));   // host1,2
   ASSERT_FALSE(PatternHasProcessToRun(registration3_->pattern()));  // no host
-  provider_host1_->SetDocumentUrl(GURL("https://other.example.com/"));
+  provider_host1->SetDocumentUrl(GURL("https://other.example.com/"));
   ASSERT_TRUE(PatternHasProcessToRun(registration2_->pattern()));  // host2
   ASSERT_TRUE(PatternHasProcessToRun(registration3_->pattern()));  // host1
-  provider_host2_->SetDocumentUrl(GURL("https://other.example.com/"));
+  provider_host2->SetDocumentUrl(GURL("https://other.example.com/"));
   ASSERT_FALSE(PatternHasProcessToRun(registration2_->pattern()));  // no host
   ASSERT_TRUE(PatternHasProcessToRun(registration3_->pattern()));   // host1,2
 }
 
 TEST_F(ServiceWorkerProviderHostTest, AssociatedRegistration_ProcessStatus) {
+  ServiceWorkerProviderHost* provider_host1 =
+      CreateProviderHost(GURL("https://www.example.com/example1.html"));
+
   // Associating the registration will also increase the process refs for
   // the registration's pattern.
-  provider_host1_->AssociateRegistration(registration1_.get(),
-                                         false /* notify_controllerchange */);
+  provider_host1->AssociateRegistration(registration1_.get(),
+                                        false /* notify_controllerchange */);
   ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
 
   // Disassociating the registration shouldn't affect the process refs for
   // the registration's pattern.
-  provider_host1_->DisassociateRegistration();
+  provider_host1->DisassociateRegistration();
   ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
 }
 
 TEST_F(ServiceWorkerProviderHostTest, MatchRegistration) {
+  ServiceWorkerProviderHost* provider_host1 =
+      CreateProviderHost(GURL("https://www.example.com/example1.html"));
+
   // Match registration should return the longest matching one.
-  ASSERT_EQ(registration2_, provider_host1_->MatchRegistration());
-  provider_host1_->RemoveMatchingRegistration(registration2_.get());
-  ASSERT_EQ(registration1_, provider_host1_->MatchRegistration());
+  ASSERT_EQ(registration2_, provider_host1->MatchRegistration());
+  provider_host1->RemoveMatchingRegistration(registration2_.get());
+  ASSERT_EQ(registration1_, provider_host1->MatchRegistration());
 
   // Should return nullptr after removing all matching registrations.
-  provider_host1_->RemoveMatchingRegistration(registration1_.get());
-  ASSERT_EQ(nullptr, provider_host1_->MatchRegistration());
+  provider_host1->RemoveMatchingRegistration(registration1_.get());
+  ASSERT_EQ(nullptr, provider_host1->MatchRegistration());
 
   // SetDocumentUrl sets all of matching registrations
-  provider_host1_->SetDocumentUrl(GURL("https://www.example.com/example1"));
-  ASSERT_EQ(registration2_, provider_host1_->MatchRegistration());
-  provider_host1_->RemoveMatchingRegistration(registration2_.get());
-  ASSERT_EQ(registration1_, provider_host1_->MatchRegistration());
+  provider_host1->SetDocumentUrl(GURL("https://www.example.com/example1"));
+  ASSERT_EQ(registration2_, provider_host1->MatchRegistration());
+  provider_host1->RemoveMatchingRegistration(registration2_.get());
+  ASSERT_EQ(registration1_, provider_host1->MatchRegistration());
 
   // SetDocumentUrl with another origin also updates matching registrations
-  provider_host1_->SetDocumentUrl(GURL("https://other.example.com/example"));
-  ASSERT_EQ(registration3_, provider_host1_->MatchRegistration());
-  provider_host1_->RemoveMatchingRegistration(registration3_.get());
-  ASSERT_EQ(nullptr, provider_host1_->MatchRegistration());
+  provider_host1->SetDocumentUrl(GURL("https://other.example.com/example"));
+  ASSERT_EQ(registration3_, provider_host1->MatchRegistration());
+  provider_host1->RemoveMatchingRegistration(registration3_.get());
+  ASSERT_EQ(nullptr, provider_host1->MatchRegistration());
 }
 
 TEST_F(ServiceWorkerProviderHostTest, ContextSecurity) {
-  using FrameSecurityLevel = ServiceWorkerProviderHost::FrameSecurityLevel;
+  ServiceWorkerProviderHost* provider_host_secure_parent =
+      CreateProviderHost(GURL("https://www.example.com/example1.html"));
+  ServiceWorkerProviderHost* provider_host_insecure_parent =
+      CreateProviderHostWithInsecureParentFrame(
+          GURL("https://www.example.com/example1.html"));
 
   // Insecure document URL.
-  provider_host1_->SetDocumentUrl(GURL("http://host"));
-  provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
-  EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
+  provider_host_secure_parent->SetDocumentUrl(GURL("http://host"));
+  EXPECT_FALSE(provider_host_secure_parent->IsContextSecureForServiceWorker());
 
   // Insecure parent frame.
-  provider_host1_->SetDocumentUrl(GURL("https://host"));
-  provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::INSECURE;
-  EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
+  provider_host_insecure_parent->SetDocumentUrl(GURL("https://host"));
+  EXPECT_FALSE(
+      provider_host_insecure_parent->IsContextSecureForServiceWorker());
 
   // Secure URL and parent frame.
-  provider_host1_->SetDocumentUrl(GURL("https://host"));
-  provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
-  EXPECT_TRUE(provider_host1_->IsContextSecureForServiceWorker());
+  provider_host_secure_parent->SetDocumentUrl(GURL("https://host"));
+  EXPECT_TRUE(provider_host_secure_parent->IsContextSecureForServiceWorker());
 
   // Exceptional service worker scheme.
   GURL url(std::string(kServiceWorkerScheme) + "://host");
   EXPECT_TRUE(url.is_valid());
-  provider_host1_->SetDocumentUrl(url);
-  provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
   EXPECT_FALSE(IsOriginSecure(url));
   EXPECT_TRUE(OriginCanAccessServiceWorkers(url));
-  EXPECT_TRUE(provider_host1_->IsContextSecureForServiceWorker());
+  provider_host_secure_parent->SetDocumentUrl(url);
+  EXPECT_TRUE(provider_host_secure_parent->IsContextSecureForServiceWorker());
 
   // Exceptional service worker scheme with insecure parent frame.
-  provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::INSECURE;
-  EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
+  provider_host_insecure_parent->SetDocumentUrl(url);
+  EXPECT_FALSE(
+      provider_host_insecure_parent->IsContextSecureForServiceWorker());
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 9e87cab..6e1b444 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -256,12 +256,9 @@
     ASSERT_EQ(SERVICE_WORKER_OK, status);
 
     // Give the active version a controllee.
-    host_.reset(new ServiceWorkerProviderHost(
-        33 /* dummy render process id */,
-        MSG_ROUTING_NONE /* render_frame_id */, 1 /* dummy provider_id */,
-        SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-        ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-        context()->AsWeakPtr(), nullptr));
+    host_ = CreateProviderHostForWindow(
+        33 /* dummy render process id */, 1 /* dummy provider_id */,
+        true /* is_parent_frame_secure */, context()->AsWeakPtr());
     version_1->AddControllee(host_.get());
 
     // Give the active version an in-flight request.
diff --git a/content/browser/service_worker/service_worker_request_handler_unittest.cc b/content/browser/service_worker/service_worker_request_handler_unittest.cc
index cdac7ba..93f795b 100644
--- a/content/browser/service_worker/service_worker_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_request_handler_unittest.cc
@@ -42,15 +42,12 @@
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
 
     // An empty host.
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
-            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), nullptr));
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForWindow(
+            helper_->mock_render_process_id(), kMockProviderId,
+            true /* is_parent_frame_secure */, context()->AsWeakPtr());
     provider_host_ = host->AsWeakPtr();
     context()->AddProviderHost(std::move(host));
-
   }
 
   void TearDown() override {
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 51e0cb6..4f8dfb48 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -1346,11 +1346,9 @@
   registration_->SetActiveVersion(registration_->waiting_version());
   storage()->UpdateToActiveState(
       registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
-  std::unique_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
-      33 /* dummy render process id */, MSG_ROUTING_NONE,
-      1 /* dummy provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), NULL));
+  std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
+      33 /* dummy render process id */, 1 /* dummy provider_id */,
+      true /* is_parent_frame_secure */, context()->AsWeakPtr());
   registration_->active_version()->AddControllee(host.get());
 
   bool was_called = false;
@@ -1398,11 +1396,9 @@
   registration_->SetWaitingVersion(NULL);
   storage()->UpdateToActiveState(
       registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
-  std::unique_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
-      33 /* dummy render process id */, MSG_ROUTING_NONE,
-      1 /* dummy provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), NULL));
+  std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
+      33 /* dummy render process id */, 1 /* dummy provider_id */,
+      true /* is_parent_frame_secure */, context()->AsWeakPtr());
   registration_->active_version()->AddControllee(host.get());
 
   bool was_called = false;
@@ -1558,11 +1554,9 @@
   registration_->SetActiveVersion(registration_->waiting_version());
   storage()->UpdateToActiveState(
       registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
-  std::unique_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
-      33 /* dummy render process id */, MSG_ROUTING_NONE,
-      1 /* dummy provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      context()->AsWeakPtr(), NULL));
+  std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
+      33 /* dummy render process id */, 1 /* dummy provider_id */,
+      true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
   registration_->active_version()->AddControllee(host.get());
 
   bool was_called = false;
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
new file mode 100644
index 0000000..e843910
--- /dev/null
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_test_utils.h"
+
+#include <utility>
+
+#include "content/browser/service_worker/service_worker_provider_host.h"
+
+namespace content {
+
+std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForWindow(
+    int process_id,
+    int provider_id,
+    bool is_parent_frame_secure,
+    base::WeakPtr<ServiceWorkerContextCore> context) {
+  ServiceWorkerProviderHostInfo info(provider_id, MSG_ROUTING_NONE,
+                                     SERVICE_WORKER_PROVIDER_FOR_WINDOW,
+                                     is_parent_frame_secure);
+  return ServiceWorkerProviderHost::Create(process_id, std::move(info),
+                                           std::move(context), nullptr);
+}
+
+std::unique_ptr<ServiceWorkerProviderHost>
+CreateProviderHostForServiceWorkerContext(
+    int process_id,
+    int provider_id,
+    bool is_parent_frame_secure,
+    base::WeakPtr<ServiceWorkerContextCore> context) {
+  ServiceWorkerProviderHostInfo info(provider_id, MSG_ROUTING_NONE,
+                                     SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
+                                     is_parent_frame_secure);
+  return ServiceWorkerProviderHost::Create(process_id, std::move(info),
+                                           std::move(context), nullptr);
+}
+
+std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostWithDispatcherHost(
+    int process_id,
+    int provider_id,
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    int route_id,
+    ServiceWorkerDispatcherHost* dispatcher_host) {
+  ServiceWorkerProviderHostInfo info(provider_id, route_id,
+                                     SERVICE_WORKER_PROVIDER_FOR_WINDOW, true);
+  return ServiceWorkerProviderHost::Create(process_id, std::move(info),
+                                           std::move(context), dispatcher_host);
+}
+
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h
index 467eaf03..279faf3 100644
--- a/content/browser/service_worker/service_worker_test_utils.h
+++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -5,15 +5,22 @@
 #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
+class ServiceWorkerContextCore;
+class ServiceWorkerDispatcherHost;
+class ServiceWorkerProviderHost;
+
 template <typename Arg>
 void ReceiveResult(BrowserThread::ID run_quit_thread,
                    const base::Closure& quit,
@@ -39,6 +46,26 @@
   return base::Bind(&ReceiveResult<Arg>, id, quit, out);
 }
 
+std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForWindow(
+    int process_id,
+    int provider_id,
+    bool is_parent_frame_secure,
+    base::WeakPtr<ServiceWorkerContextCore> context);
+
+std::unique_ptr<ServiceWorkerProviderHost>
+CreateProviderHostForServiceWorkerContext(
+    int process_id,
+    int provider_id,
+    bool is_parent_frame_secure,
+    base::WeakPtr<ServiceWorkerContextCore> context);
+
+std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostWithDispatcherHost(
+    int process_id,
+    int provider_id,
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    int route_id,
+    ServiceWorkerDispatcherHost* dispatcher_host);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 74d61e7..f9c23d4 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -195,12 +195,10 @@
       version_->SetMainScriptHttpResponseInfo(http_info);
     }
 
-    std::unique_ptr<ServiceWorkerProviderHost> provider_host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(), MSG_ROUTING_NONE, kProviderID,
-            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            helper_->context()->AsWeakPtr(), nullptr));
+    std::unique_ptr<ServiceWorkerProviderHost> provider_host =
+        CreateProviderHostForWindow(
+            helper_->mock_render_process_id(), kProviderID,
+            true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
     provider_host_ = provider_host->AsWeakPtr();
     provider_host->SetDocumentUrl(GURL("https://example.com/"));
     registration_->SetActiveVersion(version_);
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 7558a4c5..6f4c737 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -655,11 +655,9 @@
   // Adding a controllee resets the idle time.
   version_->idle_time_ -= kOneSecond;
   idle_time = version_->idle_time_;
-  std::unique_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
-      33 /* dummy render process id */, MSG_ROUTING_NONE /* render_frame_id */,
-      1 /* dummy provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
-      ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-      helper_->context()->AsWeakPtr(), NULL));
+  std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
+      33 /* dummy render process id */, 1 /* dummy provider_id */,
+      true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr());
   version_->AddControllee(host.get());
   EXPECT_TRUE(version_->timeout_timer_.IsRunning());
   EXPECT_LT(idle_time, version_->idle_time_);
@@ -1130,10 +1128,7 @@
 
   // Simulate renderer crash: do what
   // ServiceWorkerDispatcherHost::OnFilterRemoved does.
-  int process_id = helper_->mock_render_process_id();
-  helper_->context()->RemoveAllProviderHostsForProcess(process_id);
-  helper_->context()->embedded_worker_registry()->RemoveChildProcessSender(
-      process_id);
+  helper_->context()->RemoveDispatcherHost(helper_->mock_render_process_id());
   base::RunLoop().RunUntilIdle();
 
   // Callback completed.
@@ -1343,10 +1338,7 @@
 
   // Simulate renderer crash: do what
   // ServiceWorkerDispatcherHost::OnFilterRemoved does.
-  int process_id = helper_->mock_render_process_id();
-  helper_->context()->RemoveAllProviderHostsForProcess(process_id);
-  helper_->context()->embedded_worker_registry()->RemoveChildProcessSender(
-      process_id);
+  helper_->context()->RemoveDispatcherHost(helper_->mock_render_process_id());
   base::RunLoop().RunUntilIdle();
 
   // Callback completed.
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index 494dee53..8fe5f13f3 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -290,12 +290,10 @@
       int process_id,
       int provider_id,
       const scoped_refptr<ServiceWorkerVersion>& version) {
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            process_id, MSG_ROUTING_NONE, provider_id,
-            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), nullptr));
+    std::unique_ptr<ServiceWorkerProviderHost> host =
+        CreateProviderHostForServiceWorkerContext(
+            process_id, provider_id, true /* is_parent_frame_secure */,
+            context()->AsWeakPtr());
     base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
     context()->AddProviderHost(std::move(host));
     provider_host->running_hosted_version_ = version;
diff --git a/content/child/service_worker/service_worker_network_provider.cc b/content/child/service_worker/service_worker_network_provider.cc
index 9e36b7d..555f509 100644
--- a/content/child/service_worker/service_worker_network_provider.cc
+++ b/content/child/service_worker/service_worker_network_provider.cc
@@ -9,6 +9,7 @@
 #include "content/child/service_worker/service_worker_provider_context.h"
 #include "content/common/navigation_params.h"
 #include "content/common/service_worker/service_worker_messages.h"
+#include "content/common/service_worker/service_worker_provider_host_info.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "ipc/ipc_sync_channel.h"
@@ -126,18 +127,14 @@
     return;
   if (!ChildThreadImpl::current())
     return;  // May be null in some tests.
+  ServiceWorkerProviderHostInfo provider_info(
+      provider_id_, route_id, provider_type, is_parent_frame_secure);
   context_ = new ServiceWorkerProviderContext(
       provider_id_, provider_type,
       ChildThreadImpl::current()->thread_safe_sender());
-  if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) {
-    ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
-        &dispatcher_host_);
-    dispatcher_host_->OnProviderCreated(provider_id_, route_id, provider_type,
-                                        is_parent_frame_secure);
-  } else {
-    ChildThreadImpl::current()->Send(new ServiceWorkerHostMsg_ProviderCreated(
-        provider_id_, route_id, provider_type, is_parent_frame_secure));
-  }
+  ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
+      &dispatcher_host_);
+  dispatcher_host_->OnProviderCreated(std::move(provider_info));
 }
 
 ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider(
@@ -157,12 +154,7 @@
     return;
   if (!ChildThreadImpl::current())
     return;  // May be null in some tests.
-  if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) {
-    dispatcher_host_->OnProviderDestroyed(provider_id());
-  } else {
-    ChildThreadImpl::current()->Send(
-        new ServiceWorkerHostMsg_ProviderDestroyed(provider_id_));
-  }
+  dispatcher_host_->OnProviderDestroyed(provider_id());
 }
 
 void ServiceWorkerNetworkProvider::SetServiceWorkerVersionId(
@@ -171,13 +163,8 @@
   DCHECK_NE(kInvalidServiceWorkerProviderId, provider_id_);
   if (!ChildThreadImpl::current())
     return;  // May be null in some tests.
-  if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) {
-    dispatcher_host_->OnSetHostedVersionId(provider_id(), version_id,
-                                           embedded_worker_id);
-  } else {
-    ChildThreadImpl::current()->Send(new ServiceWorkerHostMsg_SetVersionId(
-        provider_id_, version_id, embedded_worker_id));
-  }
+  dispatcher_host_->OnSetHostedVersionId(provider_id(), version_id,
+                                         embedded_worker_id);
 }
 
 bool ServiceWorkerNetworkProvider::IsControlledByServiceWorker() const {
diff --git a/content/child/service_worker/service_worker_network_provider.h b/content/child/service_worker/service_worker_network_provider.h
index 099d80fd..c991aec 100644
--- a/content/child/service_worker/service_worker_network_provider.h
+++ b/content/child/service_worker/service_worker_network_provider.h
@@ -15,7 +15,6 @@
 #include "base/supports_user_data.h"
 #include "content/common/content_export.h"
 #include "content/common/service_worker/service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_types.h"
 
 namespace blink {
 class WebLocalFrame;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 6ccbdd5c..aef641f 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -298,6 +298,8 @@
     "service_worker/service_worker_client_info.cc",
     "service_worker/service_worker_client_info.h",
     "service_worker/service_worker_messages.h",
+    "service_worker/service_worker_provider_host_info.cc",
+    "service_worker/service_worker_provider_host_info.h",
     "service_worker/service_worker_status_code.cc",
     "service_worker/service_worker_status_code.h",
     "service_worker/service_worker_types.cc",
@@ -582,6 +584,7 @@
     "service_worker/embedded_worker.mojom",
     "service_worker/service_worker.mojom",
     "service_worker/service_worker_event_dispatcher.mojom",
+    "service_worker/service_worker_provider.mojom",
     "service_worker/service_worker_types.mojom",
     "storage_partition_service.mojom",
     "url_loader.mojom",
diff --git a/content/common/service_worker/OWNERS b/content/common/service_worker/OWNERS
index f5a9a9f7..c19de06c 100644
--- a/content/common/service_worker/OWNERS
+++ b/content/common/service_worker/OWNERS
@@ -11,6 +11,9 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
 
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+
 per-file *_type_converter*.*=set noparent
 per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
 
diff --git a/content/common/service_worker/service_worker.mojom b/content/common/service_worker/service_worker.mojom
index 840a3ef..cadade0 100644
--- a/content/common/service_worker/service_worker.mojom
+++ b/content/common/service_worker/service_worker.mojom
@@ -4,18 +4,19 @@
 
 module content.mojom;
 
-import "content/common/service_worker/service_worker_types.mojom";
+import "content/common/service_worker/service_worker_provider.mojom";
 
 // Per-process browser-side interface bound to ServiceWorkerDispatcherHost.
 // Each InterfacePtrs on the same render process will be bound to the same
 // ServiceWorkerDispatcherHost.
 interface ServiceWorkerDispatcherHost {
-  OnProviderCreated(int32 provider_id,
-                    int32 route_id,
-                    ServiceWorkerProviderType provider_type,
-                    bool is_parent_frame_secure);
+  OnProviderCreated(ServiceWorkerProviderHostInfo provider_info);
   OnProviderDestroyed(int32 provider_id);
-  // Make relationship between the network provider and the service worker
+
+  // Informs the browser that a service worker is starting up. |provider_id|
+  // identifies the ServiceWorkerProviderHost hosting the service
+  // worker. |version_id| identifies the ServiceWorkerVersion and
+  // |embedded_worker_id| identifies the EmbeddedWorkerInstance.  
   OnSetHostedVersionId(int32 provider_id,
                        int64 version_id,
                        int32 embedded_worker_id);
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 9f5642f3..7cc0eee 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -216,34 +216,6 @@
     url::Origin /* source_origin */,
     std::vector<content::MessagePort> /* sent_message_ports */)
 
-// Informs the browser of a new ServiceWorkerProvider in the child process,
-// |provider_id| is unique within its child process. When this provider is
-// created for a document, |route_id| is the frame ID of it. When this provider
-// is created for a Shared Worker, |route_id| is the Shared Worker route ID.
-// When this provider is created for a Service Worker, |route_id| is
-// MSG_ROUTING_NONE. |provider_type| identifies whether this provider is for
-// Service Worker controllees (documents and Shared Workers) or for controllers
-// (Service Workers).
-//
-// |is_parent_frame_secure| is false if the provider is created for a
-// document whose parent frame is not secure from the point of view of the
-// document; that is, blink::WebFrame::canHaveSecureChild() returns false.
-// This doesn't mean the document is necessarily an insecure context,
-// because the document may have a URL whose scheme is granted an exception
-// that allows bypassing the ancestor secure context check. See the
-// comment in blink::Document::isSecureContextImpl for more details.
-// If the provider is not created for a document, or the document does not have
-// a parent frame, |is_parent_frame_secure| is true.
-IPC_MESSAGE_CONTROL4(ServiceWorkerHostMsg_ProviderCreated,
-                     int /* provider_id */,
-                     int /* route_id */,
-                     content::ServiceWorkerProviderType /* provider_type */,
-                     bool /* is_parent_frame_secure */)
-
-// Informs the browser of a ServiceWorkerProvider being destroyed.
-IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_ProviderDestroyed,
-                     int /* provider_id */)
-
 // Increments and decrements the ServiceWorker object's reference
 // counting in the browser side. The ServiceWorker object is created
 // with ref-count==1 initially.
@@ -265,15 +237,6 @@
 IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_TerminateWorker,
                      int /* handle_id */)
 
-// Informs the browser that a service worker is starting up in a provider.
-// |provider_id| identifies the ServiceWorkerProviderHost hosting the service
-// worker. |version_id| identifies the ServiceWorkerVersion and
-// |embedded_worker_id| identifies the EmbeddedWorkerInstance.
-IPC_MESSAGE_CONTROL3(ServiceWorkerHostMsg_SetVersionId,
-                     int /* provider_id */,
-                     int64_t /* version_id */,
-                     int /* embedded_worker_id */)
-
 // Informs the browser that event handling has finished.
 // Routed to the target ServiceWorkerVersion.
 IPC_MESSAGE_ROUTED4(ServiceWorkerHostMsg_InstallEventFinished,
diff --git a/content/common/service_worker/service_worker_provider.mojom b/content/common/service_worker/service_worker_provider.mojom
new file mode 100644
index 0000000..6e512ed
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider.mojom
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "content/common/service_worker/service_worker_types.mojom";
+
+// A container object carried from the renderer to the browser process.
+// This contains the params for the constructor of ServiceWorkerProviderHost.
+// See also comments in
+// content/common/service_worker/service_worker_provider_host_info.h.
+struct ServiceWorkerProviderHostInfo {
+  int32 provider_id;
+  int32 route_id;
+  ServiceWorkerProviderType type;
+  bool is_parent_frame_secure;
+};
\ No newline at end of file
diff --git a/content/common/service_worker/service_worker_provider.typemap b/content/common/service_worker/service_worker_provider.typemap
new file mode 100644
index 0000000..757d4a7
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider.typemap
@@ -0,0 +1,12 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//content/common/service_worker/service_worker_provider.mojom"
+public_headers =
+    [ "//content/common/service_worker/service_worker_provider_host_info.h" ]
+traits_headers = [ "//content/common/service_worker/service_worker_provider_struct_traits.h" ]
+sources = [
+  "//content/common/service_worker/service_worker_provider_struct_traits.cc",
+]
+type_mappings = [ "content.mojom.ServiceWorkerProviderHostInfo=::content::ServiceWorkerProviderHostInfo[move_only]" ]
diff --git a/content/common/service_worker/service_worker_provider_host_info.cc b/content/common/service_worker/service_worker_provider_host_info.cc
new file mode 100644
index 0000000..04a1887
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider_host_info.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/service_worker/service_worker_provider_host_info.h"
+
+namespace content {
+
+ServiceWorkerProviderHostInfo::ServiceWorkerProviderHostInfo() {}
+
+ServiceWorkerProviderHostInfo::ServiceWorkerProviderHostInfo(
+    ServiceWorkerProviderHostInfo&& other)
+    : provider_id(other.provider_id),
+      route_id(other.route_id),
+      type(other.type),
+      is_parent_frame_secure(other.is_parent_frame_secure) {}
+
+ServiceWorkerProviderHostInfo::ServiceWorkerProviderHostInfo(
+    int provider_id,
+    int route_id,
+    ServiceWorkerProviderType type,
+    bool is_parent_frame_secure)
+    : provider_id(provider_id),
+      route_id(route_id),
+      type(type),
+      is_parent_frame_secure(is_parent_frame_secure) {}
+
+ServiceWorkerProviderHostInfo::~ServiceWorkerProviderHostInfo() {}
+
+}  // namespace content
diff --git a/content/common/service_worker/service_worker_provider_host_info.h b/content/common/service_worker/service_worker_provider_host_info.h
new file mode 100644
index 0000000..106fb62
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider_host_info.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_INFO_H_
+#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_INFO_H_
+
+#include "content/common/service_worker/service_worker_types.h"
+
+namespace content {
+
+struct CONTENT_EXPORT ServiceWorkerProviderHostInfo {
+  ServiceWorkerProviderHostInfo();
+  ServiceWorkerProviderHostInfo(ServiceWorkerProviderHostInfo&& other);
+  ServiceWorkerProviderHostInfo(int provider_id,
+                                int route_id,
+                                ServiceWorkerProviderType type,
+                                bool is_parent_frame_secure);
+  ~ServiceWorkerProviderHostInfo();
+
+  // This is unique within its child process except for PlzNavigate. When
+  // PlzNavigate is on, |provider_id| is managed on the browser process and it
+  // will be unique among all of providers.
+  int provider_id;
+
+  // When this provider is created for a document, |route_id| is the frame ID of
+  // it. When this provider is created for a Shared Worker, |route_id| is the
+  // Shared Worker route ID. When this provider is created for a Service Worker,
+  // |route_id| is MSG_ROUTING_NONE.
+  int route_id;
+
+  // This identifies whether this provider is for Service Worker controllees
+  // (documents and Shared Workers) or for controllers (Service Workers).
+  ServiceWorkerProviderType type;
+
+  // |is_parent_frame_secure| is false if the provider is created for a document
+  // whose parent frame is not secure from the point of view of the document;
+  // that is, blink::WebFrame::canHaveSecureChild() returns false. This doesn't
+  // mean the document is necessarily an insecure context, because the document
+  // may have a URL whose scheme is granted an exception that allows bypassing
+  // the ancestor secure context check. See the comment in
+  // blink::Document::isSecureContextImpl for more details. If the provider is
+  // not created for a document, or the document does not have a parent frame,
+  // is_parent_frame_secure| is true.
+  bool is_parent_frame_secure;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostInfo);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_INFO_H_
diff --git a/content/common/service_worker/service_worker_provider_struct_traits.cc b/content/common/service_worker/service_worker_provider_struct_traits.cc
new file mode 100644
index 0000000..c4a4307
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider_struct_traits.cc
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/service_worker/service_worker_provider_struct_traits.h"
+#include "content/common/service_worker/service_worker_types_struct_traits.h"
+
+namespace mojo {
+
+bool StructTraits<content::mojom::ServiceWorkerProviderHostInfoDataView,
+                  content::ServiceWorkerProviderHostInfo>::
+    Read(content::mojom::ServiceWorkerProviderHostInfoDataView in,
+         content::ServiceWorkerProviderHostInfo* out) {
+  if (!in.ReadType(&out->type))
+    return false;
+  out->provider_id = in.provider_id();
+  out->route_id = in.route_id();
+  out->is_parent_frame_secure = in.is_parent_frame_secure();
+  return true;
+}
+
+}  // namespace mojo
diff --git a/content/common/service_worker/service_worker_provider_struct_traits.h b/content/common/service_worker/service_worker_provider_struct_traits.h
new file mode 100644
index 0000000..e8cff44
--- /dev/null
+++ b/content/common/service_worker/service_worker_provider_struct_traits.h
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_STRUCT_TRAITS_H_
+#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_STRUCT_TRAITS_H_
+
+#include "content/common/service_worker/service_worker_provider.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<content::mojom::ServiceWorkerProviderHostInfoDataView,
+                    content::ServiceWorkerProviderHostInfo> {
+  static int32_t provider_id(
+      const content::ServiceWorkerProviderHostInfo& info) {
+    return info.provider_id;
+  }
+
+  static int32_t route_id(const content::ServiceWorkerProviderHostInfo& info) {
+    return info.route_id;
+  }
+
+  static content::ServiceWorkerProviderType type(
+      const content::ServiceWorkerProviderHostInfo& info) {
+    return info.type;
+  }
+
+  static bool is_parent_frame_secure(
+      const content::ServiceWorkerProviderHostInfo& info) {
+    return info.is_parent_frame_secure;
+  }
+
+  static bool Read(content::mojom::ServiceWorkerProviderHostInfoDataView in,
+                   content::ServiceWorkerProviderHostInfo* out);
+};
+
+}  // namespace mojo
+
+#endif  // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_STRUCT_TRAITS_H_
diff --git a/content/common/service_worker/service_worker_types.typemap b/content/common/service_worker/service_worker_types.typemap
index 4ec79bbc..c4d4781 100644
--- a/content/common/service_worker/service_worker_types.typemap
+++ b/content/common/service_worker/service_worker_types.typemap
@@ -5,8 +5,8 @@
 mojom = "//content/common/service_worker/service_worker_types.mojom"
 public_headers = [ "//content/common/service_worker/service_worker_types.h" ]
 traits_headers =
-    [ "//content/common/service_worker/service_worker_types_traits.h" ]
+    [ "//content/common/service_worker/service_worker_types_struct_traits.h" ]
 sources = [
-  "//content/common/service_worker/service_worker_types_traits.cc",
+  "//content/common/service_worker/service_worker_types_struct_traits.cc",
 ]
 type_mappings = [ "content.mojom.ServiceWorkerProviderType=::content::ServiceWorkerProviderType" ]
diff --git a/content/common/service_worker/service_worker_types_struct_traits.cc b/content/common/service_worker/service_worker_types_struct_traits.cc
new file mode 100644
index 0000000..f88acbc
--- /dev/null
+++ b/content/common/service_worker/service_worker_types_struct_traits.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/service_worker/service_worker_types_struct_traits.h"
+
+namespace mojo {
+
+content::mojom::ServiceWorkerProviderType
+EnumTraits<content::mojom::ServiceWorkerProviderType,
+           content::ServiceWorkerProviderType>::
+    ToMojom(content::ServiceWorkerProviderType input) {
+  return static_cast<content::mojom::ServiceWorkerProviderType>(input);
+}
+
+bool EnumTraits<content::mojom::ServiceWorkerProviderType,
+                content::ServiceWorkerProviderType>::
+    FromMojom(content::mojom::ServiceWorkerProviderType input,
+              content::ServiceWorkerProviderType* out) {
+  *out = static_cast<content::ServiceWorkerProviderType>(input);
+  return true;
+}
+
+}  // namespace mojo
diff --git a/content/common/service_worker/service_worker_types_traits.h b/content/common/service_worker/service_worker_types_struct_traits.h
similarity index 73%
rename from content/common/service_worker/service_worker_types_traits.h
rename to content/common/service_worker/service_worker_types_struct_traits.h
index 18910a5..fb4670de5 100644
--- a/content/common/service_worker/service_worker_types_traits.h
+++ b/content/common/service_worker/service_worker_types_struct_traits.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_TRAITS_H_
-#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_TRAITS_H_
+#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_STRUCT_TRAITS_H_
+#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_STRUCT_TRAITS_H_
 
 #include "content/common/service_worker/service_worker_types.mojom.h"
 
@@ -21,4 +21,4 @@
 
 }  // namespace mojo
 
-#endif  // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_TRAITS_H_
+#endif  // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_TYPES_STRUCT_TRAITS_H_
diff --git a/content/common/service_worker/service_worker_types_traits.cc b/content/common/service_worker/service_worker_types_traits.cc
deleted file mode 100644
index 1126308a..0000000
--- a/content/common/service_worker/service_worker_types_traits.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/common/service_worker/service_worker_types_traits.h"
-
-namespace mojo {
-
-content::mojom::ServiceWorkerProviderType EnumTraits<
-  content::mojom::ServiceWorkerProviderType,
-  content::ServiceWorkerProviderType>::ToMojom(
-      content::ServiceWorkerProviderType input) {
-  return static_cast<content::mojom::ServiceWorkerProviderType>(input);
-}
-
-bool EnumTraits<content::mojom::ServiceWorkerProviderType,
-                content::ServiceWorkerProviderType>::FromMojom(
-                    content::mojom::ServiceWorkerProviderType input,
-                    content::ServiceWorkerProviderType* out) {
-  *out = static_cast<content::ServiceWorkerProviderType>(input);
-  return true;
-}
-
-}  // namespace mojo
diff --git a/content/common/typemaps.gni b/content/common/typemaps.gni
index 257c74c9..684e474 100644
--- a/content/common/typemaps.gni
+++ b/content/common/typemaps.gni
@@ -7,6 +7,7 @@
   "//content/common/media/media_devices.typemap",
   "//content/common/service_worker/embedded_worker.typemap",
   "//content/common/service_worker/service_worker_event_dispatcher.typemap",
+  "//content/common/service_worker/service_worker_provider.typemap",
   "//content/common/service_worker/service_worker_types.typemap",
   "//content/common/url_loader_status.typemap",
   "//content/common/url_request.typemap",
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e7b7351..8d8206c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -37,6 +37,7 @@
     "../browser/media/session/mock_media_session_observer.h",
     "../browser/service_worker/embedded_worker_test_helper.cc",
     "../browser/service_worker/embedded_worker_test_helper.h",
+    "../browser/service_worker/service_worker_test_utils.cc",
     "../public/test/async_file_test_helper.cc",
     "../public/test/async_file_test_helper.h",
     "../public/test/background_sync_test_util.cc",
diff --git a/headless/README.md b/headless/README.md
index 062106e..b16b69a5 100644
--- a/headless/README.md
+++ b/headless/README.md
@@ -126,8 +126,11 @@
 ## Resources and Documentation
 
 Mailing list: [headless-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/headless-dev)
+
 Bug tracker: [Proj=Headless](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Proj%3DHeadless)
 
+[File a new bug](https://bugs.chromium.org/p/chromium/issues/entry?labels=Proj-Headless)
+
 * [Runtime headless mode for Chrome](https://docs.google.com/document/d/1aIJUzQr3eougZQp90bp4mqGr5gY6hdUice8UPa-Ys90/edit#)
 * [Virtual Time in Blink](https://docs.google.com/document/d/1y9kdt_zezt7pbey6uzvt1dgklwc1ob_vy4nzo1zbqmo/edit#heading=h.tn3gd1y9ifml)
 * [Headless Chrome architecture](https://docs.google.com/document/d/11zIkKkLBocofGgoTeeyibB2TZ_k7nR78v7kNelCatUE/edit)
diff --git a/ios/chrome/app/chrome_app_startup_parameters.mm b/ios/chrome/app/chrome_app_startup_parameters.mm
index f2384cf..74054b02 100644
--- a/ios/chrome/app/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/chrome_app_startup_parameters.mm
@@ -257,6 +257,20 @@
             secureSourceApp:secureSourceApp
                 completeURL:url];
   }
+
+  if ([command
+          isEqualToString:base::SysUTF8ToNSString(
+                              app_group::kChromeAppGroupFocusOmniboxCommand)]) {
+    ChromeAppStartupParameters* params = [[ChromeAppStartupParameters alloc]
+        initWithExternalURL:GURL(kChromeUINewTabURL)
+        xCallbackParameters:nil
+          declaredSourceApp:appId
+            secureSourceApp:secureSourceApp
+                completeURL:url];
+    [params setLaunchFocusOmnibox:YES];
+    return params;
+  }
+
   if ([command isEqualToString:base::SysUTF8ToNSString(
                                    app_group::kChromeAppGroupOpenURLCommand)]) {
     if (!parameter || ![parameter isKindOfClass:[NSString class]])
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index fa2f5539..01120ac 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -259,12 +259,6 @@
   // app.
   base::scoped_nsobject<AppStartupParameters> _startupParameters;
 
-  // Whether Voice Search should be started upon tab switcher dismissal.
-  BOOL _startVoiceSearchAfterTabSwitcherDismissal;
-
-  // Whether the QR Scanner should be started upon tab switcher dismissal.
-  BOOL _startQRScannerAfterTabSwitcherDismissal;
-
   // Navigation View controller for the settings.
   base::scoped_nsobject<SettingsNavigationController>
       _settingsNavigationController;
@@ -372,6 +366,10 @@
 // switcher dismissal. It can only be YES if the QR Scanner experiment is
 // enabled.
 @property(nonatomic, readwrite) BOOL startQRScannerAfterTabSwitcherDismissal;
+// Whether the QR Scanner should be started upon tab switcher dismissal.
+@property(nonatomic, readwrite) BOOL startVoiceSearchAfterTabSwitcherDismissal;
+// Whether the omnibox should be focused upon tab switcher dismissal.
+@property(nonatomic, readwrite) BOOL startFocusOmniboxAfterTabSwitcherDismissal;
 
 // Activates browsing and enables web views if |enabled| is YES.
 // Disables browsing and purges web views if |enabled| is NO.
@@ -544,6 +542,12 @@
 @synthesize window = _window;
 @synthesize isPresentingFirstRunUI = _isPresentingFirstRunUI;
 @synthesize isColdStart = _isColdStart;
+@synthesize startVoiceSearchAfterTabSwitcherDismissal =
+    _startVoiceSearchAfterTabSwitcherDismissal;
+@synthesize startQRScannerAfterTabSwitcherDismissal =
+    _startQRScannerAfterTabSwitcherDismissal;
+@synthesize startFocusOmniboxAfterTabSwitcherDismissal =
+    _startFocusOmniboxAfterTabSwitcherDismissal;
 
 #pragma mark - Application lifecycle
 
@@ -932,14 +936,6 @@
   _settingsNavigationController.reset([settingsNavigationController retain]);
 }
 
-- (BOOL)startQRScannerAfterTabSwitcherDismissal {
-  return _startQRScannerAfterTabSwitcherDismissal;
-}
-
-- (void)setStartQRScannerAfterTabSwitcherDismissal:(BOOL)startQRScanner {
-  _startQRScannerAfterTabSwitcherDismissal = startQRScanner;
-}
-
 #pragma mark - StartupInformation implementation.
 
 - (FirstUserActionRecorder*)firstUserActionRecorder {
@@ -1909,12 +1905,15 @@
 
   // Start Voice Search or QR Scanner now that they can be presented from the
   // current BVC.
-  if (_startVoiceSearchAfterTabSwitcherDismissal) {
-    _startVoiceSearchAfterTabSwitcherDismissal = NO;
+  if (self.startVoiceSearchAfterTabSwitcherDismissal) {
+    self.startVoiceSearchAfterTabSwitcherDismissal = NO;
     [self.currentBVC startVoiceSearch];
-  } else if ([self startQRScannerAfterTabSwitcherDismissal]) {
-    [self setStartQRScannerAfterTabSwitcherDismissal:NO];
+  } else if (self.startQRScannerAfterTabSwitcherDismissal) {
+    self.startQRScannerAfterTabSwitcherDismissal = NO;
     [self.currentBVC showQRScanner];
+  } else if (self.startFocusOmniboxAfterTabSwitcherDismissal) {
+    self.startFocusOmniboxAfterTabSwitcherDismissal = NO;
+    [self.currentBVC focusOmnibox];
   }
 
   [_tabSwitcherController setDelegate:nil];
@@ -2291,8 +2290,8 @@
     if (_tabSwitcherIsActive || _dismissingStackView) {
       // Since VoiceSearch is presented by the BVC, it must be started after the
       // Tab Switcher dismissal completes and the BVC's view is in the
-      // hiararchy.
-      _startVoiceSearchAfterTabSwitcherDismissal = YES;
+      // hierarchy.
+      self.startVoiceSearchAfterTabSwitcherDismissal = YES;
     } else {
       // When starting the application from the Notification center,
       // ApplicationWillResignActive is sent just after startup.
@@ -2306,7 +2305,7 @@
     if (_tabSwitcherIsActive || _dismissingStackView) {
       // QR Scanner is presented by the BVC, similarly to VoiceSearch. It must
       // also be started after the BVC's view is in the hierarchy.
-      [self setStartQRScannerAfterTabSwitcherDismissal:YES];
+      self.startQRScannerAfterTabSwitcherDismissal = YES;
     } else {
       // Start the QR Scanner asynchronously to prevent the application from
       // dismissing the modal view if QR Scanner is started from the
@@ -2315,6 +2314,14 @@
         [self.currentBVC showQRScanner];
       });
     }
+  } else if ([_startupParameters launchFocusOmnibox]) {
+    if (_tabSwitcherIsActive || _dismissingStackView) {
+      self.startFocusOmniboxAfterTabSwitcherDismissal = YES;
+    } else {
+      dispatch_async(dispatch_get_main_queue(), ^{
+        [self.currentBVC focusOmnibox];
+      });
+    }
   }
 
   if (_restoreHelper) {
diff --git a/ios/chrome/browser/app_startup_parameters.h b/ios/chrome/browser/app_startup_parameters.h
index a020265..2d99fe5 100644
--- a/ios/chrome/browser/app_startup_parameters.h
+++ b/ios/chrome/browser/app_startup_parameters.h
@@ -25,6 +25,8 @@
 @property(nonatomic, readwrite, assign) BOOL launchVoiceSearch;
 // Boolean to track if the app should launch in incognito mode.
 @property(nonatomic, readwrite, assign) BOOL launchInIncognito;
+// Boolean to track if the omnibox should be focused on startup.
+@property(nonatomic, readwrite, assign) BOOL launchFocusOmnibox;
 // Boolean to track if a QR scanner is requested at startup.
 @property(nonatomic, readwrite, assign) BOOL launchQRScanner;
 
diff --git a/ios/chrome/browser/app_startup_parameters.mm b/ios/chrome/browser/app_startup_parameters.mm
index 19c4381..0ae884d 100644
--- a/ios/chrome/browser/app_startup_parameters.mm
+++ b/ios/chrome/browser/app_startup_parameters.mm
@@ -14,14 +14,13 @@
 
 @implementation AppStartupParameters {
   GURL _externalURL;
-  BOOL _launchVoiceSearch;
-  BOOL _launchInIncognito;
-  BOOL _launchQRScanner;
 }
 
 @synthesize launchVoiceSearch = _launchVoiceSearch;
 @synthesize launchInIncognito = _launchInIncognito;
 @synthesize xCallbackParameters = _xCallbackParameters;
+@synthesize launchFocusOmnibox = _launchFocusOmnibox;
+@synthesize launchQRScanner = _launchQRScanner;
 
 - (const GURL&)externalURL {
   return _externalURL;
@@ -48,19 +47,27 @@
 }
 
 - (NSString*)description {
-  return [NSString stringWithFormat:@"ExternalURL: %s \nXCallbackParams: %@",
-                                    _externalURL.spec().c_str(),
-                                    _xCallbackParameters];
-}
+  NSMutableString* description = [NSMutableString
+      stringWithFormat:@"ExternalURL: %s \nXCallbackParams: %@",
+                       _externalURL.spec().c_str(), _xCallbackParameters];
 
-#pragma mark Property implementation.
+  if (self.launchQRScanner) {
+    [description appendString:@", should launch QR scanner"];
+  }
 
-- (BOOL)launchQRScanner {
-  return _launchQRScanner;
-}
+  if (self.launchInIncognito) {
+    [description appendString:@", should launch in incognito"];
+  }
 
-- (void)setLaunchQRScanner:(BOOL)launch {
-  _launchQRScanner = launch;
+  if (self.launchFocusOmnibox) {
+    [description appendString:@", should focus omnibox"];
+  }
+
+  if (self.launchVoiceSearch) {
+    [description appendString:@", should launch voice search"];
+  }
+
+  return description;
 }
 
 @end
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
index e7ac5c6..ee85ec4 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
@@ -93,9 +93,6 @@
 - (void)openFirstPageOfReadingList {
 }
 
-- (void)addEmptyItem {
-}
-
 - (void)openFaviconAtIndex:(NSInteger)index {
 }
 
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
index 242f0e64..6a6d4b1a 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
@@ -14,7 +14,6 @@
 #import "ios/chrome/browser/content_suggestions/content_suggestions_service_bridge_observer.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestion.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_section_information.h"
 #include "ui/gfx/image/image.h"
 
diff --git a/ios/chrome/browser/favicon/favicon_service_factory.cc b/ios/chrome/browser/favicon/favicon_service_factory.cc
index 0c72fd5..2732a769 100644
--- a/ios/chrome/browser/favicon/favicon_service_factory.cc
+++ b/ios/chrome/browser/favicon/favicon_service_factory.cc
@@ -58,4 +58,8 @@
           browser_state, ServiceAccessType::EXPLICIT_ACCESS));
 }
 
+bool FaviconServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
 }  // namespace ios
diff --git a/ios/chrome/browser/favicon/favicon_service_factory.h b/ios/chrome/browser/favicon/favicon_service_factory.h
index 8d81f8c..dc8661d 100644
--- a/ios/chrome/browser/favicon/favicon_service_factory.h
+++ b/ios/chrome/browser/favicon/favicon_service_factory.h
@@ -43,6 +43,7 @@
   // BrowserStateKeyedServiceFactory implementation.
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
 
   DISALLOW_COPY_AND_ASSIGN(FaviconServiceFactory);
 };
diff --git a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
index 1125c07d..5d01fb2 100644
--- a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
+++ b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.cc
@@ -32,7 +32,9 @@
 IOSChromeLargeIconServiceFactory::IOSChromeLargeIconServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "LargeIconService",
-          BrowserStateDependencyManager::GetInstance()) {}
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ios::FaviconServiceFactory::GetInstance());
+}
 
 IOSChromeLargeIconServiceFactory::~IOSChromeLargeIconServiceFactory() {}
 
@@ -52,3 +54,7 @@
     web::BrowserState* context) const {
   return GetBrowserStateOwnInstanceInIncognito(context);
 }
+
+bool IOSChromeLargeIconServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
diff --git a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
index d6883a7..071e3f1 100644
--- a/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
+++ b/ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h
@@ -46,6 +46,7 @@
       web::BrowserState* context) const override;
   web::BrowserState* GetBrowserStateToUse(
       web::BrowserState* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
 
   DISALLOW_COPY_AND_ASSIGN(IOSChromeLargeIconServiceFactory);
 };
diff --git a/ios/chrome/browser/ui/browser_view_controller.h b/ios/chrome/browser/ui/browser_view_controller.h
index 2ee314f..4b004dd 100644
--- a/ios/chrome/browser/ui/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view_controller.h
@@ -139,6 +139,9 @@
 // Shows the QR scanner UI.
 - (void)showQRScanner;
 
+// Focuses the omnibox.
+- (void)focusOmnibox;
+
 // Dismisses all presented views then calls |completion|.
 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion;
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 394aefd..dedf5a9 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -133,8 +133,8 @@
 #import "ios/chrome/browser/ui/print/print_controller.h"
 #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h"
 #import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_controller.h"
 #import "ios/chrome/browser/ui/stack_view/card_view.h"
@@ -403,6 +403,9 @@
   // Used to display the QR Scanner UI. Nil if not visible.
   base::scoped_nsobject<QRScannerViewController> _qrScannerViewController;
 
+  // Used to display the Reading List.
+  base::scoped_nsobject<ReadingListCoordinator> _readingListCoordinator;
+
   // Used to display the Suggestions.
   base::scoped_nsobject<ContentSuggestionsCoordinator>
       _contentSuggestionsCoordinator;
@@ -1281,7 +1284,9 @@
     self.typingShield = nil;
     if (_voiceSearchController.get())
       _voiceSearchController->SetDelegate(nil);
+    _contentSuggestionsCoordinator.reset();
     _qrScannerViewController.reset();
+    _readingListCoordinator.reset();
     _toolbarController.reset();
     _toolbarModelDelegate.reset();
     _toolbarModelIOS.reset();
@@ -4228,10 +4233,12 @@
 
 - (void)showReadingList {
   DCHECK(reading_list::switches::IsReadingListEnabled());
-  UIViewController* vc = [ReadingListViewControllerBuilder
-      readingListViewControllerInBrowserState:self.browserState
-                                       loader:self];
-  [self presentViewController:vc animated:YES completion:nil];
+  _readingListCoordinator.reset([[ReadingListCoordinator alloc]
+      initWithBaseViewController:self
+                    browserState:self.browserState
+                          loader:self]);
+
+  [_readingListCoordinator start];
 }
 
 - (void)showQRScanner {
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 5e2ce300..e35648b 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -20,14 +20,14 @@
     "content_suggestions_favicon_internal_cell.mm",
     "content_suggestions_favicon_item.h",
     "content_suggestions_favicon_item.mm",
-    "content_suggestions_item.h",
-    "content_suggestions_item.mm",
-    "content_suggestions_item_actions.h",
     "content_suggestions_section_information.h",
     "content_suggestions_section_information.mm",
     "content_suggestions_stack_item.h",
     "content_suggestions_stack_item.mm",
     "content_suggestions_stack_item_actions.h",
+    "content_suggestions_text_item.h",
+    "content_suggestions_text_item.mm",
+    "content_suggestions_text_item_actions.h",
     "content_suggestions_view_controller.h",
     "content_suggestions_view_controller.mm",
     "expandable_item.h",
@@ -52,8 +52,8 @@
     "content_suggestions_article_item_unittest.mm",
     "content_suggestions_expandable_item_unittest.mm",
     "content_suggestions_favicon_item_unittest.mm",
-    "content_suggestions_item_unittest.mm",
     "content_suggestions_stack_item_unittest.mm",
+    "content_suggestions_text_item_unittest.mm",
   ]
   deps = [
     ":content_suggestions",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
index dfa156e1..f1c79f6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
@@ -28,13 +28,6 @@
 @property(nonatomic, assign)
     ContentSuggestionsViewController* collectionViewController;
 
-// Adds a text item with a |title| and a |subtitle| in the section numbered
-// |section|. If |section| is greater than the current number of section, it
-// will add a new section at the end.
-- (void)addTextItem:(NSString*)title
-           subtitle:(NSString*)subtitle
-          toSection:(NSInteger)inputSection;
-
 // Returns whether the section should use the default, non-card style.
 - (BOOL)shouldUseCustomStyleForSection:(NSInteger)section;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 84b07616..210b089 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -15,9 +15,9 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_source.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_expandable_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_favicon_item.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_section_information.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_stack_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #include "url/gurl.h"
 
@@ -126,42 +126,6 @@
 
 #pragma mark - Public methods
 
-- (void)addTextItem:(NSString*)title
-           subtitle:(NSString*)subtitle
-          toSection:(NSInteger)inputSection {
-  DCHECK(self.collectionViewController);
-  ContentSuggestionsItem* item =
-      [[ContentSuggestionsItem alloc] initWithType:ItemTypeText
-                                             title:title
-                                          subtitle:subtitle];
-  NSInteger sectionIdentifier = kSectionIdentifierEnumZero + inputSection;
-  NSInteger sectionIndex = inputSection;
-  CollectionViewModel* model =
-      self.collectionViewController.collectionViewModel;
-  if ([model numberOfSections] <= inputSection) {
-    sectionIndex = [model numberOfSections];
-    sectionIdentifier = kSectionIdentifierEnumZero + sectionIndex;
-    [self.collectionViewController.collectionView performBatchUpdates:^{
-      [self.collectionViewController.collectionViewModel
-          addSectionWithIdentifier:sectionIdentifier];
-      [self.collectionViewController.collectionView
-          insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
-    }
-                                                           completion:nil];
-  }
-  NSInteger numberOfItemsInSection =
-      [model numberOfItemsInSection:sectionIndex];
-  [self.collectionViewController.collectionViewModel addItem:item
-                                     toSectionWithIdentifier:sectionIdentifier];
-  [self.collectionViewController.collectionView performBatchUpdates:^{
-    [self.collectionViewController.collectionView
-        insertItemsAtIndexPaths:@[ [NSIndexPath
-                                    indexPathForRow:numberOfItemsInSection
-                                          inSection:sectionIndex] ]];
-  }
-                                                         completion:nil];
-}
-
 - (BOOL)shouldUseCustomStyleForSection:(NSInteger)section {
   NSNumber* identifier = @([self.collectionViewController.collectionViewModel
       sectionIdentifierForSection:section]);
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
index 19eda28..7c11505 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -11,8 +11,6 @@
 // Commands protocol for the ContentSuggestionsViewController.
 @protocol ContentSuggestionsCommands
 
-// Adds a new empty SuggestionItem.
-- (void)addEmptyItem;
 // Opens the Reading List.
 - (void)openReadingList;
 // Opens the first page of the Reading List.
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h
similarity index 85%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h
rename to ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h
index 87b27e3..f2f8752 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_H_
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_H_
 
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
 // Item for a suggestions item with a title and subtitle.
-@interface ContentSuggestionsItem : CollectionViewItem
+@interface ContentSuggestionsTextItem : CollectionViewItem
 
 // Init a suggestions item with a |title| and a |subtitle|. |type| is the type
 // of the item.
@@ -21,11 +21,11 @@
 @end
 
 // Corresponding cell for a suggestion item.
-@interface ContentSuggestionsCell : MDCCollectionViewCell
+@interface ContentSuggestionsTextCell : MDCCollectionViewCell
 
 @property(nonatomic, readonly, strong) UIButton* titleButton;
 @property(nonatomic, readonly, strong) UILabel* detailTextLabel;
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_H_
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.mm
similarity index 88%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_item.mm
rename to ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.mm
index abc84257..96f6e678 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.mm
@@ -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 "ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h"
 
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_item_actions.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_actions.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 
@@ -12,14 +12,14 @@
 #error "This file requires ARC support."
 #endif
 
-@interface ContentSuggestionsItem ()
+@interface ContentSuggestionsTextItem ()
 
 @property(nonatomic, copy) NSString* title;
 @property(nonatomic, copy) NSString* subtitle;
 
 @end
 
-@implementation ContentSuggestionsItem
+@implementation ContentSuggestionsTextItem
 
 @synthesize title = _title;
 @synthesize subtitle = _subtitle;
@@ -29,7 +29,7 @@
                     subtitle:(NSString*)subtitle {
   self = [super initWithType:type];
   if (self) {
-    self.cellClass = [ContentSuggestionsCell class];
+    self.cellClass = [ContentSuggestionsTextCell class];
     _title = [title copy];
     _subtitle = [subtitle copy];
   }
@@ -38,7 +38,7 @@
 
 #pragma mark - CollectionViewItem
 
-- (void)configureCell:(ContentSuggestionsCell*)cell {
+- (void)configureCell:(ContentSuggestionsTextCell*)cell {
   [super configureCell:cell];
   [cell.titleButton setTitle:self.title forState:UIControlStateNormal];
   cell.detailTextLabel.text = self.subtitle;
@@ -46,9 +46,9 @@
 
 @end
 
-#pragma mark - ContentSuggestionsCell
+#pragma mark - ContentSuggestionsTextCell
 
-@implementation ContentSuggestionsCell
+@implementation ContentSuggestionsTextCell
 
 @synthesize titleButton = _titleButton;
 @synthesize detailTextLabel = _detailTextLabel;
@@ -61,7 +61,7 @@
     _titleButton.titleLabel.font = [fontLoader mediumFontOfSize:16];
     _titleButton.titleLabel.textColor = [[MDCPalette greyPalette] tint900];
     [_titleButton addTarget:nil
-                     action:@selector(addNewItem:)
+                     action:@selector(addNewTextItem:)
            forControlEvents:UIControlEventTouchUpInside];
 
     _detailTextLabel = [[UILabel alloc] init];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item_actions.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_actions.h
similarity index 76%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_item_actions.h
rename to ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_actions.h
index 0d1a80cd..64018d5 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item_actions.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_actions.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_ACTIONS_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_ACTIONS_H_
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_ACTIONS_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_ACTIONS_H_
 
 // Protocol handling the actions sent in the responder chain by the suggestions
 // items.
-@protocol SuggestionsItemActions
+@protocol ContentSuggestionsTextItemActions
 
 // Sent through the responder chain when the button of a suggestion item is
 // pressed.
-- (void)addNewItem:(id)sender;
+- (void)addNewTextItem:(id)sender;
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_ITEM_ACTIONS_H_
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_TEXT_ITEM_ACTIONS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_unittest.mm
similarity index 62%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_item_unittest.mm
rename to ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_unittest.mm
index dbac1dcb..296d2c9 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_item_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -13,15 +13,15 @@
 namespace {
 
 // Tests that configureCell: set all the fields of the cell.
-TEST(ContentSuggestionsItemTest, CellIsConfigured) {
+TEST(ContentSuggestionsTextItemTest, CellIsConfigured) {
   NSString* title = @"testTitle";
   NSString* subtitle = @"testSubtitle";
-  ContentSuggestionsItem* item =
-      [[ContentSuggestionsItem alloc] initWithType:0
-                                             title:title
-                                          subtitle:subtitle];
-  ContentSuggestionsCell* cell = [[[item cellClass] alloc] init];
-  EXPECT_EQ([ContentSuggestionsCell class], [cell class]);
+  ContentSuggestionsTextItem* item =
+      [[ContentSuggestionsTextItem alloc] initWithType:0
+                                                 title:title
+                                              subtitle:subtitle];
+  ContentSuggestionsTextCell* cell = [[[item cellClass] alloc] init];
+  EXPECT_EQ([ContentSuggestionsTextCell class], [cell class]);
 
   [item configureCell:cell];
   EXPECT_EQ(title, [cell.titleButton titleForState:UIControlStateNormal]);
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
index b078851e..e25bdb9 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
@@ -30,13 +30,6 @@
 @property(nonatomic, weak) id<ContentSuggestionsCommands>
     suggestionCommandHandler;
 
-// Adds a text item with a |title| and a |subtitle| in the section numbered
-// |section|. If |section| is greater than the current number of section, it
-// will add a new section at the end.
-- (void)addTextItem:(NSString*)title
-           subtitle:(NSString*)subtitle
-          toSection:(NSInteger)inputSection;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index e6ff832..3fb509f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -12,9 +12,9 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_article_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_item_actions.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_stack_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_stack_item_actions.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_text_item_actions.h"
 #import "ios/chrome/browser/ui/content_suggestions/expandable_item.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -25,8 +25,7 @@
 const NSTimeInterval kAnimationDuration = 0.35;
 }  // namespace
 
-@interface ContentSuggestionsViewController ()<SuggestionsItemActions,
-                                               SuggestionsStackItemActions>
+@interface ContentSuggestionsViewController ()<SuggestionsStackItemActions>
 
 @property(nonatomic, strong)
     ContentSuggestionsCollectionUpdater* collectionUpdater;
@@ -101,22 +100,6 @@
   [self.suggestionCommandHandler openFaviconAtIndex:innerIndexPath.item];
 }
 
-#pragma mark - SuggestionsItemActions
-
-- (void)addNewItem:(id)sender {
-  [self.suggestionCommandHandler addEmptyItem];
-}
-
-#pragma mark - ContentSuggestionsCollectionUpdater forwarding
-
-- (void)addTextItem:(NSString*)title
-           subtitle:(NSString*)subtitle
-          toSection:(NSInteger)inputSection {
-  [self.collectionUpdater addTextItem:title
-                             subtitle:subtitle
-                            toSection:inputSection];
-}
-
 #pragma mark - SuggestionsStackItemActions
 
 - (void)openReadingListFirstItem:(id)sender {
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index fa75f37d..5927059 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -39,6 +39,8 @@
     "reading_list_collection_view_controller.mm",
     "reading_list_collection_view_item.h",
     "reading_list_collection_view_item.mm",
+    "reading_list_coordinator.h",
+    "reading_list_coordinator.mm",
     "reading_list_empty_collection_background.h",
     "reading_list_empty_collection_background.mm",
     "reading_list_menu_notification_delegate.h",
@@ -50,8 +52,6 @@
     "reading_list_toolbar.mm",
     "reading_list_view_controller.h",
     "reading_list_view_controller.mm",
-    "reading_list_view_controller_builder.h",
-    "reading_list_view_controller_builder.mm",
   ]
   deps = [
     ":resources",
@@ -97,7 +97,7 @@
   sources = [
     "offline_page_native_content_unittest.mm",
     "reading_list_collection_view_controller_unittest.mm",
-    "reading_list_view_controller_unittest.mm",
+    "reading_list_coordinator_unittest.mm",
   ]
   deps = [
     ":reading_list",
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h
index d4b201f..8f33301 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h
@@ -21,16 +21,17 @@
 class ReadingListModel;
 @class TabModel;
 
-// Delegate for the ReadingListCollectionViewController, managing the visibility
-// of the
-// toolbar, dismissing the Reading List View and opening elements.
-@protocol ReadingListCollectionViewControllerDelegate<NSObject>
+// Audience for the ReadingListCollectionViewController
+@protocol ReadingListCollectionViewControllerAudience
 
 // Whether the collection has items.
-- (void)readingListCollectionViewController:
-            (ReadingListCollectionViewController*)
-                readingListCollectionViewController
-                                   hasItems:(BOOL)hasItems;
+- (void)readingListHasItems:(BOOL)hasItems;
+
+@end
+
+// Delegate for the ReadingListCollectionViewController, managing the visibility
+// of the toolbar, dismissing the Reading List View and opening elements.
+@protocol ReadingListCollectionViewControllerDelegate<NSObject>
 
 // Dismisses the Reading List View.
 - (void)dismissReadingListCollectionViewController:
@@ -69,6 +70,9 @@
 
 @property(nonatomic, weak) id<ReadingListCollectionViewControllerDelegate>
     delegate;
+@property(nonatomic, weak) id<ReadingListCollectionViewControllerAudience>
+    audience;
+
 @property(nonatomic, readonly) ReadingListModel* readingListModel;
 @property(nonatomic, readonly) favicon::LargeIconService* largeIconService;
 @property(nonatomic, readonly)
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
index 85237d7b..2ff56c1a 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
@@ -175,12 +175,16 @@
 @end
 
 @implementation ReadingListCollectionViewController
+
+@synthesize audience = _audience;
+@synthesize delegate = _delegate;
+
 @synthesize readingListModel = _readingListModel;
 @synthesize largeIconService = _largeIconService;
 @synthesize readingListDownloadService = _readingListDownloadService;
 @synthesize attributesProvider = _attributesProvider;
-@synthesize delegate = _delegate;
 @synthesize shouldMonitorModel = _shouldMonitorModel;
+
 #pragma mark lifecycle
 
 - (instancetype)initWithModel:(ReadingListModel*)model
@@ -228,13 +232,11 @@
   [_toolbar setState:toolbarState];
 }
 
-- (void)setDelegate:(id<ReadingListCollectionViewControllerDelegate>)delegate {
-  _delegate = delegate;
-  if (self.readingListModel->loaded())
-    [delegate
-        readingListCollectionViewController:self
-                                   hasItems:(self.readingListModel->size() >
-                                             0)];
+- (void)setAudience:(id<ReadingListCollectionViewControllerAudience>)audience {
+  _audience = audience;
+  if (self.readingListModel->loaded()) {
+    [audience readingListHasItems:(self.readingListModel->size() > 0)];
+  }
 }
 
 #pragma mark - UIViewController
@@ -412,7 +414,7 @@
     self.collectionView.alwaysBounceVertical = YES;
     [self loadItems];
     self.collectionView.backgroundView = nil;
-    [self.delegate readingListCollectionViewController:self hasItems:YES];
+    [self.audience readingListHasItems:YES];
   }
 }
 
@@ -546,7 +548,7 @@
   // The collection is empty, add background.
   self.collectionView.alwaysBounceVertical = NO;
   self.collectionView.backgroundView = _emptyCollectionBackground;
-  [self.delegate readingListCollectionViewController:self hasItems:NO];
+  [self.audience readingListHasItems:NO];
 }
 
 - (void)handleLongPress:(UILongPressGestureRecognizer*)gestureRecognizer {
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.h b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.h
new file mode 100644
index 0000000..1d6480b
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COORDINATOR_H_
+
+#import "ios/chrome/browser/chrome_coordinator.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h"
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+@protocol UrlLoader;
+
+// Coordinator for Reading List, displaying the Reading List when starting.
+@interface ReadingListCoordinator
+    : ChromeCoordinator<ReadingListCollectionViewControllerDelegate>
+
+- (nullable instancetype)
+initWithBaseViewController:(nullable UIViewController*)viewController
+              browserState:(nonnull ios::ChromeBrowserState*)browserState
+                    loader:(nullable id<UrlLoader>)loader
+    NS_DESIGNATED_INITIALIZER;
+
+- (nullable instancetype)initWithBaseViewController:
+    (nullable UIViewController*)viewController NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
new file mode 100644
index 0000000..1958db5
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
@@ -0,0 +1,289 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "components/reading_list/ios/reading_list_model.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
+#include "ios/chrome/browser/reading_list/offline_url_utils.h"
+#include "ios/chrome/browser/reading_list/reading_list_download_service.h"
+#include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h"
+#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
+#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_toolbar.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller.h"
+#import "ios/chrome/browser/ui/url_loader.h"
+#import "ios/chrome/browser/ui/util/pasteboard_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/referrer.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Action chosen by the user in the context menu, for UMA report.
+// These match tools/metrics/histograms/histograms.xml.
+enum UMAContextMenuAction {
+  // The user opened the entry in a new tab.
+  NEW_TAB = 0,
+  // The user opened the entry in a new incognito tab.
+  NEW_INCOGNITO_TAB = 1,
+  // The user copied the url of the entry.
+  COPY_LINK = 2,
+  // The user chose to view the offline version of the entry.
+  VIEW_OFFLINE = 3,
+  // The user cancelled the context menu.
+  CANCEL = 4,
+  // Add new enum above ENUM_MAX.
+  ENUM_MAX
+};
+}
+
+@interface ReadingListCoordinator ()
+
+@property(nonatomic, assign) ios::ChromeBrowserState* browserState;
+// Used to load the Reading List pages.
+@property(nonatomic, weak) id<UrlLoader> URLLoader;
+@property(nonatomic, strong) ReadingListViewController* containerViewController;
+@property(nonatomic, strong) AlertCoordinator* alertCoordinator;
+
+// Opens |URL| in a new tab |incognito| or not.
+- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito;
+
+// Opens the offline url |offlineURL| of the entry saved in the reading list
+// model with the |entryURL| url.
+- (void)openOfflineURL:(const GURL&)offlineURL
+                      correspondingEntryURL:(const GURL&)entryURL
+    fromReadingListCollectionViewController:
+        (ReadingListCollectionViewController*)
+            readingListCollectionViewController;
+
+@end
+
+@implementation ReadingListCoordinator
+
+@synthesize alertCoordinator = _alertCoordinator;
+@synthesize containerViewController = _containerViewController;
+@synthesize URLLoader = _URLLoader;
+@synthesize browserState = _browserState;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+                                    loader:(id<UrlLoader>)loader {
+  self = [super initWithBaseViewController:viewController];
+  if (self) {
+    _browserState = browserState;
+    _URLLoader = loader;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  if (!self.containerViewController) {
+    ReadingListModel* model =
+        ReadingListModelFactory::GetInstance()->GetForBrowserState(
+            self.browserState);
+    favicon::LargeIconService* largeIconService =
+        IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState);
+    ReadingListDownloadService* readingListDownloadService =
+        ReadingListDownloadServiceFactory::GetInstance()->GetForBrowserState(
+            self.browserState);
+
+    ReadingListToolbar* toolbar = [[ReadingListToolbar alloc] init];
+    ReadingListCollectionViewController* collectionViewController =
+        [[ReadingListCollectionViewController alloc]
+                         initWithModel:model
+                      largeIconService:largeIconService
+            readingListDownloadService:readingListDownloadService
+                               toolbar:toolbar];
+    collectionViewController.delegate = self;
+
+    self.containerViewController = [[ReadingListViewController alloc]
+        initWithCollectionViewController:collectionViewController
+                                 toolbar:toolbar];
+    self.containerViewController.delegate = self;
+  }
+
+  [self.baseViewController presentViewController:self.containerViewController
+                                        animated:YES
+                                      completion:nil];
+}
+
+- (void)stop {
+  [self.containerViewController.presentingViewController
+      dismissViewControllerAnimated:YES
+                         completion:nil];
+
+  self.containerViewController = nil;
+}
+
+#pragma mark - ReadingListCollectionViewControllerDelegate
+
+- (void)dismissReadingListCollectionViewController:
+    (ReadingListCollectionViewController*)readingListCollectionViewController {
+  [readingListCollectionViewController willBeDismissed];
+  [self stop];
+}
+
+- (void)readingListCollectionViewController:
+            (ReadingListCollectionViewController*)
+                readingListCollectionViewController
+                  displayContextMenuForItem:
+                      (ReadingListCollectionViewItem*)readingListItem
+                                    atPoint:(CGPoint)menuLocation {
+  if (!self.containerViewController) {
+    return;
+  }
+
+  const ReadingListEntry* entry =
+      readingListCollectionViewController.readingListModel->GetEntryByURL(
+          readingListItem.url);
+
+  if (!entry) {
+    [readingListCollectionViewController reloadData];
+    return;
+  }
+  const GURL entryURL = entry->URL();
+
+  __weak ReadingListCoordinator* weakSelf = self;
+
+  _alertCoordinator = [[ActionSheetCoordinator alloc]
+      initWithBaseViewController:self.containerViewController
+                           title:readingListItem.text
+                         message:readingListItem.detailText
+                            rect:CGRectMake(menuLocation.x, menuLocation.y, 0,
+                                            0)
+                            view:readingListCollectionViewController
+                                     .collectionView];
+
+  NSString* openInNewTabTitle =
+      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB);
+  [_alertCoordinator
+      addItemWithTitle:openInNewTabTitle
+                action:^{
+                  [weakSelf openNewTabWithURL:entryURL incognito:NO];
+                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", NEW_TAB,
+                                            ENUM_MAX);
+
+                }
+                 style:UIAlertActionStyleDefault];
+
+  NSString* openInNewTabIncognitoTitle =
+      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB);
+  [_alertCoordinator
+      addItemWithTitle:openInNewTabIncognitoTitle
+                action:^{
+                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+                                            NEW_INCOGNITO_TAB, ENUM_MAX);
+                  [weakSelf openNewTabWithURL:entryURL incognito:YES];
+                }
+                 style:UIAlertActionStyleDefault];
+
+  NSString* copyLinkTitle =
+      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY);
+  [_alertCoordinator
+      addItemWithTitle:copyLinkTitle
+                action:^{
+                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+                                            COPY_LINK, ENUM_MAX);
+                  StoreURLInPasteboard(entryURL);
+                }
+                 style:UIAlertActionStyleDefault];
+
+  if (entry->DistilledState() == ReadingListEntry::PROCESSED) {
+    GURL offlineURL = reading_list::OfflineURLForPath(
+        entry->DistilledPath(), entryURL, entry->DistilledURL());
+    NSString* viewOfflineVersionTitle =
+        l10n_util::GetNSString(IDS_IOS_READING_LIST_CONTENT_CONTEXT_OFFLINE);
+    [_alertCoordinator
+        addItemWithTitle:viewOfflineVersionTitle
+                  action:^{
+                    UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
+                                              VIEW_OFFLINE, ENUM_MAX);
+                    [weakSelf openOfflineURL:offlineURL
+                                          correspondingEntryURL:entryURL
+                        fromReadingListCollectionViewController:
+                            readingListCollectionViewController];
+                  }
+                   style:UIAlertActionStyleDefault];
+  }
+
+  [_alertCoordinator
+      addItemWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)
+                action:^{
+                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", CANCEL,
+                                            ENUM_MAX);
+                }
+                 style:UIAlertActionStyleCancel];
+
+  [_alertCoordinator start];
+}
+
+- (void)
+readingListCollectionViewController:
+    (ReadingListCollectionViewController*)readingListCollectionViewController
+                           openItem:
+                               (ReadingListCollectionViewItem*)readingListItem {
+  const ReadingListEntry* entry =
+      readingListCollectionViewController.readingListModel->GetEntryByURL(
+          readingListItem.url);
+
+  if (!entry) {
+    [readingListCollectionViewController reloadData];
+    return;
+  }
+
+  base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
+
+  [self.URLLoader loadURL:entry->URL()
+                 referrer:web::Referrer()
+               transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
+        rendererInitiated:NO];
+
+  [self stop];
+}
+
+#pragma mark - Private
+
+- (void)openOfflineURL:(const GURL&)offlineURL
+                      correspondingEntryURL:(const GURL&)entryURL
+    fromReadingListCollectionViewController:
+        (ReadingListCollectionViewController*)
+            readingListCollectionViewController {
+  [readingListCollectionViewController willBeDismissed];
+
+  [self openNewTabWithURL:offlineURL incognito:NO];
+
+  UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
+  const GURL updateURL = entryURL;
+  readingListCollectionViewController.readingListModel->SetReadStatus(updateURL,
+                                                                      true);
+}
+
+- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito {
+  base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
+
+  [self.URLLoader webPageOrderedOpen:URL
+                            referrer:web::Referrer()
+                          windowName:nil
+                         inIncognito:incognito
+                        inBackground:NO
+                            appendTo:kLastTab];
+
+  [self stop];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_unittest.mm b/ios/chrome/browser/ui/reading_list/reading_list_coordinator_unittest.mm
similarity index 82%
rename from ios/chrome/browser/ui/reading_list/reading_list_view_controller_unittest.mm
rename to ios/chrome/browser/ui/reading_list/reading_list_coordinator_unittest.mm
index d41a48d7..c0aec66 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller.h"
+#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
 
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
@@ -12,6 +12,7 @@
 #include "components/favicon/core/large_icon_service.h"
 #include "components/reading_list/ios/reading_list_entry.h"
 #include "components/reading_list/ios/reading_list_model_impl.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
@@ -110,29 +111,32 @@
 
 @end
 
-#pragma mark - ReadingListViewControllerTest
+#pragma mark - ReadingListCoordinatorTest
 
-class ReadingListViewControllerTest : public web::WebTestWithWebState {
+class ReadingListCoordinatorTest : public web::WebTestWithWebState {
  public:
-  ReadingListViewControllerTest() {
+  ReadingListCoordinatorTest() {
     loader_mock_.reset([[UrlLoaderStub alloc] init]);
     mock_favicon_service_.reset(new MockFaviconService());
 
+    TestChromeBrowserState::Builder builder;
+    browser_state_ = builder.Build();
+
     reading_list_model_.reset(new ReadingListModelImpl(nullptr, nullptr));
     large_icon_service_.reset(new favicon::LargeIconService(
         mock_favicon_service_.get(), base::ThreadTaskRunnerHandle::Get()));
-    container_.reset([[ReadingListViewController alloc]
-                     initWithModel:reading_list_model_.get()
-                            loader:loader_mock_
-                  largeIconService:large_icon_service_.get()
-        readingListDownloadService:nil]);
+    coordinator_.reset([[ReadingListCoordinator alloc]
+        initWithBaseViewController:nil
+                      browserState:browser_state_.get()
+                            loader:loader_mock_]);
   }
-  ~ReadingListViewControllerTest() override {}
+  ~ReadingListCoordinatorTest() override {}
 
-  ReadingListViewController* GetContainer() { return container_; }
+  ReadingListCoordinator* GetCoordinator() { return coordinator_; }
 
   ReadingListModel* GetReadingListModel() { return reading_list_model_.get(); }
   UrlLoaderStub* GetLoaderStub() { return loader_mock_; }
+
   ReadingListCollectionViewController*
   GetAReadingListCollectionViewController() {
     return [[[ReadingListCollectionViewController alloc]
@@ -143,16 +147,17 @@
   }
 
  private:
-  base::scoped_nsobject<ReadingListViewController> container_;
+  base::scoped_nsobject<ReadingListCoordinator> coordinator_;
   std::unique_ptr<ReadingListModelImpl> reading_list_model_;
   base::scoped_nsobject<UrlLoaderStub> loader_mock_;
   std::unique_ptr<favicon::LargeIconService> large_icon_service_;
   std::unique_ptr<MockFaviconService> mock_favicon_service_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
 };
 
-// Tests that the implementation of ReadingListCollectionViewController
-// openItemAtIndexPath opens the entry.
-TEST_F(ReadingListViewControllerTest, OpenItem) {
+// Tests that the implementation of ReadingListCoordinator openItemAtIndexPath
+// opens the entry.
+TEST_F(ReadingListCoordinatorTest, OpenItem) {
   // Setup.
   GURL url("https://chromium.org");
   std::string title("Chromium");
@@ -169,9 +174,9 @@
            distillationState:ReadingListEntry::PROCESSED]);
 
   // Action.
-  [GetContainer() readingListCollectionViewController:
-                      GetAReadingListCollectionViewController()
-                                             openItem:item];
+  [GetCoordinator() readingListCollectionViewController:
+                        GetAReadingListCollectionViewController()
+                                               openItem:item];
 
   // Tests.
   UrlLoaderStub* loader = GetLoaderStub();
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.h b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.h
index 1904d97..7584304 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.h
@@ -5,34 +5,28 @@
 #ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_VIEW_CONTROLLER_H_
 #define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_VIEW_CONTROLLER_H_
 
-#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar.h"
 
-namespace favicon {
-class LargeIconService;
-}
-
 @class ReadingListCollectionViewController;
-class ReadingListDownloadService;
-class ReadingListModel;
-@protocol UrlLoader;
+@protocol ReadingListCollectionViewControllerDelegate;
 
 // Container for the ReadingList Collection View Controller and the toolbar. It
-// handles the interactions between the two. It also acts as a ReadingList
-// delegate, opening entries and displaying context menu.
-@interface ReadingListViewController
-    : UIViewController<ReadingListCollectionViewControllerDelegate>
+// handles the interactions between the two.
+@interface ReadingListViewController : UIViewController
 
-- (instancetype)initWithModel:(ReadingListModel*)model
-                        loader:(id<UrlLoader>)loader
-              largeIconService:(favicon::LargeIconService*)largeIconService
-    readingListDownloadService:
-        (ReadingListDownloadService*)readingListDownloadService
+- (instancetype)initWithCollectionViewController:
+                    (ReadingListCollectionViewController*)
+                        collectionViewController
+                                         toolbar:(ReadingListToolbar*)toolbar
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithNibName:(NSString*)nibNameOrNil
                          bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+@property(nonatomic, weak) id<ReadingListCollectionViewControllerDelegate>
+    delegate;
 
 @end
 
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
index 253ce847..6e0eb577 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
@@ -6,47 +6,16 @@
 
 #import <MobileCoreServices/MobileCoreServices.h>
 
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "components/reading_list/ios/reading_list_model.h"
-#include "ios/chrome/browser/reading_list/offline_url_utils.h"
-#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
-#import "ios/chrome/browser/ui/url_loader.h"
-#import "ios/chrome/browser/ui/util/pasteboard_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ios/web/public/navigation_manager.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/strings/grit/ui_strings.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 namespace {
-// Action chosen by the user in the context menu, for UMA report.
-// These match tools/metrics/histograms/histograms.xml.
-enum UMAContextMenuAction {
-  // The user opened the entry in a new tab.
-  NEW_TAB = 0,
-  // The user opened the entry in a new incognito tab.
-  NEW_INCOGNITO_TAB = 1,
-  // The user copied the url of the entry.
-  COPY_LINK = 2,
-  // The user chose to view the offline version of the entry.
-  VIEW_OFFLINE = 3,
-  // The user cancelled the context menu.
-  CANCEL = 4,
-  // Add new enum above ENUM_MAX.
-  ENUM_MAX
-};
-
 // Height of the toolbar in normal state.
 const int kToolbarNormalHeight = 48;
 // Height of the expanded toolbar (buttons on multiple lines).
@@ -58,58 +27,37 @@
 };
 }
 
-@interface ReadingListViewController ()<ReadingListToolbarActions,
-                                        ReadingListToolbarHeightDelegate> {
-  // Toolbar with the actions.
-  ReadingListToolbar* _toolbar;
+@interface ReadingListViewController ()<
+    ReadingListToolbarActions,
+    ReadingListToolbarHeightDelegate,
+    ReadingListCollectionViewControllerAudience> {
   // This constraint control the expanded mode of the toolbar.
   NSLayoutConstraint* _expandedToolbarConstraint;
-  // Coordinator for the alert displayed when the user long presses an entry.
-  AlertCoordinator* _alertCoordinator;
 }
 
-// UrlLoader for navigating to entries.
-@property(nonatomic, weak, readonly) id<UrlLoader> URLLoader;
 @property(nonatomic, strong, readonly)
     ReadingListCollectionViewController* readingListCollectionViewController;
-
-// Closes the ReadingList view.
-- (void)dismiss;
-// Opens |URL| in a new tab |incognito| or not.
-- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito;
-// Opens the offline url |offlineURL| of the entry saved in the reading list
-// model with the |entryURL| url.
-- (void)openOfflineURL:(const GURL&)offlineURL
-                      correspondingEntryURL:(const GURL&)entryURL
-    fromReadingListCollectionViewController:
-        (ReadingListCollectionViewController*)
-            readingListCollectionViewController;
+@property(nonatomic, strong, readonly) ReadingListToolbar* toolbar;
 
 @end
 
 @implementation ReadingListViewController
 
+@synthesize delegate = _delegate;
 @synthesize readingListCollectionViewController =
     _readingListCollectionViewController;
-@synthesize URLLoader = _URLLoader;
+@synthesize toolbar = _toolbar;
 
-- (instancetype)initWithModel:(ReadingListModel*)model
-                        loader:(id<UrlLoader>)loader
-              largeIconService:(favicon::LargeIconService*)largeIconService
-    readingListDownloadService:
-        (ReadingListDownloadService*)readingListDownloadService {
+- (instancetype)initWithCollectionViewController:
+                    (ReadingListCollectionViewController*)
+                        collectionViewController
+                                         toolbar:(ReadingListToolbar*)toolbar {
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
-    _URLLoader = loader;
-    _toolbar = [[ReadingListToolbar alloc] initWithFrame:CGRectZero];
-    _toolbar.heightDelegate = self;
-    _readingListCollectionViewController =
-        [[ReadingListCollectionViewController alloc]
-                         initWithModel:model
-                      largeIconService:largeIconService
-            readingListDownloadService:readingListDownloadService
-                               toolbar:_toolbar];
-    _readingListCollectionViewController.delegate = self;
+    _toolbar = toolbar;
+    toolbar.heightDelegate = self;
+    _readingListCollectionViewController = collectionViewController;
+    collectionViewController.audience = self;
 
     // Configure modal presentation.
     [self setModalPresentationStyle:UIModalPresentationFormSheet];
@@ -148,160 +96,11 @@
 #pragma mark UIAccessibilityAction
 
 - (BOOL)accessibilityPerformEscape {
-  [self dismiss];
+  [self.delegate dismissReadingListCollectionViewController:
+                     self.readingListCollectionViewController];
   return YES;
 }
 
-#pragma mark - ReadingListCollectionViewControllerDelegate
-
-- (void)readingListCollectionViewController:
-            (ReadingListCollectionViewController*)
-                readingListCollectionViewController
-                                   hasItems:(BOOL)hasItems {
-  if (hasItems) {
-    // If there are items, add the toolbar.
-    [self.view addSubview:_toolbar];
-    NSDictionary* views = @{
-      @"toolbar" : _toolbar,
-      @"collection" : readingListCollectionViewController.view
-    };
-    NSArray* constraints = @[ @"V:[collection][toolbar]|", @"H:|[toolbar]|" ];
-    ApplyVisualConstraints(constraints, views);
-    NSLayoutConstraint* height =
-        [_toolbar.heightAnchor constraintEqualToConstant:kToolbarNormalHeight];
-    height.priority = LayoutPriorityHigh;
-    height.active = YES;
-    // When the toolbar is added, the only button is the "edit" button. No need
-    // to go in expanded mode.
-    _expandedToolbarConstraint = [_toolbar.heightAnchor
-        constraintEqualToConstant:kToolbarExpandedHeight];
-  } else {
-    // If there is no item, remove the toolbar. The constraints will make sure
-    // the collection takes the whole view.
-    [_toolbar removeFromSuperview];
-  }
-}
-
-- (void)dismissReadingListCollectionViewController:
-    (ReadingListCollectionViewController*)readingListCollectionViewController {
-  [readingListCollectionViewController willBeDismissed];
-  [self dismiss];
-}
-
-- (void)readingListCollectionViewController:
-            (ReadingListCollectionViewController*)
-                readingListCollectionViewController
-                  displayContextMenuForItem:
-                      (ReadingListCollectionViewItem*)readingListItem
-                                    atPoint:(CGPoint)menuLocation {
-  const ReadingListEntry* entry =
-      readingListCollectionViewController.readingListModel->GetEntryByURL(
-          readingListItem.url);
-
-  if (!entry) {
-    [readingListCollectionViewController reloadData];
-    return;
-  }
-  const GURL entryURL = entry->URL();
-
-  __weak ReadingListViewController* weakSelf = self;
-
-  _alertCoordinator = [[ActionSheetCoordinator alloc]
-      initWithBaseViewController:self
-                           title:readingListItem.text
-                         message:readingListItem.detailText
-                            rect:CGRectMake(menuLocation.x, menuLocation.y, 0,
-                                            0)
-                            view:readingListCollectionViewController
-                                     .collectionView];
-
-  NSString* openInNewTabTitle =
-      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB);
-  [_alertCoordinator
-      addItemWithTitle:openInNewTabTitle
-                action:^{
-                  [weakSelf openNewTabWithURL:entryURL incognito:NO];
-                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", NEW_TAB,
-                                            ENUM_MAX);
-
-                }
-                 style:UIAlertActionStyleDefault];
-
-  NSString* openInNewTabIncognitoTitle =
-      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB);
-  [_alertCoordinator
-      addItemWithTitle:openInNewTabIncognitoTitle
-                action:^{
-                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
-                                            NEW_INCOGNITO_TAB, ENUM_MAX);
-                  [weakSelf openNewTabWithURL:entryURL incognito:YES];
-                }
-                 style:UIAlertActionStyleDefault];
-
-  NSString* copyLinkTitle =
-      l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPY);
-  [_alertCoordinator
-      addItemWithTitle:copyLinkTitle
-                action:^{
-                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
-                                            COPY_LINK, ENUM_MAX);
-                  StoreURLInPasteboard(entryURL);
-                }
-                 style:UIAlertActionStyleDefault];
-
-  if (entry->DistilledState() == ReadingListEntry::PROCESSED) {
-    GURL offlineURL = reading_list::OfflineURLForPath(
-        entry->DistilledPath(), entryURL, entry->DistilledURL());
-    NSString* viewOfflineVersionTitle =
-        l10n_util::GetNSString(IDS_IOS_READING_LIST_CONTENT_CONTEXT_OFFLINE);
-    [_alertCoordinator
-        addItemWithTitle:viewOfflineVersionTitle
-                  action:^{
-                    UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu",
-                                              VIEW_OFFLINE, ENUM_MAX);
-                    [weakSelf openOfflineURL:offlineURL
-                                          correspondingEntryURL:entryURL
-                        fromReadingListCollectionViewController:
-                            readingListCollectionViewController];
-                  }
-                   style:UIAlertActionStyleDefault];
-  }
-
-  [_alertCoordinator
-      addItemWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)
-                action:^{
-                  UMA_HISTOGRAM_ENUMERATION("ReadingList.ContextMenu", CANCEL,
-                                            ENUM_MAX);
-                }
-                 style:UIAlertActionStyleCancel];
-
-  [_alertCoordinator start];
-}
-
-- (void)
-readingListCollectionViewController:
-    (ReadingListCollectionViewController*)readingListCollectionViewController
-                           openItem:
-                               (ReadingListCollectionViewItem*)readingListItem {
-  const ReadingListEntry* entry =
-      readingListCollectionViewController.readingListModel->GetEntryByURL(
-          readingListItem.url);
-
-  if (!entry) {
-    [readingListCollectionViewController reloadData];
-    return;
-  }
-
-  base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
-
-  [self.URLLoader loadURL:entry->URL()
-                 referrer:web::Referrer()
-               transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
-        rendererInitiated:NO];
-
-  [self dismiss];
-}
-
 #pragma mark - ReadingListToolbarActionTarget
 
 - (void)markPressed {
@@ -320,6 +119,33 @@
   [self.readingListCollectionViewController exitEditingModePressed];
 }
 
+#pragma mark - ReadingListCollectionViewControllerAudience
+
+- (void)readingListHasItems:(BOOL)hasItems {
+  if (hasItems) {
+    // If there are items, add the toolbar.
+    [self.view addSubview:_toolbar];
+    NSDictionary* views = @{
+      @"toolbar" : _toolbar,
+      @"collection" : self.readingListCollectionViewController.view
+    };
+    NSArray* constraints = @[ @"V:[collection][toolbar]|", @"H:|[toolbar]|" ];
+    ApplyVisualConstraints(constraints, views);
+    NSLayoutConstraint* height =
+        [_toolbar.heightAnchor constraintEqualToConstant:kToolbarNormalHeight];
+    height.priority = LayoutPriorityHigh;
+    height.active = YES;
+    // When the toolbar is added, the only button is the "edit" button. No need
+    // to go in expanded mode.
+    _expandedToolbarConstraint = [_toolbar.heightAnchor
+        constraintEqualToConstant:kToolbarExpandedHeight];
+  } else {
+    // If there is no item, remove the toolbar. The constraints will make sure
+    // the collection takes the whole view.
+    [_toolbar removeFromSuperview];
+  }
+}
+
 #pragma mark - ReadingListToolbarHeightDelegate
 
 - (void)toolbar:(id)toolbar onHeightChanged:(ReadingListToolbarHeight)height {
@@ -336,51 +162,19 @@
   });
 }
 
-#pragma mark - Private
-
-- (void)dismiss {
-  [self.presentingViewController dismissViewControllerAnimated:YES
-                                                    completion:nil];
-}
-
-- (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito {
-  base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
-
-  [self.URLLoader webPageOrderedOpen:URL
-                            referrer:web::Referrer()
-                          windowName:nil
-                         inIncognito:incognito
-                        inBackground:NO
-                            appendTo:kLastTab];
-
-  [self dismiss];
-}
-
-- (void)openOfflineURL:(const GURL&)offlineURL
-                      correspondingEntryURL:(const GURL&)entryURL
-    fromReadingListCollectionViewController:
-        (ReadingListCollectionViewController*)
-            readingListCollectionViewController {
-  [readingListCollectionViewController willBeDismissed];
-
-  [self openNewTabWithURL:offlineURL incognito:NO];
-
-  UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
-  const GURL updateURL = entryURL;
-  readingListCollectionViewController.readingListModel->SetReadStatus(updateURL,
-                                                                      true);
-}
-
 #pragma mark - UIResponder
 
 - (NSArray*)keyCommands {
   __weak ReadingListViewController* weakSelf = self;
-  return @[ [UIKeyCommand cr_keyCommandWithInput:UIKeyInputEscape
-                                   modifierFlags:Cr_UIKeyModifierNone
-                                           title:nil
-                                          action:^{
-                                            [weakSelf dismiss];
-                                          }] ];
+  return @[ [UIKeyCommand
+      cr_keyCommandWithInput:UIKeyInputEscape
+               modifierFlags:Cr_UIKeyModifierNone
+                       title:nil
+                      action:^{
+                        [weakSelf.delegate
+                            dismissReadingListCollectionViewController:
+                                weakSelf.readingListCollectionViewController];
+                      }] ];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.h b/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.h
deleted file mode 100644
index bc33b6a..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_VIEW_CONTROLLER_BUILDER_H_
-#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_VIEW_CONTROLLER_BUILDER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
-
-@class TabModel;
-@protocol UrlLoader;
-
-@protocol ReadingListViewControllerDelegate;
-
-// A builder class that constructs ReadingListViewControllers.
-@interface ReadingListViewControllerBuilder : NSObject
-
-+ (ReadingListViewController*)
-readingListViewControllerInBrowserState:(ios::ChromeBrowserState*)browserState
-                                 loader:(id<UrlLoader>)loader;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_VIEW_CONTROLLER_BUILDER_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.mm
deleted file mode 100644
index 1c0df38..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.mm
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_view_controller_builder.h"
-
-#include "components/prefs/pref_service.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
-#include "ios/chrome/browser/pref_names.h"
-#include "ios/chrome/browser/reading_list/reading_list_download_service.h"
-#include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation ReadingListViewControllerBuilder
-
-+ (ReadingListViewController*)
-readingListViewControllerInBrowserState:(ios::ChromeBrowserState*)browserState
-                                 loader:(id<UrlLoader>)loader {
-  ReadingListModel* model =
-      ReadingListModelFactory::GetInstance()->GetForBrowserState(browserState);
-  favicon::LargeIconService* service =
-      IOSChromeLargeIconServiceFactory::GetForBrowserState(browserState);
-  ReadingListDownloadService* rlservice =
-      ReadingListDownloadServiceFactory::GetInstance()->GetForBrowserState(
-          browserState);
-  ReadingListViewController* vc =
-      [[ReadingListViewController alloc] initWithModel:model
-                                                loader:loader
-                                      largeIconService:service
-                            readingListDownloadService:rlservice];
-  return vc;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/util/BUILD.gn b/ios/chrome/browser/ui/util/BUILD.gn
index 6c810dc..09d47cbb 100644
--- a/ios/chrome/browser/ui/util/BUILD.gn
+++ b/ios/chrome/browser/ui/util/BUILD.gn
@@ -8,6 +8,8 @@
     "CRUILabel+AttributeUtils.mm",
     "core_text_util.h",
     "core_text_util.mm",
+    "i18n_string.h",
+    "i18n_string.mm",
     "label_link_controller.h",
     "label_link_controller.mm",
     "label_observer.h",
diff --git a/ios/chrome/browser/ui/util/i18n_string.h b/ios/chrome/browser/ui/util/i18n_string.h
new file mode 100644
index 0000000..6cd734a
--- /dev/null
+++ b/ios/chrome/browser/ui/util/i18n_string.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_UTIL_I18N_STRING_H_
+#define IOS_CHROME_BROWSER_UI_UTIL_I18N_STRING_H_
+
+#import <Foundation/Foundation.h>
+
+// Wrapper for base::i18n::AdjustStringForLocaleDirection.
+// Given the string in |text|, this function returns a new string with
+// the appropriate Unicode formatting marks that mark the string direction
+// (either left-to-right or right-to-left).
+NSString* AdjustStringForLocaleDirection(NSString* text);
+
+#endif  // IOS_CHROME_BROWSER_UI_UTIL_I18N_STRING_H_
diff --git a/ios/chrome/browser/ui/util/i18n_string.mm b/ios/chrome/browser/ui/util/i18n_string.mm
new file mode 100644
index 0000000..5242053c0
--- /dev/null
+++ b/ios/chrome/browser/ui/util/i18n_string.mm
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/util/i18n_string.h"
+
+#include "base/i18n/rtl.h"
+#include "base/strings/string16.h"
+#include "base/strings/sys_string_conversions.h"
+
+NSString* AdjustStringForLocaleDirection(NSString* text) {
+  base::string16 converted_text = base::SysNSStringToUTF16(text);
+  bool has_changed =
+      base::i18n::AdjustStringForLocaleDirection(&converted_text);
+  if (has_changed) {
+    return base::SysUTF16ToNSString(converted_text);
+  }
+  return text;
+}
diff --git a/ios/chrome/common/app_group/app_group_constants.h b/ios/chrome/common/app_group/app_group_constants.h
index 483b66bb..445e86d 100644
--- a/ios/chrome/common/app_group/app_group_constants.h
+++ b/ios/chrome/common/app_group/app_group_constants.h
@@ -52,6 +52,9 @@
 // The command to open a new tab.
 extern const char kChromeAppGroupNewTabCommand[];
 
+// The command to focus the omnibox.
+extern const char kChromeAppGroupFocusOmniboxCommand[];
+
 // The key in kChromeAppGroupCommandPreference containing a NSDate at which
 // |kChromeAppGroupCommandAppPreference| issued the command.
 extern const char kChromeAppGroupCommandTimePreference[];
diff --git a/ios/chrome/common/app_group/app_group_constants.mm b/ios/chrome/common/app_group/app_group_constants.mm
index f080bb9f..926db07 100644
--- a/ios/chrome/common/app_group/app_group_constants.mm
+++ b/ios/chrome/common/app_group/app_group_constants.mm
@@ -31,6 +31,7 @@
 const char kChromeAppGroupOpenURLCommand[] = "openurl";
 const char kChromeAppGroupVoiceSearchCommand[] = "voicesearch";
 const char kChromeAppGroupNewTabCommand[] = "newtab";
+const char kChromeAppGroupFocusOmniboxCommand[] = "focusomnibox";
 
 const char kChromeAppClientID[] = "ClientID";
 const char kUserMetricsEnabledDate[] = "UserMetricsEnabledDate";
diff --git a/ios/chrome/widget_extension/BUILD.gn b/ios/chrome/widget_extension/BUILD.gn
index 1316fa9..6b020507 100644
--- a/ios/chrome/widget_extension/BUILD.gn
+++ b/ios/chrome/widget_extension/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/ios/rules.gni")
+import("//build/config/mac/base_rules.gni")
 import("//build/mac/tweak_info_plist.gni")
 import("//ios/build/chrome_build.gni")
 
@@ -27,6 +28,23 @@
     "widget_view_controller.mm",
   ]
 
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//components/prefs",
+    "//components/variations",
+    "//components/version_info",
+    "//ios/chrome/common",
+    "//ios/chrome/common/app_group",
+    "//ios/chrome/common/app_group:client",
+    "//ios/chrome/common/physical_web",
+    "//ios/chrome/today_extension/strings",
+    "//ios/third_party/material_components_ios",
+    "//net",
+    "//ui/base",
+    "//url",
+  ]
+
   libs = [
     "Foundation.framework",
     "NotificationCenter.framework",
diff --git a/ios/chrome/widget_extension/widget_view.h b/ios/chrome/widget_extension/widget_view.h
index a13f35ee..9e2ff38 100644
--- a/ios/chrome/widget_extension/widget_view.h
+++ b/ios/chrome/widget_extension/widget_view.h
@@ -7,9 +7,23 @@
 
 #import <UIKit/UIKit.h>
 
+// Protocol to be implemented by targets for user actions coming from the widget
+// view.
+@protocol WidgetViewActionTarget
+
+// Called when the user taps the fake omnibox.
+- (void)openApp:(id)sender;
+
+@end
+
+// View for the widget. Shows a blinking cursor for a fake omnibox and calls the
+// target when tapped.
 @interface WidgetView : UIView
 
-- (instancetype)init NS_DESIGNATED_INITIALIZER;
+// Designated initializer, creates the widget view with a |target| for user
+// actions.
+- (instancetype)initWithActionTarget:(id<WidgetViewActionTarget>)target
+    NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
 
diff --git a/ios/chrome/widget_extension/widget_view.mm b/ios/chrome/widget_extension/widget_view.mm
index d2c5a77e..d746fd0 100644
--- a/ios/chrome/widget_extension/widget_view.mm
+++ b/ios/chrome/widget_extension/widget_view.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/widget_extension/widget_view.h"
 
+#include "base/logging.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -12,16 +14,22 @@
 
 const CGFloat kCursorHeight = 40;
 const CGFloat kCursorWidth = 2;
-const CGFloat kCursorHorizontalPadding = 40;
+const CGFloat kCursorHorizontalPadding = 10;
+const CGFloat kCursorVerticalPadding = 10;
+const CGFloat kFakeboxHorizontalPadding = 40;
+const CGFloat kFakeboxVerticalPadding = 40;
 
 }  // namespace
 
-@interface WidgetView ()
+@interface WidgetView () {
+  __weak id<WidgetViewActionTarget> _target;
+}
 
 @property(nonatomic, weak) UIView* cursor;
 
-// Creates a cursor, adds it to the view and sets the class cursor property.
-- (void)addCursor;
+// Creates and adds a fake omnibox with blinking cursor to the view and sets the
+// class cursor property.
+- (void)addFakebox;
 
 @end
 
@@ -29,28 +37,50 @@
 
 @synthesize cursor = _cursor;
 
-- (instancetype)init {
+- (instancetype)initWithActionTarget:(id<WidgetViewActionTarget>)target {
   self = [super initWithFrame:CGRectZero];
   if (self) {
-    [self addCursor];
+    DCHECK(target);
+    _target = target;
+    [self addFakebox];
   }
   return self;
 }
 
-- (void)addCursor {
+- (void)addFakebox {
+  UIView* fakebox = [[UIView alloc] initWithFrame:CGRectZero];
+
+  UIGestureRecognizer* tapRecognizer =
+      [[UITapGestureRecognizer alloc] initWithTarget:_target
+                                              action:@selector(openApp:)];
+
+  [fakebox addGestureRecognizer:tapRecognizer];
+  [self addSubview:fakebox];
+
   UIView* cursor = [[UIView alloc] initWithFrame:CGRectZero];
   self.cursor = cursor;
   self.cursor.backgroundColor = [UIColor blueColor];
-  [self addSubview:self.cursor];
+  [fakebox addSubview:self.cursor];
 
+  [fakebox setTranslatesAutoresizingMaskIntoConstraints:NO];
   [self.cursor setTranslatesAutoresizingMaskIntoConstraints:NO];
   [NSLayoutConstraint activateConstraints:@[
+    [[fakebox leadingAnchor] constraintEqualToAnchor:self.leadingAnchor
+                                            constant:kFakeboxHorizontalPadding],
+    [[fakebox trailingAnchor]
+        constraintEqualToAnchor:self.trailingAnchor
+                       constant:-kFakeboxHorizontalPadding],
+    [[fakebox topAnchor] constraintEqualToAnchor:self.topAnchor
+                                        constant:kFakeboxVerticalPadding],
+    [[fakebox heightAnchor]
+        constraintEqualToConstant:kCursorHeight + 2 * kCursorVerticalPadding],
+
     [[self.cursor widthAnchor] constraintEqualToConstant:kCursorWidth],
     [[self.cursor leadingAnchor]
-        constraintEqualToAnchor:self.leadingAnchor
+        constraintEqualToAnchor:fakebox.leadingAnchor
                        constant:kCursorHorizontalPadding],
     [[self.cursor heightAnchor] constraintEqualToConstant:kCursorHeight],
-    [[self.cursor centerYAnchor] constraintEqualToAnchor:self.centerYAnchor]
+    [[self.cursor centerYAnchor] constraintEqualToAnchor:fakebox.centerYAnchor]
   ]];
 
   [UIView animateWithDuration:0.3
diff --git a/ios/chrome/widget_extension/widget_view_controller.mm b/ios/chrome/widget_extension/widget_view_controller.mm
index 735d49e..aa93999c 100644
--- a/ios/chrome/widget_extension/widget_view_controller.mm
+++ b/ios/chrome/widget_extension/widget_view_controller.mm
@@ -6,13 +6,19 @@
 
 #import <NotificationCenter/NotificationCenter.h>
 
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "ios/chrome/common/app_group/app_group_constants.h"
+#include "ios/chrome/common/x_callback_url.h"
 #import "ios/chrome/widget_extension/widget_view.h"
+#import "net/base/mac/url_conversions.h"
+#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface WidgetViewController ()
+@interface WidgetViewController ()<WidgetViewActionTarget>
 @property(nonatomic, weak) WidgetView* widgetView;
 @end
 
@@ -27,7 +33,7 @@
 
   // A local variable is necessary here as the property is declared weak and the
   // object would be deallocated before being retained by the addSubview call.
-  WidgetView* widgetView = [[WidgetView alloc] init];
+  WidgetView* widgetView = [[WidgetView alloc] initWithActionTarget:self];
   self.widgetView = widgetView;
   [self.view addSubview:self.widgetView];
 
@@ -44,4 +50,40 @@
   ]];
 }
 
+- (void)openApp:(id)sender {
+  NSUserDefaults* sharedDefaults =
+      [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()];
+  NSString* defaultsKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference);
+  [sharedDefaults setObject:[WidgetViewController commandDict]
+                     forKey:defaultsKey];
+  [sharedDefaults synchronize];
+
+  NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle]
+      objectForInfoDictionaryKey:@"KSChannelChromeScheme"]);
+  if (!scheme)
+    return;
+  const GURL openURL =
+      CreateXCallbackURL(base::SysNSStringToUTF8(scheme),
+                         app_group::kChromeAppGroupXCallbackCommand);
+  [self.extensionContext openURL:net::NSURLWithGURL(openURL)
+               completionHandler:nil];
+}
+
++ (NSDictionary*)commandDict {
+  NSString* command =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupFocusOmniboxCommand);
+  NSString* timePrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference);
+  NSString* appPrefKey =
+      base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference);
+  NSString* commandPrefKey = base::SysUTF8ToNSString(
+      app_group::kChromeAppGroupCommandCommandPreference);
+  return @{
+    timePrefKey : [NSDate date],
+    appPrefKey : @"TodayExtension",
+    commandPrefKey : command,
+  };
+}
+
 @end
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 31b114f..74f72b0 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1848,10 +1848,8 @@
 # were matched on pass/fail but not exact error messages.
 crbug.com/688854 external/wpt/hr-time/window-worker-time-origin.html [ Failure ]
 
-# 2017-02-14: These directories were just imported but expectations and baselines haven't been set yet.
-crbug.com/692105 external/wpt/mixed-content [ Skip ]
-crbug.com/692105 external/wpt/content-security-policy [ Skip ]
 # 2017-02-17: These directories were just imported but expectations and baselines haven't been set yet.
+crbug.com/692105 external/wpt/content-security-policy [ Skip ]
 crbug.com/692105 external/wpt/domxpath/interfaces.html [ Skip ]
 crbug.com/692105 external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html [ Skip ]
 crbug.com/692105 external/wpt/html/browsers/history/the-location-interface/location-valueof.html [ Skip ]
@@ -1882,6 +1880,45 @@
 crbug.com/627968 external/wpt/referrer-policy/strict-origin/ [ Skip ]
 crbug.com/627968 external/wpt/referrer-policy/strict-origin-when-cross-origin/ [ Skip ]
 
+# These wpt/mixed-content tests have console errors that are different on different test runs.
+crbug.com/679742 external/wpt/mixed-content/allowed/http-csp/same-host-wss/websocket-request/top-level/keep-scheme-redirect/websocket-allowed.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/allowed/meta-csp/same-host-wss/websocket-request/top-level/no-redirect/websocket-allowed.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/allowed/no-opt-in/same-host-wss/websocket-request/top-level/keep-scheme-redirect/websocket-allowed.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/allowed/no-opt-in/same-host-wss/websocket-request/top-level/no-redirect/websocket-allowed.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/allowed/http-csp/same-host-wss/websocket-request/top-level/no-redirect/websocket-allowed.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html [ Failure ]
+crbug.com/679742 external/wpt/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html [ Failure ]
+
+# These wpt/mixed-content tests consistently time out.
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/form-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/form-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/form-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/iframe-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/iframe-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/cross-origin-http/iframe-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/form-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/form-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/form-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/iframe-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/iframe-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/http-csp/same-host-http/iframe-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/meta-csp/cross-origin-http/form-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/meta-csp/cross-origin-http/iframe-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/meta-csp/same-host-http/form-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/meta-csp/same-host-http/iframe-tag/top-level/no-redirect/opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/form-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/form-tag/top-level/no-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/form-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/iframe-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/iframe-tag/top-level/no-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/cross-origin-http/iframe-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/form-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/form-tag/top-level/no-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/form-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/iframe-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/iframe-tag/top-level/no-redirect/no-opt-in-blocks.https.html [ Timeout ]
+crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/iframe-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
+
 # ====== New tests from w3c-test-autoroller added here ======
 crbug.com/626703 external/wpt/html/webappapis/idle-callbacks/cancel-invoked.html [ Timeout ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_completely_move_up.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/editing/selection/extend-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/extend-expected.txt
index 83532dc4..0aec32e 100644
--- a/third_party/WebKit/LayoutTests/editing/selection/extend-expected.txt
+++ b/third_party/WebKit/LayoutTests/editing/selection/extend-expected.txt
@@ -1,7 +1,7 @@
 foobarbaz
 Success: window.getSelection() is ar.
-Success: s.extend(span2.firstChild, 4) raised IndexSizeError: Failed to execute 'extend' on 'Selection': The offset 4 is larger than or equal to the node's length (3)..
-Success: s.extend(span2.firstChild, -1) raised IndexSizeError: Failed to execute 'extend' on 'Selection': The offset -1 is larger than or equal to the node's length (3)..
+Success: s.extend(span2.firstChild, 4) raised IndexSizeError: Failed to execute 'extend' on 'Selection': The offset 4 is larger than the node's length (3)..
+Success: s.extend(span2.firstChild, -1) raised IndexSizeError: Failed to execute 'extend' on 'Selection': The offset 4294967295 is larger than the node's length (3)..
 Success: window.getSelection() is b.
 Success: window.getSelection() is arbaz.
 Success: s.extend() raised TypeError: Failed to execute 'extend' on 'Selection': 1 argument required, but only 0 present..
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..3f9d961
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: http-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..df64316
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: http-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..f9885bec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: http-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..914f1b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/http-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: http-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..5cc06fce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: meta-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..dab56d99
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/meta-csp/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: meta-csp
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..73233aebc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..a1235887
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/audio-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..8ef5634
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/keep-scheme-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
new file mode 100644
index 0000000..228c938f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/allowed/no-opt-in/same-host-https/video-tag/top-level/no-redirect/allowed.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-https
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..d98a36f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..fcf141f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..35a25232
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: swap-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..68e4c5f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..85532b4b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..2bca83be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: cross-origin-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: swap-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..8cd8c2d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..24dea3b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..86dd1ff2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: swap-scheme-redirect
+                                 subresource: audio-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..eff10c8f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: keep-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..dfb612b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: no-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
new file mode 100644
index 0000000..6866856
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL opt_in_method: no-opt-in
+                                 origin: same-host-http
+                                 source_scheme: https
+                                 context_nesting: top-level
+                                 redirection: swap-scheme-redirect
+                                 subresource: video-tag
+                                 expectation: allowed assert_equals: The triggered event should match 'allowed'. expected "allowed" but got "blocked"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/fast/text/selection-exceptions-expected.txt b/third_party/WebKit/LayoutTests/fast/text/selection-exceptions-expected.txt
index 6ed111c..dd9f853 100644
--- a/third_party/WebKit/LayoutTests/fast/text/selection-exceptions-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/text/selection-exceptions-expected.txt
@@ -3,15 +3,15 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS getSelection().collapse(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'collapse' on 'Selection': There is no child at offset -1..
+PASS getSelection().collapse(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'collapse' on 'Selection': There is no child at offset 4294967295..
 PASS getSelection().collapseToStart() threw exception InvalidStateError: Failed to execute 'collapseToStart' on 'Selection': there is no selection..
 PASS getSelection().collapseToEnd() threw exception InvalidStateError: Failed to execute 'collapseToEnd' on 'Selection': there is no selection..
-PASS getSelection().setBaseAndExtent(document.documentElement, -1, document.documentElement, 0) threw exception IndexSizeError: Failed to execute 'setBaseAndExtent' on 'Selection': There is no child at offset -1..
-PASS getSelection().setBaseAndExtent(document.documentElement, 0, document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'setBaseAndExtent' on 'Selection': There is no child at offset -1..
-PASS getSelection().collapse(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'collapse' on 'Selection': There is no child at offset -1..
+PASS getSelection().setBaseAndExtent(document.documentElement, -1, document.documentElement, 0) threw exception IndexSizeError: Failed to execute 'setBaseAndExtent' on 'Selection': There is no child at offset 4294967295..
+PASS getSelection().setBaseAndExtent(document.documentElement, 0, document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'setBaseAndExtent' on 'Selection': There is no child at offset 4294967295..
+PASS getSelection().collapse(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'collapse' on 'Selection': There is no child at offset 4294967295..
 PASS getSelection().getRangeAt(-1) threw exception IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': -1 is not a valid index..
 PASS getSelection().extend(0, -1) threw exception TypeError: Failed to execute 'extend' on 'Selection': parameter 1 is not of type 'Node'..
-PASS getSelection().extend(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'extend' on 'Selection': There is no child at offset -1..
+PASS getSelection().extend(document.documentElement, -1) threw exception IndexSizeError: Failed to execute 'extend' on 'Selection': There is no child at offset 4294967295..
 PASS getSelection().extend(document.documentElement, 1000) threw exception IndexSizeError: Failed to execute 'extend' on 'Selection': There is no child at offset 1000..
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/fast/text/selection-exceptions.html b/third_party/WebKit/LayoutTests/fast/text/selection-exceptions.html
index d2bf0bf..51df485 100644
--- a/third_party/WebKit/LayoutTests/fast/text/selection-exceptions.html
+++ b/third_party/WebKit/LayoutTests/fast/text/selection-exceptions.html
@@ -7,22 +7,22 @@
     <script>
         description("This tests that 'Selection' methods throw exceptions with reasonable messages.");
 
-        shouldThrow('getSelection().collapse(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'collapse\' on \'Selection\': There is no child at offset -1."');
+        shouldThrow('getSelection().collapse(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'collapse\' on \'Selection\': There is no child at offset 4294967295."');
 
         getSelection().empty();
         shouldThrow('getSelection().collapseToStart()', '"InvalidStateError: Failed to execute \'collapseToStart\' on \'Selection\': there is no selection."');
         shouldThrow('getSelection().collapseToEnd()', '"InvalidStateError: Failed to execute \'collapseToEnd\' on \'Selection\': there is no selection."');
 
-        shouldThrow('getSelection().setBaseAndExtent(document.documentElement, -1, document.documentElement, 0)', '"IndexSizeError: Failed to execute \'setBaseAndExtent\' on \'Selection\': There is no child at offset -1."');
-        shouldThrow('getSelection().setBaseAndExtent(document.documentElement, 0, document.documentElement, -1)', '"IndexSizeError: Failed to execute \'setBaseAndExtent\' on \'Selection\': There is no child at offset -1."');
+        shouldThrow('getSelection().setBaseAndExtent(document.documentElement, -1, document.documentElement, 0)', '"IndexSizeError: Failed to execute \'setBaseAndExtent\' on \'Selection\': There is no child at offset 4294967295."');
+        shouldThrow('getSelection().setBaseAndExtent(document.documentElement, 0, document.documentElement, -1)', '"IndexSizeError: Failed to execute \'setBaseAndExtent\' on \'Selection\': There is no child at offset 4294967295."');
 
-        shouldThrow('getSelection().collapse(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'collapse\' on \'Selection\': There is no child at offset -1."');
+        shouldThrow('getSelection().collapse(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'collapse\' on \'Selection\': There is no child at offset 4294967295."');
 
         shouldThrow('getSelection().getRangeAt(-1)', '"IndexSizeError: Failed to execute \'getRangeAt\' on \'Selection\': -1 is not a valid index."');
 
         shouldThrow('getSelection().extend(0, -1)', '"TypeError: Failed to execute \'extend\' on \'Selection\': parameter 1 is not of type \'Node\'."');
         getSelection().addRange(document.createRange());
-        shouldThrow('getSelection().extend(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'extend\' on \'Selection\': There is no child at offset -1."');
+        shouldThrow('getSelection().extend(document.documentElement, -1)', '"IndexSizeError: Failed to execute \'extend\' on \'Selection\': There is no child at offset 4294967295."');
         shouldThrow('getSelection().extend(document.documentElement, 1000)', '"IndexSizeError: Failed to execute \'extend\' on \'Selection\': There is no child at offset 1000."');
     </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/vr/getVRDisplays_detached.html b/third_party/WebKit/LayoutTests/vr/getVRDisplays_detached.html
new file mode 100644
index 0000000..b3d7079a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/vr/getVRDisplays_detached.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Detached use of navigator.getVRDisplays()</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+</head>
+<body>
+<iframe sandbox="allow-same-origin" id="subframe"></iframe>
+<script>
+test(() => {
+    var nav = window.frames[0].navigator;
+    document.getElementById("subframe").remove();
+    // A promise won't be rejected when in a detached context,
+    // so the undefined value is returned.
+    assert_equals(nav.getVRDisplays(), undefined);
+});
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/Source/core/dom/BUILD.gn b/third_party/WebKit/Source/core/dom/BUILD.gn
index d9183b9f..09ff510 100644
--- a/third_party/WebKit/Source/core/dom/BUILD.gn
+++ b/third_party/WebKit/Source/core/dom/BUILD.gn
@@ -261,6 +261,8 @@
     "ShadowTreeStyleSheetCollection.cpp",
     "ShadowTreeStyleSheetCollection.h",
     "SimulatedClickOptions.h",
+    "SinkDocument.cpp",
+    "SinkDocument.h",
     "SpaceSplitString.cpp",
     "StaticNodeList.h",
     "StaticRange.cpp",
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 4b14631..03583eda 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -2693,7 +2693,10 @@
 
     // When focusing an editable element in an iframe, don't reset the selection
     // if it already contains a selection.
-    if (this == frame->selection().rootEditableElement())
+    if (this ==
+        frame->selection()
+            .computeVisibleSelectionInDOMTreeDeprecated()
+            .rootEditableElement())
       return;
 
     // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
diff --git a/third_party/WebKit/Source/core/dom/NodeWithIndex.h b/third_party/WebKit/Source/core/dom/NodeWithIndex.h
index 9d8fc49..aa74968 100644
--- a/third_party/WebKit/Source/core/dom/NodeWithIndex.h
+++ b/third_party/WebKit/Source/core/dom/NodeWithIndex.h
@@ -41,6 +41,7 @@
 
   Node& node() const { return *m_node; }
 
+  // TODO(tkent): Should be unsigned.
   int index() const {
     if (!hasIndex())
       m_index = node().nodeIndex();
diff --git a/third_party/WebKit/Source/core/dom/Range.cpp b/third_party/WebKit/Source/core/dom/Range.cpp
index 0805621..bd212e13 100644
--- a/third_party/WebKit/Source/core/dom/Range.cpp
+++ b/third_party/WebKit/Source/core/dom/Range.cpp
@@ -70,9 +70,9 @@
 
 inline Range::Range(Document& ownerDocument,
                     Node* startContainer,
-                    int startOffset,
+                    unsigned startOffset,
                     Node* endContainer,
-                    int endOffset)
+                    unsigned endOffset)
     : m_ownerDocument(&ownerDocument),
       m_start(m_ownerDocument),
       m_end(m_ownerDocument) {
@@ -86,9 +86,9 @@
 
 Range* Range::create(Document& ownerDocument,
                      Node* startContainer,
-                     int startOffset,
+                     unsigned startOffset,
                      Node* endContainer,
-                     int endOffset) {
+                     unsigned endOffset) {
   return new Range(ownerDocument, startContainer, startOffset, endContainer,
                    endOffset);
 }
@@ -163,7 +163,7 @@
 }
 
 void Range::setStart(Node* refNode,
-                     int offset,
+                     unsigned offset,
                      ExceptionState& exceptionState) {
   if (!refNode) {
     // FIXME: Generated bindings code never calls with null, and neither should
@@ -193,7 +193,9 @@
   updateSelectionIfAddedToSelection();
 }
 
-void Range::setEnd(Node* refNode, int offset, ExceptionState& exceptionState) {
+void Range::setEnd(Node* refNode,
+                   unsigned offset,
+                   ExceptionState& exceptionState) {
   if (!refNode) {
     // FIXME: Generated bindings code never calls with null, and neither should
     // other callers!
@@ -244,7 +246,7 @@
 
 bool Range::isNodeFullyContained(Node& node) const {
   ContainerNode* parentNode = node.parentNode();
-  int nodeIndex = node.nodeIndex();
+  unsigned nodeIndex = node.nodeIndex();
   return isPointInRange(
              parentNode, nodeIndex,
              IGNORE_EXCEPTION_FOR_TESTING)  // starts in the middle of this
@@ -268,7 +270,7 @@
 }
 
 bool Range::isPointInRange(Node* refNode,
-                           int offset,
+                           unsigned offset,
                            ExceptionState& exceptionState) const {
   if (!refNode) {
     // FIXME: Generated bindings code never calls with null, and neither should
@@ -292,7 +294,7 @@
 }
 
 short Range::comparePoint(Node* refNode,
-                          int offset,
+                          unsigned offset,
                           ExceptionState& exceptionState) const {
   // http://developer.mozilla.org/en/docs/DOM:range.comparePoint
   // This method returns -1, 0 or 1 depending on if the point described by the
@@ -379,9 +381,9 @@
 }
 
 short Range::compareBoundaryPoints(Node* containerA,
-                                   int offsetA,
+                                   unsigned offsetA,
                                    Node* containerB,
-                                   int offsetB,
+                                   unsigned offsetB,
                                    ExceptionState& exceptionState) {
   bool disconnected = false;
   short result = comparePositionsInDOMTree(containerA, offsetA, containerB,
@@ -957,13 +959,12 @@
     Node::NodeType type = n->getNodeType();
     if (type == Node::kTextNode || type == Node::kCdataSectionNode) {
       String data = toCharacterData(n)->data();
-      int length = data.length();
-      int start = (n == m_start.container())
-                      ? std::min(std::max(0, m_start.offset()), length)
-                      : 0;
-      int end = (n == m_end.container())
-                    ? std::min(std::max(start, m_end.offset()), length)
-                    : length;
+      unsigned length = data.length();
+      unsigned start =
+          (n == m_start.container()) ? std::min(m_start.offset(), length) : 0;
+      unsigned end = (n == m_end.container())
+                         ? std::min(std::max(start, m_end.offset()), length)
+                         : length;
       builder.append(data, start, end - start);
     }
   }
@@ -1025,7 +1026,7 @@
 }
 
 Node* Range::checkNodeWOffset(Node* n,
-                              int offset,
+                              unsigned offset,
                               ExceptionState& exceptionState) {
   switch (n->getNodeType()) {
     case Node::kDocumentTypeNode:
@@ -1036,22 +1037,23 @@
     case Node::kCdataSectionNode:
     case Node::kCommentNode:
     case Node::kTextNode:
-      if (static_cast<unsigned>(offset) > toCharacterData(n)->length())
+      if (offset > toCharacterData(n)->length()) {
         exceptionState.throwDOMException(
-            IndexSizeError,
-            "The offset " + String::number(offset) +
-                " is larger than or equal to the node's length (" +
-                String::number(toCharacterData(n)->length()) + ").");
+            IndexSizeError, "The offset " + String::number(offset) +
+                                " is larger than the node's length (" +
+                                String::number(toCharacterData(n)->length()) +
+                                ").");
+      }
       return nullptr;
     case Node::kProcessingInstructionNode:
-      if (static_cast<unsigned>(offset) >
-          toProcessingInstruction(n)->data().length())
+      if (offset > toProcessingInstruction(n)->data().length()) {
         exceptionState.throwDOMException(
             IndexSizeError,
             "The offset " + String::number(offset) +
-                " is larger than or equal to than the node's length (" +
+                " is larger than the node's length (" +
                 String::number(toProcessingInstruction(n)->data().length()) +
                 ").");
+      }
       return nullptr;
     case Node::kAttributeNode:
     case Node::kDocumentFragmentNode:
@@ -1060,10 +1062,11 @@
       if (!offset)
         return nullptr;
       Node* childBefore = NodeTraversal::childAt(*n, offset - 1);
-      if (!childBefore)
+      if (!childBefore) {
         exceptionState.throwDOMException(
             IndexSizeError,
             "There is no child at offset " + String::number(offset) + ".");
+      }
       return childBefore;
     }
   }
@@ -1424,9 +1427,10 @@
     if (!r || !r->isText())
       continue;
     LayoutText* layoutText = toLayoutText(r);
-    int startOffset = node == startContainer ? m_start.offset() : 0;
-    int endOffset =
-        node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
+    unsigned startOffset = node == startContainer ? m_start.offset() : 0;
+    unsigned endOffset = node == endContainer
+                             ? m_end.offset()
+                             : std::numeric_limits<unsigned>::max();
     layoutText->absoluteRectsForRange(rects, startOffset, endOffset,
                                       useSelectionHeight);
   }
@@ -1445,9 +1449,10 @@
     if (!r || !r->isText())
       continue;
     LayoutText* layoutText = toLayoutText(r);
-    int startOffset = node == startContainer ? m_start.offset() : 0;
-    int endOffset =
-        node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
+    unsigned startOffset = node == startContainer ? m_start.offset() : 0;
+    unsigned endOffset = node == endContainer
+                             ? m_end.offset()
+                             : std::numeric_limits<unsigned>::max();
     layoutText->absoluteQuadsForRange(quads, startOffset, endOffset,
                                       useSelectionHeight);
   }
@@ -1564,7 +1569,7 @@
     boundary.set(oldNode.node().previousSibling(), boundary.offset() + offset,
                  0);
   else if (boundary.container() == oldNode.node().parentNode() &&
-           boundary.offset() == oldNode.index())
+           boundary.offset() == static_cast<unsigned>(oldNode.index()))
     boundary.set(oldNode.node().previousSibling(), offset, 0);
 }
 
@@ -1682,8 +1687,10 @@
       }
     } else if (node->isTextNode()) {
       if (LayoutText* layoutText = toText(node)->layoutObject()) {
-        int startOffset = (node == startContainer) ? m_start.offset() : 0;
-        int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX;
+        unsigned startOffset = (node == startContainer) ? m_start.offset() : 0;
+        unsigned endOffset = (node == endContainer)
+                                 ? m_end.offset()
+                                 : std::numeric_limits<unsigned>::max();
 
         Vector<FloatQuad> textQuads;
         layoutText->absoluteQuadsForRange(textQuads, startOffset, endOffset);
diff --git a/third_party/WebKit/Source/core/dom/Range.h b/third_party/WebKit/Source/core/dom/Range.h
index 18bc278c..f76b3f020 100644
--- a/third_party/WebKit/Source/core/dom/Range.h
+++ b/third_party/WebKit/Source/core/dom/Range.h
@@ -56,9 +56,9 @@
   static Range* create(Document&);
   static Range* create(Document&,
                        Node* startContainer,
-                       int startOffset,
+                       unsigned startOffset,
                        Node* endContainer,
-                       int endOffset);
+                       unsigned endOffset);
   static Range* create(Document&, const Position&, const Position&);
   static Range* createAdjustedToTreeScope(const TreeScope&, const Position&);
 
@@ -69,9 +69,9 @@
     return *m_ownerDocument.get();
   }
   Node* startContainer() const { return m_start.container(); }
-  int startOffset() const { return m_start.offset(); }
+  unsigned startOffset() const { return m_start.offset(); }
   Node* endContainer() const { return m_end.container(); }
-  int endOffset() const { return m_end.offset(); }
+  unsigned endOffset() const { return m_end.offset(); }
 
   bool collapsed() const { return m_start == m_end; }
   bool isConnected() const;
@@ -80,15 +80,15 @@
   static Node* commonAncestorContainer(const Node* containerA,
                                        const Node* containerB);
   void setStart(Node* container,
-                int offset,
+                unsigned offset,
                 ExceptionState& = ASSERT_NO_EXCEPTION);
   void setEnd(Node* container,
-              int offset,
+              unsigned offset,
               ExceptionState& = ASSERT_NO_EXCEPTION);
   void collapse(bool toStart);
   bool isNodeFullyContained(Node&) const;
-  bool isPointInRange(Node* refNode, int offset, ExceptionState&) const;
-  short comparePoint(Node* refNode, int offset, ExceptionState&) const;
+  bool isPointInRange(Node* refNode, unsigned offset, ExceptionState&) const;
+  short comparePoint(Node* refNode, unsigned offset, ExceptionState&) const;
   enum CompareResults {
     NODE_BEFORE,
     NODE_AFTER,
@@ -100,9 +100,9 @@
                               const Range* sourceRange,
                               ExceptionState&) const;
   static short compareBoundaryPoints(Node* containerA,
-                                     int offsetA,
+                                     unsigned offsetA,
                                      Node* containerB,
-                                     int offsetB,
+                                     unsigned offsetB,
                                      ExceptionState&);
   static short compareBoundaryPoints(const RangeBoundaryPoint& boundaryA,
                                      const RangeBoundaryPoint& boundaryB,
@@ -166,7 +166,7 @@
   ClientRectList* getClientRects() const;
   ClientRect* getBoundingClientRect() const;
 
-  static Node* checkNodeWOffset(Node*, int offset, ExceptionState&);
+  static Node* checkNodeWOffset(Node*, unsigned offset, ExceptionState&);
 
   DECLARE_TRACE();
 
@@ -174,9 +174,9 @@
   explicit Range(Document&);
   Range(Document&,
         Node* startContainer,
-        int startOffset,
+        unsigned startOffset,
         Node* endContainer,
-        int endOffset);
+        unsigned endOffset);
 
   void setDocument(Document&);
 
diff --git a/third_party/WebKit/Source/core/dom/Range.idl b/third_party/WebKit/Source/core/dom/Range.idl
index d5a8567..5c45326 100644
--- a/third_party/WebKit/Source/core/dom/Range.idl
+++ b/third_party/WebKit/Source/core/dom/Range.idl
@@ -19,21 +19,19 @@
  */
 
 // https://dom.spec.whatwg.org/#interface-range
-
-// FIXME: All long types in this interface should be unsigned long.
 [
     Constructor,
     ConstructorCallWith=Document,
 ] interface Range {
     readonly attribute Node startContainer;
-    readonly attribute long startOffset;
+    readonly attribute unsigned long startOffset;
     readonly attribute Node endContainer;
-    readonly attribute long endOffset;
+    readonly attribute unsigned long endOffset;
     readonly attribute boolean collapsed;
     readonly attribute Node commonAncestorContainer;
 
-    [RaisesException] void setStart(Node node, long offset);
-    [RaisesException] void setEnd(Node node, long offset);
+    [RaisesException] void setStart(Node node, unsigned long offset);
+    [RaisesException] void setEnd(Node node, unsigned long offset);
     [RaisesException] void setStartBefore(Node node);
     [RaisesException] void setStartAfter(Node node);
     [RaisesException] void setEndBefore(Node node);
@@ -57,8 +55,8 @@
     [NewObject] Range cloneRange();
     [MeasureAs=RangeDetach] void detach();
 
-    [RaisesException] boolean isPointInRange(Node node, long offset);
-    [RaisesException] short comparePoint(Node node, long offset);
+    [RaisesException] boolean isPointInRange(Node node, unsigned long offset);
+    [RaisesException] short comparePoint(Node node, unsigned long offset);
 
     [RaisesException] boolean intersectsNode(Node node);
 
diff --git a/third_party/WebKit/Source/core/dom/RangeBoundaryPoint.h b/third_party/WebKit/Source/core/dom/RangeBoundaryPoint.h
index 7b2dd71..32d2681f 100644
--- a/third_party/WebKit/Source/core/dom/RangeBoundaryPoint.h
+++ b/third_party/WebKit/Source/core/dom/RangeBoundaryPoint.h
@@ -44,13 +44,13 @@
   const Position toPosition() const;
 
   Node* container() const;
-  int offset() const;
+  unsigned offset() const;
   Node* childBefore() const;
 
   void clear();
 
-  void set(Node* container, int offset, Node* childBefore);
-  void setOffset(int);
+  void set(Node* container, unsigned offset, Node* childBefore);
+  void setOffset(unsigned);
 
   void setToBeforeChild(Node&);
   void setToStartOfNode(Node&);
@@ -70,12 +70,12 @@
   void ensureOffsetIsValid() const;
   bool isOffsetValid() const;
 
-  static const int invalidOffset = -1;
+  static const unsigned invalidOffset = static_cast<unsigned>(-1);
 
   Member<Node> m_containerNode;
   Member<Node> m_childBeforeBoundary;
   mutable uint64_t m_domTreeVersion;
-  mutable int m_offsetInContainer;
+  mutable unsigned m_offsetInContainer;
 };
 
 inline RangeBoundaryPoint::RangeBoundaryPoint(Node* container)
@@ -135,7 +135,7 @@
                                      m_offsetInContainer);
 }
 
-inline int RangeBoundaryPoint::offset() const {
+inline unsigned RangeBoundaryPoint::offset() const {
   ensureOffsetIsValid();
   return m_offsetInContainer;
 }
@@ -148,10 +148,10 @@
 }
 
 inline void RangeBoundaryPoint::set(Node* container,
-                                    int offset,
+                                    unsigned offset,
                                     Node* childBefore) {
   DCHECK(container);
-  DCHECK_GE(offset, 0);
+  DCHECK_GE(offset, 0u);
   DCHECK_EQ(childBefore,
             offset ? NodeTraversal::childAt(*container, offset - 1) : 0);
   m_containerNode = container;
@@ -160,10 +160,10 @@
   markValid();
 }
 
-inline void RangeBoundaryPoint::setOffset(int offset) {
+inline void RangeBoundaryPoint::setOffset(unsigned offset) {
   DCHECK(m_containerNode);
   DCHECK(m_containerNode->isCharacterDataNode());
-  DCHECK_GE(m_offsetInContainer, 0);
+  DCHECK_GE(m_offsetInContainer, 0u);
   DCHECK(!m_childBeforeBoundary);
   m_offsetInContainer = offset;
   markValid();
@@ -200,7 +200,7 @@
   m_childBeforeBoundary = m_childBeforeBoundary->previousSibling();
   if (!isOffsetValid())
     return;
-  DCHECK_GT(m_offsetInContainer, 0);
+  DCHECK_GT(m_offsetInContainer, 0u);
   if (!m_childBeforeBoundary)
     m_offsetInContainer = 0;
   else if (m_offsetInContainer > 0)
diff --git a/third_party/WebKit/Source/core/dom/RangeTest.cpp b/third_party/WebKit/Source/core/dom/RangeTest.cpp
index c3127e40..18762ae 100644
--- a/third_party/WebKit/Source/core/dom/RangeTest.cpp
+++ b/third_party/WebKit/Source/core/dom/RangeTest.cpp
@@ -76,29 +76,29 @@
 
   EXPECT_TRUE(range04->boundaryPointsValid());
   EXPECT_EQ(oldText, range04->startContainer());
-  EXPECT_EQ(0, range04->startOffset());
+  EXPECT_EQ(0u, range04->startOffset());
   EXPECT_EQ(newText, range04->endContainer());
-  EXPECT_EQ(2, range04->endOffset());
+  EXPECT_EQ(2u, range04->endOffset());
 
   EXPECT_TRUE(range02->boundaryPointsValid());
   EXPECT_EQ(oldText, range02->startContainer());
-  EXPECT_EQ(0, range02->startOffset());
+  EXPECT_EQ(0u, range02->startOffset());
   EXPECT_EQ(oldText, range02->endContainer());
-  EXPECT_EQ(2, range02->endOffset());
+  EXPECT_EQ(2u, range02->endOffset());
 
   // Our implementation always moves the boundary point at the separation point
   // to the end of the original text node.
   EXPECT_TRUE(range22->boundaryPointsValid());
   EXPECT_EQ(oldText, range22->startContainer());
-  EXPECT_EQ(2, range22->startOffset());
+  EXPECT_EQ(2u, range22->startOffset());
   EXPECT_EQ(oldText, range22->endContainer());
-  EXPECT_EQ(2, range22->endOffset());
+  EXPECT_EQ(2u, range22->endOffset());
 
   EXPECT_TRUE(range24->boundaryPointsValid());
   EXPECT_EQ(oldText, range24->startContainer());
-  EXPECT_EQ(2, range24->startOffset());
+  EXPECT_EQ(2u, range24->startOffset());
   EXPECT_EQ(newText, range24->endContainer());
-  EXPECT_EQ(2, range24->endOffset());
+  EXPECT_EQ(2u, range24->endOffset());
 }
 
 TEST_F(RangeTest, SplitTextNodeRangeOutsideText) {
@@ -128,41 +128,41 @@
 
   EXPECT_TRUE(rangeOuterOutside->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterOutside->startContainer());
-  EXPECT_EQ(0, rangeOuterOutside->startOffset());
+  EXPECT_EQ(0u, rangeOuterOutside->startOffset());
   EXPECT_EQ(outer, rangeOuterOutside->endContainer());
-  EXPECT_EQ(6,
+  EXPECT_EQ(6u,
             rangeOuterOutside
                 ->endOffset());  // Increased by 1 since a new node is inserted.
 
   EXPECT_TRUE(rangeOuterInside->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterInside->startContainer());
-  EXPECT_EQ(1, rangeOuterInside->startOffset());
+  EXPECT_EQ(1u, rangeOuterInside->startOffset());
   EXPECT_EQ(outer, rangeOuterInside->endContainer());
-  EXPECT_EQ(5, rangeOuterInside->endOffset());
+  EXPECT_EQ(5u, rangeOuterInside->endOffset());
 
   EXPECT_TRUE(rangeOuterSurroundingText->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterSurroundingText->startContainer());
-  EXPECT_EQ(2, rangeOuterSurroundingText->startOffset());
+  EXPECT_EQ(2u, rangeOuterSurroundingText->startOffset());
   EXPECT_EQ(outer, rangeOuterSurroundingText->endContainer());
-  EXPECT_EQ(4, rangeOuterSurroundingText->endOffset());
+  EXPECT_EQ(4u, rangeOuterSurroundingText->endOffset());
 
   EXPECT_TRUE(rangeInnerLeft->boundaryPointsValid());
   EXPECT_EQ(innerLeft, rangeInnerLeft->startContainer());
-  EXPECT_EQ(0, rangeInnerLeft->startOffset());
+  EXPECT_EQ(0u, rangeInnerLeft->startOffset());
   EXPECT_EQ(innerLeft, rangeInnerLeft->endContainer());
-  EXPECT_EQ(1, rangeInnerLeft->endOffset());
+  EXPECT_EQ(1u, rangeInnerLeft->endOffset());
 
   EXPECT_TRUE(rangeInnerRight->boundaryPointsValid());
   EXPECT_EQ(innerRight, rangeInnerRight->startContainer());
-  EXPECT_EQ(0, rangeInnerRight->startOffset());
+  EXPECT_EQ(0u, rangeInnerRight->startOffset());
   EXPECT_EQ(innerRight, rangeInnerRight->endContainer());
-  EXPECT_EQ(1, rangeInnerRight->endOffset());
+  EXPECT_EQ(1u, rangeInnerRight->endOffset());
 
   EXPECT_TRUE(rangeFromTextToMiddleOfElement->boundaryPointsValid());
   EXPECT_EQ(newText, rangeFromTextToMiddleOfElement->startContainer());
-  EXPECT_EQ(3, rangeFromTextToMiddleOfElement->startOffset());
+  EXPECT_EQ(3u, rangeFromTextToMiddleOfElement->startOffset());
   EXPECT_EQ(outer, rangeFromTextToMiddleOfElement->endContainer());
-  EXPECT_EQ(4, rangeFromTextToMiddleOfElement->endOffset());
+  EXPECT_EQ(4u, rangeFromTextToMiddleOfElement->endOffset());
 }
 
 TEST_F(RangeTest, updateOwnerDocumentIfNeeded) {
@@ -176,9 +176,9 @@
   anotherDocument->appendChild(foo);
 
   EXPECT_EQ(bar, range->startContainer());
-  EXPECT_EQ(0, range->startOffset());
+  EXPECT_EQ(0u, range->startOffset());
   EXPECT_EQ(foo, range->endContainer());
-  EXPECT_EQ(1, range->endOffset());
+  EXPECT_EQ(1u, range->endOffset());
 }
 
 // Regression test for crbug.com/639184
@@ -198,9 +198,9 @@
 
   EXPECT_TRUE(range->boundaryPointsValid());
   EXPECT_EQ(span2, range->startContainer());
-  EXPECT_EQ(0, range->startOffset());
+  EXPECT_EQ(0u, range->startOffset());
   EXPECT_EQ(div, range->endContainer());
-  EXPECT_EQ(2, range->endOffset());
+  EXPECT_EQ(2u, range->endOffset());
 }
 
 // Regression test for crbug.com/639184
@@ -220,9 +220,9 @@
 
   EXPECT_TRUE(range->boundaryPointsValid());
   EXPECT_EQ(span2, range->startContainer());
-  EXPECT_EQ(0, range->startOffset());
+  EXPECT_EQ(0u, range->startOffset());
   EXPECT_EQ(div, range->endContainer());
-  EXPECT_EQ(2, range->endOffset());
+  EXPECT_EQ(2u, range->endOffset());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/SinkDocument.cpp b/third_party/WebKit/Source/core/dom/SinkDocument.cpp
similarity index 98%
rename from third_party/WebKit/Source/core/loader/SinkDocument.cpp
rename to third_party/WebKit/Source/core/dom/SinkDocument.cpp
index a0e1cb1..30c42a1 100644
--- a/third_party/WebKit/Source/core/loader/SinkDocument.cpp
+++ b/third_party/WebKit/Source/core/dom/SinkDocument.cpp
@@ -23,7 +23,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "core/loader/SinkDocument.h"
+#include "core/dom/SinkDocument.h"
 
 #include "core/dom/RawDataDocumentParser.h"
 #include "core/frame/UseCounter.h"
diff --git a/third_party/WebKit/Source/core/loader/SinkDocument.h b/third_party/WebKit/Source/core/dom/SinkDocument.h
similarity index 100%
rename from third_party/WebKit/Source/core/loader/SinkDocument.h
rename to third_party/WebKit/Source/core/dom/SinkDocument.h
diff --git a/third_party/WebKit/Source/core/dom/StaticRangeTest.cpp b/third_party/WebKit/Source/core/dom/StaticRangeTest.cpp
index 1fd88c54..885cfe7 100644
--- a/third_party/WebKit/Source/core/dom/StaticRangeTest.cpp
+++ b/third_party/WebKit/Source/core/dom/StaticRangeTest.cpp
@@ -66,29 +66,29 @@
   // Range should mutate.
   EXPECT_TRUE(range04->boundaryPointsValid());
   EXPECT_EQ(oldText, range04->startContainer());
-  EXPECT_EQ(0, range04->startOffset());
+  EXPECT_EQ(0u, range04->startOffset());
   EXPECT_EQ(newText, range04->endContainer());
-  EXPECT_EQ(2, range04->endOffset());
+  EXPECT_EQ(2u, range04->endOffset());
 
   EXPECT_TRUE(range02->boundaryPointsValid());
   EXPECT_EQ(oldText, range02->startContainer());
-  EXPECT_EQ(0, range02->startOffset());
+  EXPECT_EQ(0u, range02->startOffset());
   EXPECT_EQ(oldText, range02->endContainer());
-  EXPECT_EQ(2, range02->endOffset());
+  EXPECT_EQ(2u, range02->endOffset());
 
   // Our implementation always moves the boundary point at the separation point
   // to the end of the original text node.
   EXPECT_TRUE(range22->boundaryPointsValid());
   EXPECT_EQ(oldText, range22->startContainer());
-  EXPECT_EQ(2, range22->startOffset());
+  EXPECT_EQ(2u, range22->startOffset());
   EXPECT_EQ(oldText, range22->endContainer());
-  EXPECT_EQ(2, range22->endOffset());
+  EXPECT_EQ(2u, range22->endOffset());
 
   EXPECT_TRUE(range24->boundaryPointsValid());
   EXPECT_EQ(oldText, range24->startContainer());
-  EXPECT_EQ(2, range24->startOffset());
+  EXPECT_EQ(2u, range24->startOffset());
   EXPECT_EQ(newText, range24->endContainer());
-  EXPECT_EQ(2, range24->endOffset());
+  EXPECT_EQ(2u, range24->endOffset());
 
   // StaticRange shouldn't mutate.
   EXPECT_EQ(oldText, staticRange04->startContainer());
@@ -154,41 +154,41 @@
   // Range should mutate.
   EXPECT_TRUE(rangeOuterOutside->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterOutside->startContainer());
-  EXPECT_EQ(0, rangeOuterOutside->startOffset());
+  EXPECT_EQ(0u, rangeOuterOutside->startOffset());
   EXPECT_EQ(outer, rangeOuterOutside->endContainer());
-  EXPECT_EQ(6,
+  EXPECT_EQ(6u,
             rangeOuterOutside
                 ->endOffset());  // Increased by 1 since a new node is inserted.
 
   EXPECT_TRUE(rangeOuterInside->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterInside->startContainer());
-  EXPECT_EQ(1, rangeOuterInside->startOffset());
+  EXPECT_EQ(1u, rangeOuterInside->startOffset());
   EXPECT_EQ(outer, rangeOuterInside->endContainer());
-  EXPECT_EQ(5, rangeOuterInside->endOffset());
+  EXPECT_EQ(5u, rangeOuterInside->endOffset());
 
   EXPECT_TRUE(rangeOuterSurroundingText->boundaryPointsValid());
   EXPECT_EQ(outer, rangeOuterSurroundingText->startContainer());
-  EXPECT_EQ(2, rangeOuterSurroundingText->startOffset());
+  EXPECT_EQ(2u, rangeOuterSurroundingText->startOffset());
   EXPECT_EQ(outer, rangeOuterSurroundingText->endContainer());
-  EXPECT_EQ(4, rangeOuterSurroundingText->endOffset());
+  EXPECT_EQ(4u, rangeOuterSurroundingText->endOffset());
 
   EXPECT_TRUE(rangeInnerLeft->boundaryPointsValid());
   EXPECT_EQ(innerLeft, rangeInnerLeft->startContainer());
-  EXPECT_EQ(0, rangeInnerLeft->startOffset());
+  EXPECT_EQ(0u, rangeInnerLeft->startOffset());
   EXPECT_EQ(innerLeft, rangeInnerLeft->endContainer());
-  EXPECT_EQ(1, rangeInnerLeft->endOffset());
+  EXPECT_EQ(1u, rangeInnerLeft->endOffset());
 
   EXPECT_TRUE(rangeInnerRight->boundaryPointsValid());
   EXPECT_EQ(innerRight, rangeInnerRight->startContainer());
-  EXPECT_EQ(0, rangeInnerRight->startOffset());
+  EXPECT_EQ(0u, rangeInnerRight->startOffset());
   EXPECT_EQ(innerRight, rangeInnerRight->endContainer());
-  EXPECT_EQ(1, rangeInnerRight->endOffset());
+  EXPECT_EQ(1u, rangeInnerRight->endOffset());
 
   EXPECT_TRUE(rangeFromTextToMiddleOfElement->boundaryPointsValid());
   EXPECT_EQ(newText, rangeFromTextToMiddleOfElement->startContainer());
-  EXPECT_EQ(3, rangeFromTextToMiddleOfElement->startOffset());
+  EXPECT_EQ(3u, rangeFromTextToMiddleOfElement->startOffset());
   EXPECT_EQ(outer, rangeFromTextToMiddleOfElement->endContainer());
-  EXPECT_EQ(4, rangeFromTextToMiddleOfElement->endOffset());
+  EXPECT_EQ(4u, rangeFromTextToMiddleOfElement->endOffset());
 
   // StaticRange shouldn't mutate.
   EXPECT_EQ(outer, staticRangeOuterOutside->startContainer());
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index ff28ffad..e60d713 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -204,7 +204,7 @@
 }
 
 static bool isCaretAtStartOfWrappedLine(const FrameSelection& selection) {
-  if (!selection.isCaret())
+  if (!selection.computeVisibleSelectionInDOMTreeDeprecated().isCaret())
     return false;
   if (selection.affinity() != TextAffinity::Downstream)
     return false;
@@ -263,7 +263,10 @@
 }
 
 bool Editor::canEdit() const {
-  return frame().selection().rootEditableElement();
+  return frame()
+      .selection()
+      .computeVisibleSelectionInDOMTreeDeprecated()
+      .rootEditableElement();
 }
 
 bool Editor::canEditRichly() const {
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index 5ac86457..7a0fa9a 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -480,7 +480,8 @@
 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const {
   DCHECK_GE(document().lifecycle().state(), DocumentLifecycle::LayoutClean);
   bool result = m_frameCaret->shouldPaintCaret(block);
-  DCHECK(!result || (isCaret() && hasEditableStyle()));
+  DCHECK(!result || (computeVisibleSelectionInDOMTreeDeprecated().isCaret() &&
+                     hasEditableStyle()));
   return result;
 }
 
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.h b/third_party/WebKit/Source/core/editing/FrameSelection.h
index bcb452c..d2a3c77 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.h
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.h
@@ -198,9 +198,6 @@
   Position start() const {
     return computeVisibleSelectionInDOMTreeDeprecated().start();
   }
-  Position end() const {
-    return computeVisibleSelectionInDOMTreeDeprecated().end();
-  }
 
   // Returns true if specified layout block should paint caret. This function is
   // called during painting only.
@@ -216,9 +213,6 @@
   bool isNone() const {
     return computeVisibleSelectionInDOMTreeDeprecated().isNone();
   }
-  bool isCaret() const {
-    return computeVisibleSelectionInDOMTreeDeprecated().isCaret();
-  }
   bool isRange() const {
     return computeVisibleSelectionInDOMTreeDeprecated().isRange();
   }
diff --git a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
index ddfaa7f..a3c95cd 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelectionTest.cpp
@@ -78,7 +78,8 @@
   selection().setSelection(
       SelectionInDOMTree::Builder().collapse(Position(text, 0)).build());
   document().view()->updateAllLifecyclePhases();
-  EXPECT_TRUE(selection().isCaret());
+  EXPECT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   EXPECT_TRUE(toLayoutBlock(document().body()->layoutObject())
                   ->shouldPaintCursorCaret());
 
diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.cpp b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
index 01eaa00..7ea8eddf 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
@@ -279,7 +279,11 @@
     return true;
   }
 
-  Element* rootEditableElement = frame().selection().rootEditableElement();
+  Element* rootEditableElement =
+      frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
   if (!rootEditableElement)
     return false;
   PlainTextRange compositionRange =
@@ -374,7 +378,11 @@
     const String& text,
     int relativeCaretPosition,
     const Vector<CompositionUnderline>& underlines) {
-  Element* rootEditableElement = frame().selection().rootEditableElement();
+  Element* rootEditableElement =
+      frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
   if (!rootEditableElement)
     return false;
   DCHECK(hasComposition());
@@ -419,7 +427,11 @@
     if (!insertText(text))
       return false;
 
-    Element* rootEditableElement = frame().selection().rootEditableElement();
+    Element* rootEditableElement =
+        frame()
+            .selection()
+            .computeVisibleSelectionInDOMTreeDeprecated()
+            .rootEditableElement();
     if (rootEditableElement) {
       addCompositionUnderlines(underlines, rootEditableElement, textStart);
     }
@@ -626,7 +638,10 @@
     const Vector<CompositionUnderline>& underlines,
     unsigned compositionStart,
     unsigned compositionEnd) {
-  Element* editable = frame().selection().rootEditableElement();
+  Element* editable = frame()
+                          .selection()
+                          .computeVisibleSelectionInDOMTreeDeprecated()
+                          .rootEditableElement();
   if (!editable)
     return;
 
@@ -689,7 +704,11 @@
     const PlainTextRange& offsets) const {
   if (offsets.isNull())
     return EphemeralRange();
-  Element* rootEditableElement = frame().selection().rootEditableElement();
+  Element* rootEditableElement =
+      frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
   if (!rootEditableElement)
     return EphemeralRange();
 
@@ -726,7 +745,11 @@
   start = std::max(start, 0);
   end = std::max(end, start);
 
-  Element* rootEditableElement = frame().selection().rootEditableElement();
+  Element* rootEditableElement =
+      frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
   if (!rootEditableElement)
     return PlainTextRange();
   const EphemeralRange& range =
@@ -793,7 +816,11 @@
     if (before == 0)
       break;
     ++before;
-  } while (frame().selection().start() == frame().selection().end() &&
+  } while (frame().selection().start() ==
+               frame()
+                   .selection()
+                   .computeVisibleSelectionInDOMTreeDeprecated()
+                   .end() &&
            before <= static_cast<int>(selectionOffsets.start()));
   // TODO(chongz): Find a way to distinguish Forward and Backward.
   Node* target = document().focusedElement();
@@ -813,7 +840,10 @@
   if (selectionOffsets.isNull())
     return;
   Element* const rootEditableElement =
-      frame().selection().rootEditableElement();
+      frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
   if (!rootEditableElement)
     return;
   int selectionStart = static_cast<int>(selectionOffsets.start());
@@ -877,7 +907,10 @@
     // plugins/mouse-capture-inside-shadow.html reaches here.
     return info;
   }
-  Element* element = frame().selection().rootEditableElement();
+  Element* element = frame()
+                         .selection()
+                         .computeVisibleSelectionInDOMTreeDeprecated()
+                         .rootEditableElement();
   if (!element)
     return info;
 
diff --git a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
index af4913f..eb342fb 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
@@ -175,8 +175,8 @@
   controller().setCompositionFromExistingText(underlines, 0, 5);
 
   Range* range = controller().compositionRange();
-  EXPECT_EQ(0, range->startOffset());
-  EXPECT_EQ(5, range->endOffset());
+  EXPECT_EQ(0u, range->startOffset());
+  EXPECT_EQ(5u, range->endOffset());
 
   PlainTextRange plainTextRange(PlainTextRange::create(*div, *range));
   EXPECT_EQ(0u, plainTextRange.start());
@@ -194,7 +194,11 @@
   document().updateStyleAndLayout();
   controller().setEditableSelectionOffsets(PlainTextRange(2, 2));
   EXPECT_EQ(2, frame().selection().start().computeOffsetInContainerNode());
-  EXPECT_EQ(2, frame().selection().end().computeOffsetInContainerNode());
+  EXPECT_EQ(2, frame()
+                   .selection()
+                   .computeVisibleSelectionInDOMTreeDeprecated()
+                   .end()
+                   .computeOffsetInContainerNode());
 
   controller().setComposition(String("a"), underlines, 1, 1);
   EXPECT_STREQ("\xF0\x9F\x8F\x86\x61", div->innerText().utf8().data());
@@ -342,7 +346,11 @@
 
   controller().finishComposingText(InputMethodController::KeepSelection);
   EXPECT_EQ(0, frame().selection().start().computeOffsetInContainerNode());
-  EXPECT_EQ(0, frame().selection().end().computeOffsetInContainerNode());
+  EXPECT_EQ(0, frame()
+                   .selection()
+                   .computeVisibleSelectionInDOMTreeDeprecated()
+                   .end()
+                   .computeOffsetInContainerNode());
 }
 
 TEST_F(InputMethodControllerTest, DeleteBySettingEmptyComposition) {
@@ -384,8 +392,8 @@
   controller().setCompositionFromExistingText(underlines, 0, 5);
 
   Range* range = controller().compositionRange();
-  EXPECT_EQ(1, range->startOffset());
-  EXPECT_EQ(6, range->endOffset());
+  EXPECT_EQ(1u, range->startOffset());
+  EXPECT_EQ(6u, range->endOffset());
 
   PlainTextRange plainTextRange(PlainTextRange::create(*div, *range));
   EXPECT_EQ(0u, plainTextRange.start());
diff --git a/third_party/WebKit/Source/core/editing/PendingSelection.cpp b/third_party/WebKit/Source/core/editing/PendingSelection.cpp
index 1aca83e8..1b10665 100644
--- a/third_party/WebKit/Source/core/editing/PendingSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/PendingSelection.cpp
@@ -55,7 +55,7 @@
   return true;
 }
 
-VisibleSelectionInFlatTree PendingSelection::calcVisibleSelection(
+SelectionInFlatTree PendingSelection::calcVisibleSelection(
     const VisibleSelectionInFlatTree& originalSelection) const {
   const PositionInFlatTree& start = originalSelection.start();
   const PositionInFlatTree& end = originalSelection.end();
@@ -66,7 +66,6 @@
       m_frameSelection->shouldShowBlockCursor() &&
       selectionType == SelectionType::CaretSelection &&
       !isLogicalEndOfLine(createVisiblePosition(end, affinity));
-  VisibleSelectionInFlatTree selection;
   if (enclosingTextControl(start.computeContainerNode())) {
     // TODO(yosin) We should use |PositionMoveType::CodePoint| to avoid
     // ending paint at middle of character.
@@ -74,8 +73,9 @@
         paintBlockCursor ? nextPositionOf(originalSelection.extent(),
                                           PositionMoveType::CodeUnit)
                          : end;
-    selection.setWithoutValidation(start, endPosition);
-    return selection;
+    return SelectionInFlatTree::Builder()
+        .setBaseAndExtent(start, endPosition)
+        .build();
   }
 
   const VisiblePositionInFlatTree& visibleStart = createVisiblePosition(
@@ -83,27 +83,27 @@
                  ? TextAffinity::Downstream
                  : affinity);
   if (visibleStart.isNull())
-    return VisibleSelectionInFlatTree();
+    return SelectionInFlatTree();
   if (paintBlockCursor) {
     const VisiblePositionInFlatTree visibleExtent = nextPositionOf(
         createVisiblePosition(end, affinity), CanSkipOverEditingBoundary);
     if (visibleExtent.isNull())
-      return VisibleSelectionInFlatTree();
+      return SelectionInFlatTree();
     SelectionInFlatTree::Builder builder;
     builder.collapse(visibleStart.toPositionWithAffinity());
     builder.extend(visibleExtent.deepEquivalent());
-    return createVisibleSelection(builder.build());
+    return builder.build();
   }
   const VisiblePositionInFlatTree visibleEnd =
       createVisiblePosition(end, selectionType == SelectionType::RangeSelection
                                      ? TextAffinity::Upstream
                                      : affinity);
   if (visibleEnd.isNull())
-    return VisibleSelectionInFlatTree();
+    return SelectionInFlatTree();
   SelectionInFlatTree::Builder builder;
   builder.collapse(visibleStart.toPositionWithAffinity());
   builder.extend(visibleEnd.deepEquivalent());
-  return createVisibleSelection(builder.build());
+  return builder.build();
 }
 
 void PendingSelection::commit(LayoutView& layoutView) {
@@ -124,7 +124,7 @@
   // <https://bugs.webkit.org/show_bug.cgi?id=69563> and
   // <rdar://problem/10232866>.
   const VisibleSelectionInFlatTree& selection =
-      calcVisibleSelection(originalSelection);
+      createVisibleSelection(calcVisibleSelection(originalSelection));
 
   if (!selection.isRange()) {
     layoutView.clearSelection();
diff --git a/third_party/WebKit/Source/core/editing/PendingSelection.h b/third_party/WebKit/Source/core/editing/PendingSelection.h
index aec09a8..f1b7b624 100644
--- a/third_party/WebKit/Source/core/editing/PendingSelection.h
+++ b/third_party/WebKit/Source/core/editing/PendingSelection.h
@@ -48,7 +48,7 @@
 
   const VisibleSelection& visibleSelection() const;
 
-  VisibleSelectionInFlatTree calcVisibleSelection(
+  SelectionInFlatTree calcVisibleSelection(
       const VisibleSelectionInFlatTree&) const;
 
   Member<FrameSelection> m_frameSelection;
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
index 8d897926..72c96efb 100644
--- a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
@@ -85,13 +85,9 @@
     DCHECK(end.isNull());
     return NoSelection;
   }
+  DCHECK(!needsLayoutTreeUpdate(start)) << start << ' ' << end;
   if (start == end)
     return CaretSelection;
-  // TODO(yosin) We should call |Document::updateStyleAndLayout()| here for
-  // |mostBackwardCaretPosition()|. However, we are here during
-  // |Node::removeChild()|.
-  start.anchorNode()->updateDistribution();
-  end.anchorNode()->updateDistribution();
   if (mostBackwardCaretPosition(start) == mostBackwardCaretPosition(end))
     return CaretSelection;
   return RangeSelection;
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp b/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
index 549b61c..b7b0415 100644
--- a/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
@@ -209,8 +209,8 @@
   EXPECT_TRUE(selectionInFlatTree.isCaret());
 
   Range* range = firstRangeOf(selection);
-  EXPECT_EQ(0, range->startOffset());
-  EXPECT_EQ(0, range->endOffset());
+  EXPECT_EQ(0u, range->startOffset());
+  EXPECT_EQ(0u, range->endOffset());
   EXPECT_EQ("", range->text());
   testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                  selectionInFlatTree);
@@ -351,8 +351,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(0, range->startOffset());
-    EXPECT_EQ(5, range->endOffset());
+    EXPECT_EQ(0u, range->startOffset());
+    EXPECT_EQ(5u, range->endOffset());
     EXPECT_EQ("Lorem", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -367,8 +367,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(6, range->startOffset());
-    EXPECT_EQ(11, range->endOffset());
+    EXPECT_EQ(6u, range->startOffset());
+    EXPECT_EQ(11u, range->endOffset());
     EXPECT_EQ("ipsum", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -385,8 +385,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(5, range->startOffset());
-    EXPECT_EQ(6, range->endOffset());
+    EXPECT_EQ(5u, range->startOffset());
+    EXPECT_EQ(6u, range->endOffset());
     EXPECT_EQ(" ", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -403,8 +403,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(26, range->startOffset());
-    EXPECT_EQ(27, range->endOffset());
+    EXPECT_EQ(26u, range->startOffset());
+    EXPECT_EQ(27u, range->endOffset());
     EXPECT_EQ(",", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -419,8 +419,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(27, range->startOffset());
-    EXPECT_EQ(28, range->endOffset());
+    EXPECT_EQ(27u, range->startOffset());
+    EXPECT_EQ(28u, range->endOffset());
     EXPECT_EQ(" ", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -435,8 +435,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(0, range->startOffset());
-    EXPECT_EQ(5, range->endOffset());
+    EXPECT_EQ(0u, range->startOffset());
+    EXPECT_EQ(5u, range->endOffset());
     EXPECT_EQ("Lorem", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
@@ -451,8 +451,8 @@
         expandUsingGranularity(selectionInFlatTree, WordGranularity);
 
     Range* range = firstRangeOf(selection);
-    EXPECT_EQ(0, range->startOffset());
-    EXPECT_EQ(11, range->endOffset());
+    EXPECT_EQ(0u, range->startOffset());
+    EXPECT_EQ(11u, range->endOffset());
     EXPECT_EQ("Lorem ipsum", range->text());
     testFlatTreePositionsToEqualToDOMTreePositions(selection,
                                                    selectionInFlatTree);
diff --git a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommandTest.cpp b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommandTest.cpp
index d9851af..6dfd318 100644
--- a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommandTest.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommandTest.cpp
@@ -54,7 +54,9 @@
   EXPECT_TRUE(command->apply()) << "the delete command should have succeeded";
   EXPECT_EQ("<div contenteditable=\"true\"><br></div>",
             document().body()->innerHTML());
-  EXPECT_TRUE(frame->selection().isCaret());
+  EXPECT_TRUE(frame->selection()
+                  .computeVisibleSelectionInDOMTreeDeprecated()
+                  .isCaret());
   EXPECT_EQ(Position(div, 0), frame->selection().base().toOffsetInAnchor());
 }
 
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
index f82a155..f1c8290 100644
--- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -415,7 +415,7 @@
 
 static TriState selectionListState(const FrameSelection& selection,
                                    const QualifiedName& tagName) {
-  if (selection.isCaret()) {
+  if (selection.computeVisibleSelectionInDOMTreeDeprecated().isCaret()) {
     if (enclosingElementWithTag(
             selection.computeVisibleSelectionInDOMTreeDeprecated().start(),
             tagName))
@@ -1918,7 +1918,9 @@
          frame.selection()
              .computeVisibleSelectionInDOMTreeDeprecated()
              .isContentRichlyEditable() &&
-         frame.selection().rootEditableElement();
+         frame.selection()
+             .computeVisibleSelectionInDOMTreeDeprecated()
+             .rootEditableElement();
 }
 
 static bool enabledPaste(LocalFrame& frame,
diff --git a/third_party/WebKit/Source/core/editing/commands/RemoveFormatCommand.cpp b/third_party/WebKit/Source/core/editing/commands/RemoveFormatCommand.cpp
index 8132dae..f57bda07 100644
--- a/third_party/WebKit/Source/core/editing/commands/RemoveFormatCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/RemoveFormatCommand.cpp
@@ -65,7 +65,9 @@
 
   // Get the default style for this editable root, it's the style that we'll
   // give the content that we're operating on.
-  Element* root = frame->selection().rootEditableElement();
+  Element* root = frame->selection()
+                      .computeVisibleSelectionInDOMTreeDeprecated()
+                      .rootEditableElement();
   EditingStyle* defaultStyle = EditingStyle::create(root);
 
   // We want to remove everything but transparent background.
diff --git a/third_party/WebKit/Source/core/editing/iterators/CharacterIteratorTest.cpp b/third_party/WebKit/Source/core/editing/iterators/CharacterIteratorTest.cpp
index a5e54ed9..ffba4aa 100644
--- a/third_party/WebKit/Source/core/editing/iterators/CharacterIteratorTest.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/CharacterIteratorTest.cpp
@@ -61,8 +61,8 @@
 
   Node* textNode = document().getElementById("div")->lastChild();
   Range* entireRange = Range::create(document(), textNode, 1, textNode, 4);
-  EXPECT_EQ(1, entireRange->startOffset());
-  EXPECT_EQ(4, entireRange->endOffset());
+  EXPECT_EQ(1u, entireRange->startOffset());
+  EXPECT_EQ(4u, entireRange->endOffset());
 
   const EphemeralRange& result =
       calculateCharacterSubrange(EphemeralRange(entireRange), 2, 0);
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
index bd4b4a3..ed065a7 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
@@ -640,11 +640,15 @@
   int selectionOffset = 0;
   int ambiguousBoundaryOffset = -1;
 
-  if (frame().selection().isCaret()) {
+  if (frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .isCaret()) {
     // TODO(xiaochengh): The following comment does not match the current
     // behavior and should be rewritten.
     // Attempt to save the caret position so we can restore it later if needed
-    const Position& caretPosition = frame().selection().end();
+    const Position& caretPosition =
+        frame().selection().computeVisibleSelectionInDOMTreeDeprecated().end();
     selectionOffset = paragraph.offsetTo(caretPosition);
     if (selectionOffset > 0 &&
         static_cast<unsigned>(selectionOffset) <= paragraph.text().length() &&
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index 824c132..4810ed8 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -40,6 +40,7 @@
 #include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/FrameRequestCallback.h"
 #include "core/dom/SandboxFlags.h"
+#include "core/dom/SinkDocument.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/dom/custom/CustomElementRegistry.h"
 #include "core/editing/Editor.h"
@@ -70,7 +71,6 @@
 #include "core/inspector/InspectorTraceEvents.h"
 #include "core/loader/DocumentLoader.h"
 #include "core/loader/FrameLoaderClient.h"
-#include "core/loader/SinkDocument.h"
 #include "core/loader/appcache/ApplicationCache.h"
 #include "core/page/ChromeClient.h"
 #include "core/page/CreateWindow.h"
diff --git a/third_party/WebKit/Source/core/frame/Location.cpp b/third_party/WebKit/Source/core/frame/Location.cpp
index d030155..23ffea3f 100644
--- a/third_party/WebKit/Source/core/frame/Location.cpp
+++ b/third_party/WebKit/Source/core/frame/Location.cpp
@@ -286,7 +286,7 @@
 }
 
 bool Location::isAttached() const {
-  return m_domWindow->frame() && m_domWindow->frame()->host();
+  return m_domWindow->frame();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/Location.h b/third_party/WebKit/Source/core/frame/Location.h
index 43a58ff..12fc280 100644
--- a/third_party/WebKit/Source/core/frame/Location.h
+++ b/third_party/WebKit/Source/core/frame/Location.h
@@ -131,9 +131,7 @@
   // LocalDOMWindow.
   Document* document() const;
 
-  // Returns true if:
-  // (1) the associated Window is the active Window in the frame
-  // (2) and the frame is attached.
+  // Returns true if the associated Window is the active Window in the frame.
   bool isAttached() const;
 
   enum class SetLocationPolicy { Normal, ReplaceThisFrame };
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index f0e2f9e0..ef4fa20 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1476,6 +1476,7 @@
     FetchResponseConstructionWithStream = 1826,
     LocationOrigin = 1827,
     DocumentOrigin = 1828,
+    SubtleCryptoOnlyStrictSecureContextCheckFailed = 1829,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
index f2646b8..7055cd55 100644
--- a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
@@ -153,7 +153,8 @@
 
   TapEventBuilder singleTapEvent(IntPoint(0, 0), 1);
   document().frame()->eventHandler().handleGestureEvent(singleTapEvent);
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   EXPECT_EQ(Position(line, 0), selection().start());
 
   // Multi-tap events on editable elements should trigger selection, just
@@ -163,10 +164,12 @@
   ASSERT_TRUE(selection().isRange());
   EXPECT_EQ(Position(line, 0), selection().start());
   if (document().frame()->editor().isSelectTrailingWhitespaceEnabled()) {
-    EXPECT_EQ(Position(line, 4), selection().end());
+    EXPECT_EQ(Position(line, 4),
+              selection().computeVisibleSelectionInDOMTreeDeprecated().end());
     EXPECT_EQ("One ", WebString(selection().selectedText()).utf8());
   } else {
-    EXPECT_EQ(Position(line, 3), selection().end());
+    EXPECT_EQ(Position(line, 3),
+              selection().computeVisibleSelectionInDOMTreeDeprecated().end());
     EXPECT_EQ("One", WebString(selection().selectedText()).utf8());
   }
 
@@ -174,7 +177,8 @@
   document().frame()->eventHandler().handleGestureEvent(tripleTapEvent);
   ASSERT_TRUE(selection().isRange());
   EXPECT_EQ(Position(line, 0), selection().start());
-  EXPECT_EQ(Position(line, 13), selection().end());
+  EXPECT_EQ(Position(line, 13),
+            selection().computeVisibleSelectionInDOMTreeDeprecated().end());
   EXPECT_EQ("One Two Three", WebString(selection().selectedText()).utf8());
 }
 
@@ -188,18 +192,21 @@
 
   TapEventBuilder singleTapEvent(IntPoint(0, 0), 1);
   document().frame()->eventHandler().handleGestureEvent(singleTapEvent);
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   EXPECT_EQ(Position(line, 0), selection().start());
 
   // As the text is readonly, multi-tap events should not trigger selection.
   TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2);
   document().frame()->eventHandler().handleGestureEvent(doubleTapEvent);
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   EXPECT_EQ(Position(line, 0), selection().start());
 
   TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3);
   document().frame()->eventHandler().handleGestureEvent(tripleTapEvent);
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   EXPECT_EQ(Position(line, 0), selection().start());
 }
 
@@ -304,7 +311,8 @@
   TapEventBuilder singleTapEvent(IntPoint(200, 200), 1);
   document().frame()->eventHandler().handleGestureEvent(singleTapEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_FALSE(selection().isHandleVisible());
 }
 
@@ -314,7 +322,8 @@
   TapEventBuilder singleTapEvent(IntPoint(200, 200), 1);
   document().frame()->eventHandler().handleGestureEvent(singleTapEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_TRUE(selection().isHandleVisible());
 }
 
@@ -324,14 +333,16 @@
   LongPressEventBuilder longPressEvent(IntPoint(200, 200));
   document().frame()->eventHandler().handleGestureEvent(longPressEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_TRUE(selection().isHandleVisible());
 
   // Single Tap on an empty edit field should clear insertion handle
   TapEventBuilder singleTapEvent(IntPoint(200, 200), 1);
   document().frame()->eventHandler().handleGestureEvent(singleTapEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_FALSE(selection().isHandleVisible());
 }
 
@@ -341,7 +352,8 @@
   LongPressEventBuilder longPressEvent(IntPoint(200, 200));
   document().frame()->eventHandler().handleGestureEvent(longPressEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_TRUE(selection().isHandleVisible());
 }
 
@@ -352,7 +364,8 @@
   LongPressEventBuilder longPressEvent(IntPoint(200, 200));
   document().frame()->eventHandler().handleGestureEvent(longPressEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_TRUE(selection().isHandleVisible());
 
   // Tap away from text area should clear handle
@@ -370,7 +383,8 @@
       IntPoint(200, 200), 1, WebPointerProperties::Button::Left);
   document().frame()->eventHandler().handleMousePressEvent(leftMousePressEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_FALSE(selection().isHandleVisible());
 
   MousePressEventBuilder rightMousePressEvent(
@@ -378,7 +392,8 @@
   document().frame()->eventHandler().handleMousePressEvent(
       rightMousePressEvent);
 
-  ASSERT_TRUE(selection().isCaret());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isCaret());
   ASSERT_FALSE(selection().isHandleVisible());
 
   MousePressEventBuilder doubleClickMousePressEvent(
diff --git a/third_party/WebKit/Source/core/loader/BUILD.gn b/third_party/WebKit/Source/core/loader/BUILD.gn
index be6e6a63..e58fc416 100644
--- a/third_party/WebKit/Source/core/loader/BUILD.gn
+++ b/third_party/WebKit/Source/core/loader/BUILD.gn
@@ -41,8 +41,6 @@
     "PrerendererClient.cpp",
     "PrerendererClient.h",
     "ProgressTracker.cpp",
-    "SinkDocument.cpp",
-    "SinkDocument.h",
     "TextResourceDecoderBuilder.cpp",
     "TextResourceDecoderBuilder.h",
     "TextTrackLoader.cpp",
diff --git a/third_party/WebKit/Source/core/page/DragController.cpp b/third_party/WebKit/Source/core/page/DragController.cpp
index 3ffdd07b..4622e1d 100644
--- a/third_party/WebKit/Source/core/page/DragController.cpp
+++ b/third_party/WebKit/Source/core/page/DragController.cpp
@@ -572,7 +572,10 @@
     return false;
   }
   Range* range = createRange(dragCaret.toNormalizedEphemeralRange());
-  Element* rootEditableElement = innerFrame->selection().rootEditableElement();
+  Element* rootEditableElement =
+      innerFrame->selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .rootEditableElement();
 
   // For range to be null a WebKit client must have done something bad while
   // manually controlling drag behaviour
@@ -1139,7 +1142,10 @@
   } else if (state.m_dragType == DragSourceActionLink) {
     if (linkURL.isEmpty())
       return false;
-    if (src->selection().isCaret() && src->selection().isContentEditable()) {
+    if (src->selection()
+            .computeVisibleSelectionInDOMTreeDeprecated()
+            .isCaret() &&
+        src->selection().isContentEditable()) {
       // a user can initiate a drag on a link without having any text
       // selected.  In this case, we should expand the selection to
       // the enclosing anchor element
diff --git a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
index 7c21c93..dd8655f5 100644
--- a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
@@ -19,7 +19,8 @@
 void WorkerLoaderProxy::detachProvider(
     WorkerLoaderProxyProvider* proxyProvider) {
   MutexLocker locker(m_lock);
-  DCHECK(proxyProvider == m_loaderProxyProvider);
+  DCHECK(isMainThread());
+  DCHECK_EQ(proxyProvider, m_loaderProxyProvider);
   m_loaderProxyProvider = nullptr;
 }
 
@@ -27,6 +28,7 @@
     const WebTraceLocation& location,
     std::unique_ptr<ExecutionContextTask> task) {
   MutexLocker locker(m_lock);
+  DCHECK(!isMainThread());
   if (!m_loaderProxyProvider)
     return;
   m_loaderProxyProvider->postTaskToLoader(location, std::move(task));
@@ -36,6 +38,7 @@
     const WebTraceLocation& location,
     std::unique_ptr<WTF::CrossThreadClosure> task) {
   MutexLocker locker(m_lock);
+  DCHECK(isMainThread());
   if (!m_loaderProxyProvider)
     return;
   m_loaderProxyProvider->postTaskToWorkerGlobalScope(location, std::move(task));
diff --git a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
index 5951d8b..770719f 100644
--- a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
+++ b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
@@ -62,11 +62,12 @@
   virtual ~WorkerLoaderProxyProvider() {}
 
   // Posts a task to the thread which runs the loading code (normally, the main
-  // thread).
+  // thread). This must be called from a worker thread.
   virtual void postTaskToLoader(const WebTraceLocation&,
                                 std::unique_ptr<ExecutionContextTask>) = 0;
 
-  // Posts callbacks from loading code to the WorkerGlobalScope.
+  // Posts callbacks from loading code to the WorkerGlobalScope. This must be
+  // called from the main thread.
   virtual void postTaskToWorkerGlobalScope(
       const WebTraceLocation&,
       std::unique_ptr<WTF::CrossThreadClosure>) = 0;
@@ -82,14 +83,17 @@
 
   ~WorkerLoaderProxy();
 
+  // This must be called from a worker thread.
   void postTaskToLoader(const WebTraceLocation&,
                         std::unique_ptr<ExecutionContextTask>);
+
+  // This must be called from the main thread.
   void postTaskToWorkerGlobalScope(const WebTraceLocation&,
                                    std::unique_ptr<WTF::CrossThreadClosure>);
 
   // Notification from the provider that it can no longer be accessed. An
   // implementation of WorkerLoaderProxyProvider is required to call
-  // detachProvider() when finalizing.
+  // detachProvider() when finalizing. This must be called from the main thread.
   void detachProvider(WorkerLoaderProxyProvider*);
 
  private:
diff --git a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
index 5c3e3e3..9d8c75e5 100644
--- a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
+++ b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
@@ -35,6 +35,7 @@
 #include "core/dom/DOMArrayBufferView.h"
 #include "core/dom/DOMArrayPiece.h"
 #include "core/dom/ExecutionContext.h"
+#include "core/frame/UseCounter.h"
 #include "modules/crypto/CryptoHistograms.h"
 #include "modules/crypto/CryptoKey.h"
 #include "modules/crypto/CryptoResultImpl.h"
@@ -65,6 +66,12 @@
     return false;
   }
 
+  if (!scriptState->getExecutionContext()->isSecureContext()) {
+    UseCounter::count(
+        scriptState->getExecutionContext(),
+        UseCounter::SubtleCryptoOnlyStrictSecureContextCheckFailed);
+  }
+
   return true;
 }
 
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
index 9afab67..10b97ce 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
@@ -25,9 +25,19 @@
 
 namespace blink {
 
+namespace {
+
+void rejectNavigatorDetached(ScriptPromiseResolver* resolver) {
+  DOMException* exception = DOMException::create(
+      InvalidStateError, "The object is no longer associated with a document.");
+  resolver->reject(exception);
+}
+
+}  // namespace
+
 NavigatorVR* NavigatorVR::from(Document& document) {
   if (!document.frame() || !document.frame()->domWindow())
-    return 0;
+    return nullptr;
   Navigator& navigator = *document.frame()->domWindow()->navigator();
   return &from(navigator);
 }
@@ -44,6 +54,13 @@
 
 ScriptPromise NavigatorVR::getVRDisplays(ScriptState* scriptState,
                                          Navigator& navigator) {
+  if (!navigator.frame()) {
+    ScriptPromiseResolver* resolver =
+        ScriptPromiseResolver::create(scriptState);
+    ScriptPromise promise = resolver->promise();
+    rejectNavigatorDetached(resolver);
+    return promise;
+  }
   return NavigatorVR::from(navigator).getVRDisplays(scriptState);
 }
 
@@ -52,9 +69,7 @@
   ScriptPromise promise = resolver->promise();
 
   if (!document()) {
-    DOMException* exception = DOMException::create(
-        InvalidStateError, "The object is no longer associated to a document.");
-    resolver->reject(exception);
+    rejectNavigatorDetached(resolver);
     return promise;
   }
 
@@ -73,7 +88,7 @@
 
 VRController* NavigatorVR::controller() {
   if (!supplementable()->frame())
-    return 0;
+    return nullptr;
 
   if (!m_controller) {
     m_controller = new VRController(this);
@@ -85,8 +100,10 @@
 }
 
 Document* NavigatorVR::document() {
-  return supplementable()->frame() ? supplementable()->frame()->document()
-                                   : nullptr;
+  if (!supplementable()->frame())
+    return nullptr;
+
+  return supplementable()->frame()->document();
 }
 
 DEFINE_TRACE(NavigatorVR) {
@@ -108,14 +125,16 @@
 }
 
 void NavigatorVR::enqueueVREvent(VRDisplayEvent* event) {
-  if (supplementable()->frame()) {
-    supplementable()->frame()->domWindow()->enqueueWindowEvent(event);
-  }
+  if (!supplementable()->frame())
+    return;
+
+  supplementable()->frame()->domWindow()->enqueueWindowEvent(event);
 }
 
 void NavigatorVR::dispatchVRGestureEvent(VRDisplayEvent* event) {
   if (!(supplementable()->frame()))
     return;
+
   UserGestureIndicator gestureIndicator(
       DocumentUserGestureToken::create(document()));
   LocalDOMWindow* window = supplementable()->frame()->domWindow();
@@ -161,10 +180,11 @@
 }
 
 void NavigatorVR::didRemoveAllEventListeners(LocalDOMWindow* window) {
-  if (m_controller) {
-    m_controller->setListeningForActivate(false);
-    m_listeningForActivate = false;
-  }
+  if (!m_controller)
+    return;
+
+  m_controller->setListeningForActivate(false);
+  m_listeningForActivate = false;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.cpp b/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.cpp
index 04e14ae0..d7160669 100644
--- a/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.cpp
@@ -36,14 +36,10 @@
 
 MediaStreamAudioSourceHandler::MediaStreamAudioSourceHandler(
     AudioNode& node,
-    MediaStream& mediaStream,
-    MediaStreamTrack* audioTrack,
     std::unique_ptr<AudioSourceProvider> audioSourceProvider)
     : AudioHandler(NodeTypeMediaStreamAudioSource,
                    node,
                    node.context()->sampleRate()),
-      m_mediaStream(mediaStream),
-      m_audioTrack(audioTrack),
       m_audioSourceProvider(std::move(audioSourceProvider)),
       m_sourceNumberOfChannels(0) {
   // Default to stereo. This could change depending on the format of the
@@ -55,11 +51,9 @@
 
 PassRefPtr<MediaStreamAudioSourceHandler> MediaStreamAudioSourceHandler::create(
     AudioNode& node,
-    MediaStream& mediaStream,
-    MediaStreamTrack* audioTrack,
     std::unique_ptr<AudioSourceProvider> audioSourceProvider) {
-  return adoptRef(new MediaStreamAudioSourceHandler(
-      node, mediaStream, audioTrack, std::move(audioSourceProvider)));
+  return adoptRef(
+      new MediaStreamAudioSourceHandler(node, std::move(audioSourceProvider)));
 }
 
 MediaStreamAudioSourceHandler::~MediaStreamAudioSourceHandler() {
@@ -104,8 +98,7 @@
     return;
   }
 
-  if (!getMediaStream() ||
-      m_sourceNumberOfChannels != outputBus->numberOfChannels()) {
+  if (m_sourceNumberOfChannels != outputBus->numberOfChannels()) {
     outputBus->zero();
     return;
   }
@@ -129,9 +122,9 @@
     MediaStream& mediaStream,
     MediaStreamTrack* audioTrack,
     std::unique_ptr<AudioSourceProvider> audioSourceProvider)
-    : AudioNode(context) {
+    : AudioNode(context), m_audioTrack(audioTrack), m_mediaStream(mediaStream) {
   setHandler(MediaStreamAudioSourceHandler::create(
-      *this, mediaStream, audioTrack, std::move(audioSourceProvider)));
+      *this, std::move(audioSourceProvider)));
 }
 
 MediaStreamAudioSourceNode* MediaStreamAudioSourceNode::create(
@@ -180,6 +173,8 @@
 }
 
 DEFINE_TRACE(MediaStreamAudioSourceNode) {
+  visitor->trace(m_audioTrack);
+  visitor->trace(m_mediaStream);
   AudioSourceProviderClient::trace(visitor);
   AudioNode::trace(visitor);
 }
@@ -190,7 +185,7 @@
 }
 
 MediaStream* MediaStreamAudioSourceNode::getMediaStream() const {
-  return mediaStreamAudioSourceHandler().getMediaStream();
+  return m_mediaStream;
 }
 
 void MediaStreamAudioSourceNode::setFormat(size_t numberOfChannels,
diff --git a/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.h b/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.h
index a771111..52e245c 100644
--- a/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/MediaStreamAudioSourceNode.h
@@ -43,13 +43,9 @@
  public:
   static PassRefPtr<MediaStreamAudioSourceHandler> create(
       AudioNode&,
-      MediaStream&,
-      MediaStreamTrack*,
       std::unique_ptr<AudioSourceProvider>);
   ~MediaStreamAudioSourceHandler() override;
 
-  MediaStream* getMediaStream() { return m_mediaStream.get(); }
-
   // AudioHandler
   void process(size_t framesToProcess) override;
 
@@ -57,22 +53,17 @@
   // MediaStreamAudioSourceNode.
   void setFormat(size_t numberOfChannels, float sampleRate);
 
+ private:
+  MediaStreamAudioSourceHandler(AudioNode&,
+                                std::unique_ptr<AudioSourceProvider>);
+
+  // As an audio source, we will never propagate silence.
+  bool propagatesSilence() const override { return false; }
+
   AudioSourceProvider* getAudioSourceProvider() const {
     return m_audioSourceProvider.get();
   }
 
- private:
-  MediaStreamAudioSourceHandler(AudioNode&,
-                                MediaStream&,
-                                MediaStreamTrack*,
-                                std::unique_ptr<AudioSourceProvider>);
-  // As an audio source, we will never propagate silence.
-  bool propagatesSilence() const override { return false; }
-
-  // These Persistents don't make reference cycles including the owner
-  // MediaStreamAudioSourceNode.
-  Persistent<MediaStream> m_mediaStream;
-  Persistent<MediaStreamTrack> m_audioTrack;
   std::unique_ptr<AudioSourceProvider> m_audioSourceProvider;
 
   Mutex m_processLock;
@@ -95,7 +86,6 @@
       ExceptionState&);
 
   DECLARE_VIRTUAL_TRACE();
-  MediaStreamAudioSourceHandler& mediaStreamAudioSourceHandler() const;
 
   MediaStream* getMediaStream() const;
 
@@ -107,6 +97,11 @@
                              MediaStream&,
                              MediaStreamTrack*,
                              std::unique_ptr<AudioSourceProvider>);
+
+  MediaStreamAudioSourceHandler& mediaStreamAudioSourceHandler() const;
+
+  Member<MediaStreamTrack> m_audioTrack;
+  Member<MediaStream> m_mediaStream;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index dba0b1d5..925929f 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -558,7 +558,7 @@
   DocumentLifecycle::DisallowTransitionScope disallowTransition(
       localFrame->document()->lifecycle());
 
-  if (selection.isCaret()) {
+  if (selection.computeVisibleSelectionInDOMTreeDeprecated().isCaret()) {
     anchor = focus = selection.absoluteCaretBounds();
   } else {
     const EphemeralRange selectedRange =
@@ -604,7 +604,10 @@
     return false;
   start =
       toWebTextDirection(primaryDirectionOf(*selection.start().anchorNode()));
-  end = toWebTextDirection(primaryDirectionOf(*selection.end().anchorNode()));
+  end = toWebTextDirection(
+      primaryDirectionOf(*selection.computeVisibleSelectionInDOMTreeDeprecated()
+                              .end()
+                              .anchorNode()));
   return true;
 }
 
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index de2132e..89d55678 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1086,7 +1086,11 @@
     return pluginContainer->plugin()->hasSelection();
 
   // frame()->selection()->isNone() never returns true.
-  return frame()->selection().start() != frame()->selection().end();
+  return frame()->selection().start() !=
+         frame()
+             ->selection()
+             .computeVisibleSelectionInDOMTreeDeprecated()
+             .end();
 }
 
 WebRange WebLocalFrameImpl::selectionRange() const {
diff --git a/third_party/WebKit/Source/web/WebRange.cpp b/third_party/WebKit/Source/web/WebRange.cpp
index e3194e3..803d842 100644
--- a/third_party/WebKit/Source/web/WebRange.cpp
+++ b/third_party/WebKit/Source/web/WebRange.cpp
@@ -61,7 +61,9 @@
 }
 
 EphemeralRange WebRange::createEphemeralRange(LocalFrame* frame) const {
-  Element* selectionRoot = frame->selection().rootEditableElement();
+  Element* selectionRoot = frame->selection()
+                               .computeVisibleSelectionInDOMTreeDeprecated()
+                               .rootEditableElement();
   ContainerNode* scope =
       selectionRoot ? selectionRoot : frame->document()->documentElement();
 
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 155c10e..a203cd2 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -2367,7 +2367,7 @@
   DocumentLifecycle::DisallowTransitionScope disallowTransition(
       localFrame->document()->lifecycle());
 
-  if (selection.isCaret()) {
+  if (selection.computeVisibleSelectionInDOMTreeDeprecated().isCaret()) {
     anchor = focus = selection.absoluteCaretBounds();
   } else {
     const EphemeralRange selectedRange =
@@ -2423,7 +2423,10 @@
     return false;
   start =
       toWebTextDirection(primaryDirectionOf(*selection.start().anchorNode()));
-  end = toWebTextDirection(primaryDirectionOf(*selection.end().anchorNode()));
+  end = toWebTextDirection(
+      primaryDirectionOf(*selection.computeVisibleSelectionInDOMTreeDeprecated()
+                              .end()
+                              .anchorNode()));
   return true;
 }
 
diff --git a/third_party/WebKit/Source/web/tests/TextFinderTest.cpp b/third_party/WebKit/Source/web/tests/TextFinderTest.cpp
index 76f22e4..6f23217 100644
--- a/third_party/WebKit/Source/web/tests/TextFinderTest.cpp
+++ b/third_party/WebKit/Source/web/tests/TextFinderTest.cpp
@@ -85,9 +85,9 @@
   Range* activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(4, activeMatch->startOffset());
+  EXPECT_EQ(4u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(10, activeMatch->endOffset());
+  EXPECT_EQ(10u, activeMatch->endOffset());
 
   findOptions.findNext = true;
   ASSERT_TRUE(
@@ -95,9 +95,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(14, activeMatch->startOffset());
+  EXPECT_EQ(14u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(20, activeMatch->endOffset());
+  EXPECT_EQ(20u, activeMatch->endOffset());
 
   // Should wrap to the first match.
   ASSERT_TRUE(
@@ -105,9 +105,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(4, activeMatch->startOffset());
+  EXPECT_EQ(4u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(10, activeMatch->endOffset());
+  EXPECT_EQ(10u, activeMatch->endOffset());
 
   // Search in the reverse order.
   identifier = 1;
@@ -119,9 +119,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(14, activeMatch->startOffset());
+  EXPECT_EQ(14u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(20, activeMatch->endOffset());
+  EXPECT_EQ(20u, activeMatch->endOffset());
 
   findOptions.findNext = true;
   ASSERT_TRUE(
@@ -129,9 +129,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(4, activeMatch->startOffset());
+  EXPECT_EQ(4u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(10, activeMatch->endOffset());
+  EXPECT_EQ(10u, activeMatch->endOffset());
 
   // Wrap to the first match (last occurence in the document).
   ASSERT_TRUE(
@@ -139,9 +139,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textNode, activeMatch->startContainer());
-  EXPECT_EQ(14, activeMatch->startOffset());
+  EXPECT_EQ(14u, activeMatch->startOffset());
   EXPECT_EQ(textNode, activeMatch->endContainer());
-  EXPECT_EQ(20, activeMatch->endOffset());
+  EXPECT_EQ(20u, activeMatch->endOffset());
 }
 
 TEST_F(TextFinderTest, FindTextAutosizing) {
@@ -220,9 +220,9 @@
   Range* activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInIElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInIElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   findOptions.findNext = true;
   ASSERT_TRUE(
@@ -230,18 +230,18 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInUElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInUElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   ASSERT_TRUE(
       textFinder().find(identifier, searchText, findOptions, wrapWithinFrame));
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInBElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInBElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   // Should wrap to the first match.
   ASSERT_TRUE(
@@ -249,9 +249,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInIElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInIElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   // Fresh search in the reverse order.
   identifier = 1;
@@ -263,9 +263,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInBElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInBElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   findOptions.findNext = true;
   ASSERT_TRUE(
@@ -273,18 +273,18 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInUElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInUElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   ASSERT_TRUE(
       textFinder().find(identifier, searchText, findOptions, wrapWithinFrame));
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInIElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInIElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 
   // And wrap.
   ASSERT_TRUE(
@@ -292,9 +292,9 @@
   activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_EQ(textInBElement, activeMatch->startContainer());
-  EXPECT_EQ(0, activeMatch->startOffset());
+  EXPECT_EQ(0u, activeMatch->startOffset());
   EXPECT_EQ(textInBElement, activeMatch->endContainer());
-  EXPECT_EQ(3, activeMatch->endOffset());
+  EXPECT_EQ(3u, activeMatch->endOffset());
 }
 
 TEST_F(TextFinderTest, ScopeTextMatchesSimple) {
@@ -485,8 +485,8 @@
   Range* activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_FALSE(activeNow);
-  EXPECT_EQ(2, activeMatch->startOffset());
-  EXPECT_EQ(8, activeMatch->endOffset());
+  EXPECT_EQ(2u, activeMatch->startOffset());
+  EXPECT_EQ(8u, activeMatch->endOffset());
 
   // Restart full search and check that added text is found.
   findOptions.findNext = false;
@@ -539,8 +539,8 @@
   Range* activeMatch = textFinder().activeMatch();
   ASSERT_TRUE(activeMatch);
   EXPECT_FALSE(activeNow);
-  EXPECT_EQ(2, activeMatch->startOffset());
-  EXPECT_EQ(8, activeMatch->endOffset());
+  EXPECT_EQ(2u, activeMatch->startOffset());
+  EXPECT_EQ(8u, activeMatch->endOffset());
 
   // Restart full search and check that added text is found.
   findOptions.findNext = false;
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index ad05184..5cf3d88 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -5452,9 +5452,18 @@
   EXPECT_EQ("Length", selectionAsString(frame));
   webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
 
-  EXPECT_EQ(0, frame->frame()->selection().rootEditableElement()->scrollLeft());
+  EXPECT_EQ(0, frame->frame()
+                   ->selection()
+                   .computeVisibleSelectionInDOMTreeDeprecated()
+                   .rootEditableElement()
+                   ->scrollLeft());
   frame->moveRangeSelectionExtent(WebPoint(endWebRect.x + 500, endWebRect.y));
-  EXPECT_GE(frame->frame()->selection().rootEditableElement()->scrollLeft(), 1);
+  EXPECT_GE(frame->frame()
+                ->selection()
+                .computeVisibleSelectionInDOMTreeDeprecated()
+                .rootEditableElement()
+                ->scrollLeft(),
+            1);
   EXPECT_EQ("Lengthy text goes here.", selectionAsString(frame));
 }
 
@@ -5473,8 +5482,11 @@
   initializeTextSelectionWebView(m_baseURL + "select_range_span_editable.html",
                                  &webViewHelper);
   WebLocalFrameImpl* mainFrame = webViewHelper.webView()->mainFrameImpl();
-  LayoutObject* layoutObject =
-      mainFrame->frame()->selection().rootEditableElement()->layoutObject();
+  LayoutObject* layoutObject = mainFrame->frame()
+                                   ->selection()
+                                   .computeVisibleSelectionInDOMTreeDeprecated()
+                                   .rootEditableElement()
+                                   ->layoutObject();
   EXPECT_EQ(0, computeOffset(layoutObject, -1, -1));
   EXPECT_EQ(64, computeOffset(layoutObject, 1000, 1000));
 
@@ -5482,8 +5494,11 @@
   initializeTextSelectionWebView(m_baseURL + "select_range_div_editable.html",
                                  &webViewHelper);
   mainFrame = webViewHelper.webView()->mainFrameImpl();
-  layoutObject =
-      mainFrame->frame()->selection().rootEditableElement()->layoutObject();
+  layoutObject = mainFrame->frame()
+                     ->selection()
+                     .computeVisibleSelectionInDOMTreeDeprecated()
+                     .rootEditableElement()
+                     ->layoutObject();
   EXPECT_EQ(0, computeOffset(layoutObject, -1, -1));
   EXPECT_EQ(64, computeOffset(layoutObject, 1000, 1000));
 }
diff --git a/tools/idl_parser/idl_lexer.py b/tools/idl_parser/idl_lexer.py
index 1f186d72..a5fc49c3 100755
--- a/tools/idl_parser/idl_lexer.py
+++ b/tools/idl_parser/idl_lexer.py
@@ -17,20 +17,10 @@
 import os.path
 import sys
 
-#
-# Try to load the ply module, if not, then assume it is in the third_party
-# directory.
-#
-try:
-  # Disable lint check which fails to find the ply module.
-  # pylint: disable=F0401
-  from ply import lex
-except ImportError:
-  module_path, module_name = os.path.split(__file__)
-  third_party = os.path.join(module_path, '..', '..', 'third_party')
-  sys.path.append(third_party)
-  # pylint: disable=F0401
-  from ply import lex
+SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
+sys.path.insert(0, os.path.join(SRC_DIR, 'third_party'))
+from ply import lex
+
 
 #
 # IDL Lexer
diff --git a/tools/idl_parser/idl_parser.py b/tools/idl_parser/idl_parser.py
index b1e74c7..d7a00aa 100755
--- a/tools/idl_parser/idl_parser.py
+++ b/tools/idl_parser/idl_parser.py
@@ -36,22 +36,11 @@
 from idl_lexer import IDLLexer
 from idl_node import IDLAttribute, IDLNode
 
-#
-# Try to load the ply module, if not, then assume it is in the third_party
-# directory.
-#
-try:
-  # Disable lint check which fails to find the ply module.
-  # pylint: disable=F0401
-  from ply import lex
-  from ply import yacc
-except ImportError:
-  module_path, module_name = os.path.split(__file__)
-  third_party = os.path.join(module_path, os.par, os.par, 'third_party')
-  sys.path.append(third_party)
-  # pylint: disable=F0401
-  from ply import lex
-  from ply import yacc
+SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
+sys.path.insert(0, os.path.join(SRC_DIR, 'third_party'))
+from ply import lex
+from ply import yacc
+
 
 #
 # ERROR_REMAP
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 242b9f62..48e0bb9b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -91450,6 +91450,7 @@
   <int value="1826" label="FetchResponseConstructionWithStream"/>
   <int value="1827" label="LocationOrigin"/>
   <int value="1828" label="DocumentOrigin"/>
+  <int value="1829" label="SubtleCryptoOnlyStrictSecureContextCheckFailed"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">