Reland "Hook up running extension SW information to ProcessManager"

This is a reland of 7466707780d996e09adff361f84c0ae0add4602c
The only change is to wait for "activate" SW event in tests
instead of "install" event. This fixes the previous test
flakiness locally.

Original change's description:
> Hook up running extension SW information to ProcessManager
>
> When service worker context initializes, add the corresponding
> entry to ProcessManager and remove it when the context stops.
>
> Introduce IPC for worker initialization
> (DidInitializeServiceWorkerContext) and modify worker
> stop IPC (DidStopServiceWorkerContext) as necessary.
>
> Note that we cannot use loadstop
> (DidStartServiceWorkerContext) for PM entry addition because it
> can be too late as extension API calls might arrive in browser
> process before this.
>
> Also add SW information cleanup on render process termination as
> termination doesn't trigger stop worker messages (where the
> regular cleanup in PM happens).
>
> This CL will enable extension messaging in future CLs.
>
> Add tests for shutdown and termination behavior.
>
> Bug: 925918
> Change-Id: I9442bac9a1e9acf01063084a1e6ef2fac3279e09
> Reviewed-on: https://chromium-review.googlesource.com/c/1455267
> Reviewed-by: Dominick Ng <dominickn@chromium.org>
> Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
> Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#636929}

TBR=rdevlin.cronin@chromium.org,dominickn@chromium.org

Bug: 925918
Change-Id: I148fe7ddce1b66b934eec20536172cbb94b27540
Reviewed-on: https://chromium-review.googlesource.com/c/1497862
Reviewed-by: Istiaque Ahmed <lazyboy@chromium.org>
Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#637118}
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index 8a0d4f3..f7969ce 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -35,12 +36,14 @@
 #include "components/version_info/version_info.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
 #include "content/public/common/page_type.h"
+#include "content/public/common/result_codes.h"
 #include "content/public/test/background_sync_test_util.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/service_worker_test_helpers.h"
@@ -246,6 +249,25 @@
     ServiceWorkerTest::SetUpOnMainThread();
   }
 
+  // Returns the only running worker id for |extension_id|.
+  // Returns base::nullopt if there isn't any worker running or more than one
+  // worker is running for |extension_id|.
+  base::Optional<WorkerId> GetUniqueRunningWorkerId(
+      const ExtensionId& extension_id) {
+    ProcessManager* process_manager = ProcessManager::Get(profile());
+    std::vector<WorkerId> all_workers =
+        process_manager->GetAllWorkersIdsForTesting();
+    base::Optional<WorkerId> running_worker_id;
+    for (const WorkerId& worker_id : all_workers) {
+      if (worker_id.extension_id == extension_id) {
+        if (running_worker_id)  // More than one worker present.
+          return base::nullopt;
+        running_worker_id = worker_id;
+      }
+    }
+    return running_worker_id;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerBasedBackgroundTest);
 };
@@ -1563,6 +1585,68 @@
   EXPECT_TRUE(worker_filtered_event_listener.WaitUntilSatisfied());
 }
 
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBasedBackgroundTest,
+                       ProcessManagerRegistrationOnShutdown) {
+  // Note that StopServiceWorkerForScope call below expects the worker to be
+  // completely installed, so wait for the |extension| worker to see "activate"
+  // event.
+  ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED", false);
+  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
+      "service_worker/worker_based_background/process_manager"));
+  ASSERT_TRUE(extension);
+  EXPECT_TRUE(activated_listener.WaitUntilSatisfied());
+
+  base::Optional<WorkerId> worker_id =
+      GetUniqueRunningWorkerId(extension->id());
+  ASSERT_TRUE(worker_id);
+  {
+    // Shutdown the worker.
+    // TODO(lazyboy): Ideally we'd want to test worker shutdown on idle, do that
+    // once //content API allows to override test timeouts for Service Workers.
+    base::RunLoop run_loop;
+    content::StoragePartition* storage_partition =
+        content::BrowserContext::GetDefaultStoragePartition(
+            browser()->profile());
+    GURL scope = extension->url();
+    content::StopServiceWorkerForScope(
+        storage_partition->GetServiceWorkerContext(),
+        // The service worker is registered at the top level scope.
+        extension->url(), run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  EXPECT_FALSE(ProcessManager::Get(profile())->HasServiceWorker(*worker_id));
+}
+
+IN_PROC_BROWSER_TEST_P(ServiceWorkerBasedBackgroundTest,
+                       ProcessManagerRegistrationOnTerminate) {
+  // NOTE: It is not necessary to wait for "activate" event from the worker
+  // for this test, but we're lazily reusing the extension from
+  // ProcessManagerRegistrationOnShutdown test.
+  ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED", false);
+  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
+      "service_worker/worker_based_background/process_manager"));
+  ASSERT_TRUE(extension);
+  EXPECT_TRUE(activated_listener.WaitUntilSatisfied());
+
+  base::Optional<WorkerId> worker_id =
+      GetUniqueRunningWorkerId(extension->id());
+  ASSERT_TRUE(worker_id);
+  {
+    // Terminate worker's RenderProcessHost.
+    content::RenderProcessHost* worker_render_process_host =
+        content::RenderProcessHost::FromID(worker_id->render_process_id);
+    ASSERT_TRUE(worker_render_process_host);
+    content::RenderProcessHostWatcher process_exit_observer(
+        worker_render_process_host,
+        content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+    worker_render_process_host->Shutdown(content::RESULT_CODE_KILLED);
+    process_exit_observer.Wait();
+  }
+
+  EXPECT_FALSE(ProcessManager::Get(profile())->HasServiceWorker(*worker_id));
+}
+
 // Run with both native and JS-based bindings. This ensures that both stable
 // (JS) and experimental (native) phases work correctly with worker scripts
 // while we launch native bindings to stable.
diff --git a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json
new file mode 100644
index 0000000..a1699a6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json
@@ -0,0 +1,7 @@
+{
+  "name": "Service Worker based background script",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Tests launching ProcessManager behavior on worker shutdown",
+  "background": {"service_worker_script": "service_worker_background.js"}
+}
diff --git a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js
new file mode 100644
index 0000000..ab535ee
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js
@@ -0,0 +1,11 @@
+// Copyright 2019 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.
+
+// browser_test waits for worker activation to complete before it shuts down
+// this worker.  //content API (StopServiceWorkerForScope) used for shutdown
+// DCHECKs if the worker hasn't finished installation.
+this.addEventListener('activate', function(e) {
+  console.log('activate');
+  chrome.test.sendMessage('WORKER_ACTIVATED');
+});
diff --git a/extensions/browser/extension_service_worker_message_filter.cc b/extensions/browser/extension_service_worker_message_filter.cc
index 58d237e..59c4915 100644
--- a/extensions/browser/extension_service_worker_message_filter.cc
+++ b/extensions/browser/extension_service_worker_message_filter.cc
@@ -34,6 +34,8 @@
     content::BrowserThread::ID* thread) {
   if (message.type() == ExtensionHostMsg_RequestWorker::ID ||
       message.type() == ExtensionHostMsg_EventAckWorker::ID ||
+      message.type() ==
+          ExtensionHostMsg_DidInitializeServiceWorkerContext::ID ||
       message.type() == ExtensionHostMsg_DidStartServiceWorkerContext::ID ||
       message.type() == ExtensionHostMsg_DidStopServiceWorkerContext::ID) {
     *thread = content::BrowserThread::UI;
@@ -50,6 +52,8 @@
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementServiceWorkerActivity,
                         OnDecrementServiceWorkerActivity)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAckWorker, OnEventAckWorker)
+    IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidInitializeServiceWorkerContext,
+                        OnDidInitializeServiceWorkerContext)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStartServiceWorkerContext,
                         OnDidStartServiceWorkerContext)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStopServiceWorkerContext,
@@ -102,8 +106,22 @@
                          this));
 }
 
+void ExtensionServiceWorkerMessageFilter::OnDidInitializeServiceWorkerContext(
+    const ExtensionId& extension_id,
+    int64_t service_worker_version_id,
+    int thread_id) {
+  if (!ProcessMap::Get(browser_context_)
+           ->Contains(extension_id, render_process_id_)) {
+    // We can legitimately get here if the extension was already unloaded.
+    return;
+  }
+  ServiceWorkerTaskQueue::Get(browser_context_)
+      ->DidInitializeServiceWorkerContext(render_process_id_, extension_id,
+                                          service_worker_version_id, thread_id);
+}
+
 void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
-    const std::string& extension_id,
+    const ExtensionId& extension_id,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
@@ -124,7 +142,7 @@
 }
 
 void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
-    const std::string& extension_id,
+    const ExtensionId& extension_id,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
diff --git a/extensions/browser/extension_service_worker_message_filter.h b/extensions/browser/extension_service_worker_message_filter.h
index 3790b69..6976e49 100644
--- a/extensions/browser/extension_service_worker_message_filter.h
+++ b/extensions/browser/extension_service_worker_message_filter.h
@@ -46,6 +46,9 @@
   void OnDecrementServiceWorkerActivity(int64_t service_worker_version_id,
                                         const std::string& request_uuid);
   void OnEventAckWorker(int64_t service_worker_version_id, int event_id);
+  void OnDidInitializeServiceWorkerContext(const ExtensionId& extension_id,
+                                           int64_t service_worker_version_id,
+                                           int thread_id);
   void OnDidStartServiceWorkerContext(const ExtensionId& extension_id,
                                       const GURL& service_worker_scope,
                                       int64_t service_worker_version_id,
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc
index 874c6cf..14fa00f 100644
--- a/extensions/browser/process_manager.cc
+++ b/extensions/browser/process_manager.cc
@@ -259,6 +259,7 @@
           {content::BrowserThread::IO})),
       startup_background_hosts_created_(false),
       last_background_close_sequence_id_(0),
+      process_observer_(this),
       weak_ptr_factory_(this) {
   // ExtensionRegistry is shared between incognito and regular contexts.
   DCHECK_EQ(original_context, extension_registry_->browser_context());
@@ -968,9 +969,49 @@
 
 void ProcessManager::RegisterServiceWorker(const WorkerId& worker_id) {
   all_extension_workers_.Add(worker_id);
+
+  // Observe the RenderProcessHost for cleaning up on process shutdown.
+  int render_process_id = worker_id.render_process_id;
+  bool inserted = worker_process_to_extension_ids_[render_process_id]
+                      .insert(worker_id.extension_id)
+                      .second;
+  if (inserted) {
+    content::RenderProcessHost* render_process_host =
+        content::RenderProcessHost::FromID(render_process_id);
+    DCHECK(render_process_host);
+    if (!process_observer_.IsObserving(render_process_host)) {
+      // These will be cleaned up in RenderProcessExited().
+      process_observer_.Add(render_process_host);
+    }
+  }
+}
+
+void ProcessManager::RenderProcessExited(
+    content::RenderProcessHost* host,
+    const content::ChildProcessTerminationInfo& info) {
+  DCHECK(process_observer_.IsObserving(host));
+  process_observer_.Remove(host);
+  const int render_process_id = host->GetID();
+  // Look up and then clean up the entries that are affected by
+  // |render_process_id| destruction.
+  //
+  // TODO(lazyboy): Revisit this once incognito is tested for extension SWs, as
+  // the cleanup below only works because regular and OTR ProcessManagers are
+  // separate. The conclusive approach would be to have a
+  // all_extension_workers_.RemoveAllForProcess(render_process_id) method:
+  //   Pros: We won't need worker_process_to_extension_ids_ anymore.
+  //   Cons: We would require traversing all workers within
+  //         |all_extension_workers_| (slow) as things stand right now.
+  auto iter = worker_process_to_extension_ids_.find(render_process_id);
+  if (iter == worker_process_to_extension_ids_.end())
+    return;
+  for (const ExtensionId& extension_id : iter->second)
+    all_extension_workers_.RemoveAllForExtension(extension_id);
+  worker_process_to_extension_ids_.erase(iter);
 }
 
 void ProcessManager::UnregisterServiceWorker(const WorkerId& worker_id) {
+  // TODO(lazyboy): DCHECK that |worker_id| exists in |all_extension_workers_|.
   all_extension_workers_.Remove(worker_id);
 }
 
@@ -985,6 +1026,10 @@
                                                    render_process_id);
 }
 
+std::vector<WorkerId> ProcessManager::GetAllWorkersIdsForTesting() {
+  return all_extension_workers_.GetAllForTesting();
+}
+
 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
   background_page_data_.erase(extension_id);
 
diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h
index a1a3841..463d557 100644
--- a/extensions/browser/process_manager.h
+++ b/extensions/browser/process_manager.h
@@ -19,10 +19,12 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_process_host_observer.h"
 #include "extensions/browser/activity.h"
 #include "extensions/browser/event_page_tracker.h"
 #include "extensions/browser/extension_registry_observer.h"
@@ -55,7 +57,8 @@
                        public content::NotificationObserver,
                        public ExtensionRegistryObserver,
                        public EventPageTracker,
-                       public content::DevToolsAgentHostObserver {
+                       public content::DevToolsAgentHostObserver,
+                       public content::RenderProcessHostObserver {
  public:
   using ExtensionHostSet = std::set<extensions::ExtensionHost*>;
 
@@ -72,7 +75,6 @@
 
   // Registers or unregisters a running worker state to this process manager.
   // Note: This does not create any Service Workers.
-  // TODO(lazyboy): Hook this up to ServiceWorkerTaskQueue class.
   void RegisterServiceWorker(const WorkerId& worker_id);
   void UnregisterServiceWorker(const WorkerId& worker_id);
 
@@ -235,6 +237,8 @@
     return startup_background_hosts_created_;
   }
 
+  std::vector<WorkerId> GetAllWorkersIdsForTesting();
+
  protected:
   static ProcessManager* Create(content::BrowserContext* context);
 
@@ -264,6 +268,11 @@
                            const Extension* extension,
                            UnloadedExtensionReason reason) override;
 
+  // content::RenderProcessHostObserver:
+  void RenderProcessExited(
+      content::RenderProcessHost* host,
+      const content::ChildProcessTerminationInfo& info) override;
+
   // Extra information we keep for each extension's background page.
   struct BackgroundPageData;
   struct ExtensionRenderFrameData;
@@ -376,6 +385,13 @@
   // start notification. In that case we want to avoid decrementing keepalive.
   std::map<int, ExtensionHost*> pending_network_requests_;
 
+  // Observers of Service Worker RPH this ProcessManager manages.
+  ScopedObserver<content::RenderProcessHost, content::RenderProcessHostObserver>
+      process_observer_;
+  // Maps render render_process_id -> extension_id for all Service Workers this
+  // ProcessManager manages.
+  std::map<int, std::set<ExtensionId>> worker_process_to_extension_ids_;
+
   // Must be last member, see doc on WeakPtrFactory.
   base::WeakPtrFactory<ProcessManager> weak_ptr_factory_;
 
diff --git a/extensions/browser/service_worker/worker_id_set.cc b/extensions/browser/service_worker/worker_id_set.cc
index d417a3a..311d138 100644
--- a/extensions/browser/service_worker/worker_id_set.cc
+++ b/extensions/browser/service_worker/worker_id_set.cc
@@ -91,4 +91,8 @@
   return std::vector<WorkerId>(begin_range, end_range);
 }
 
+std::vector<WorkerId> WorkerIdSet::GetAllForTesting() const {
+  return std::vector<WorkerId>(workers_.begin(), workers_.end());
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/service_worker/worker_id_set.h b/extensions/browser/service_worker/worker_id_set.h
index 7a050ff..4be2fd0 100644
--- a/extensions/browser/service_worker/worker_id_set.h
+++ b/extensions/browser/service_worker/worker_id_set.h
@@ -28,6 +28,7 @@
   std::vector<WorkerId> GetAllForExtension(const ExtensionId& extension_id,
                                            int render_process_id) const;
 
+  std::vector<WorkerId> GetAllForTesting() const;
   size_t count_for_testing() const { return workers_.size(); }
 
  private:
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc
index f7cc7fb..051d4c2 100644
--- a/extensions/browser/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker_task_queue.cc
@@ -20,6 +20,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/lazy_context_id.h"
+#include "extensions/browser/process_manager.h"
 #include "extensions/browser/service_worker_task_queue_factory.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/manifest_handlers/background_info.h"
@@ -95,6 +96,17 @@
   RunPendingTasksIfWorkerReady(context_id, version_id, process_id, thread_id);
 }
 
+void ServiceWorkerTaskQueue::DidInitializeServiceWorkerContext(
+    int render_process_id,
+    const ExtensionId& extension_id,
+    int64_t service_worker_version_id,
+    int thread_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  ProcessManager::Get(browser_context_)
+      ->RegisterServiceWorker({extension_id, render_process_id,
+                               service_worker_version_id, thread_id});
+}
+
 void ServiceWorkerTaskQueue::DidStartServiceWorkerContext(
     int render_process_id,
     const ExtensionId& extension_id,
@@ -116,6 +128,9 @@
     int64_t service_worker_version_id,
     int thread_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  ProcessManager::Get(browser_context_)
+      ->UnregisterServiceWorker({extension_id, render_process_id,
+                                 service_worker_version_id, thread_id});
   LazyContextId context_id(browser_context_, extension_id,
                            service_worker_scope);
   // TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
diff --git a/extensions/browser/service_worker_task_queue.h b/extensions/browser/service_worker_task_queue.h
index dafa067e..4b423beb 100644
--- a/extensions/browser/service_worker_task_queue.h
+++ b/extensions/browser/service_worker_task_queue.h
@@ -52,6 +52,12 @@
   // e.g. unregistering |extension|'s worker.
   void DeactivateExtension(const Extension* extension);
 
+  // Called once an extension Service Worker context was initialized but not
+  // necessarily started executing its JavaScript.
+  void DidInitializeServiceWorkerContext(int render_process_id,
+                                         const ExtensionId& extension_id,
+                                         int64_t service_worker_version_id,
+                                         int thread_id);
   // Called once an extension Service Worker started running.
   // This can be thought as "loadstop", i.e. the global JS script of the worker
   // has completed executing.
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index a372dde..dd37b8c 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -1039,6 +1039,13 @@
                      int64_t /* service_worker_version_id */,
                      int /* event_id */)
 
+// Tells the browser that an extension service worker context was initialized,
+// but possibly didn't start executing its top-level JavaScript.
+IPC_MESSAGE_CONTROL3(ExtensionHostMsg_DidInitializeServiceWorkerContext,
+                     std::string /* extension_id */,
+                     int64_t /* service_worker_version_id */,
+                     int /* worker_thread_id */)
+
 // Tells the browser that an extension service worker context has started and
 // finished executing its top-level JavaScript.
 // Start corresponds to EmbeddedWorkerInstance::OnStarted notification.
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index f5b02e2..254813d 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -423,6 +423,8 @@
     // TODO(lazyboy): Get rid of RequireGuestViewModules() as this doesn't seem
     // necessary for Extension SW.
     RequireGuestViewModules(context);
+
+    worker_dispatcher->DidInitializeContext(service_worker_version_id);
   }
 
   g_worker_script_context_set.Get().Insert(base::WrapUnique(context));
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index de817f0..9d830fe 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -168,6 +168,16 @@
   }
 }
 
+void WorkerThreadDispatcher::DidInitializeContext(
+    int64_t service_worker_version_id) {
+  ServiceWorkerData* data = g_data_tls.Pointer()->Get();
+  DCHECK_EQ(service_worker_version_id, data->service_worker_version_id());
+  const int thread_id = content::WorkerThread::GetCurrentId();
+  DCHECK_NE(thread_id, kMainThreadId);
+  Send(new ExtensionHostMsg_DidInitializeServiceWorkerContext(
+      data->context()->GetExtensionID(), service_worker_version_id, thread_id));
+}
+
 void WorkerThreadDispatcher::DidStartContext(
     const GURL& service_worker_scope,
     int64_t service_worker_version_id) {
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h
index f092e4d..de3ae75 100644
--- a/extensions/renderer/worker_thread_dispatcher.h
+++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -56,6 +56,9 @@
 
   EventBookkeeper* event_bookkeeper() { return &event_bookkeeper_; }
 
+  // Called when a service worker context was initialized.
+  void DidInitializeContext(int64_t service_worker_version_id);
+
   // Called when a service worker context started running.
   void DidStartContext(const GURL& service_worker_scope,
                        int64_t service_worker_version_id);