WebContentsObserver::OnServiceWorkerAccessed

Instead of passing WebContentsGetter to ContentBrowserClient, add explicit
callbacks to WebContentsObserver taking RenderFrameHost* or NavigationHandle*
as a parameter.

This allows to attribute cookie/javascript accesses to the correct
frame or navigation request, which is a prerequisite for attributing
content settings to the correct document and making them per-document.

[email protected],[email protected]
BUG=998171,1061899

Change-Id: Iec55a1db235b26bab44f1c1de76900b73ac6614d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151866
Commit-Queue: Alexander Timin <[email protected]>
Reviewed-by: Balazs Engedy <[email protected]>
Reviewed-by: Matt Falkenhagen <[email protected]>
Cr-Commit-Position: refs/heads/master@{#762018}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 691f3e2..59965808 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1663,6 +1663,7 @@
     "service_worker/embedded_worker_status.h",
     "service_worker/payment_handler_support.cc",
     "service_worker/payment_handler_support.h",
+    "service_worker/service_worker_accessed_callback.h",
     "service_worker/service_worker_cache_writer.cc",
     "service_worker/service_worker_cache_writer.h",
     "service_worker/service_worker_client_info.cc",
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 38f5ab2..fe04fabb 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -2258,6 +2258,19 @@
   frame_tree_node_->navigator()->LogResourceRequestTime(timestamp,
                                                         common_params_->url);
 }
+namespace {
+
+void OnServiceWorkerAccessedThreadSafeWrapper(
+    base::WeakPtr<NavigationRequest> navigation,
+    const GURL& scope,
+    AllowServiceWorkerResult allowed) {
+  RunOrPostTaskOnThread(
+      FROM_HERE, BrowserThread::UI,
+      base::BindOnce(&NavigationRequest::OnServiceWorkerAccessed, navigation,
+                     scope, allowed));
+}
+
+}  // namespace
 
 void NavigationRequest::OnStartChecksComplete(
     NavigationThrottle::ThrottleCheckResult result) {
@@ -2336,7 +2349,9 @@
         static_cast<ServiceWorkerContextWrapper*>(
             partition->GetServiceWorkerContext());
     service_worker_handle_ = std::make_unique<ServiceWorkerMainResourceHandle>(
-        service_worker_context);
+        service_worker_context,
+        base::BindRepeating(&OnServiceWorkerAccessedThreadSafeWrapper,
+                            weak_factory_.GetWeakPtr()));
   }
 
   if (IsSchemeSupportedForAppCache(common_params_->url)) {
@@ -2443,6 +2458,12 @@
   DCHECK(!render_frame_host_);
 }
 
+void NavigationRequest::OnServiceWorkerAccessed(
+    const GURL& scope,
+    AllowServiceWorkerResult allowed) {
+  GetDelegate()->OnServiceWorkerAccessed(this, scope, allowed);
+}
+
 void NavigationRequest::OnRedirectChecksComplete(
     NavigationThrottle::ThrottleCheckResult result) {
   DCHECK(result.action() != NavigationThrottle::DEFER);
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 218ab3d1..9bf8cc97 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -27,6 +27,7 @@
 #include "content/common/content_export.h"
 #include "content/common/navigation_params.h"
 #include "content/common/navigation_params.mojom.h"
+#include "content/public/browser/allow_service_worker_result.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
@@ -574,6 +575,9 @@
   //     ->GetPageUkmSourceId() for main-frame cross-document navigations.
   ukm::SourceId GetPreviousPageUkmSourceId();
 
+  void OnServiceWorkerAccessed(const GURL& scope,
+                               AllowServiceWorkerResult allowed);
+
  private:
   friend class NavigationRequestTest;
 
diff --git a/content/browser/frame_host/navigator_delegate.h b/content/browser/frame_host/navigator_delegate.h
index f036fe2db..d34fcef 100644
--- a/content/browser/frame_host/navigator_delegate.h
+++ b/content/browser/frame_host/navigator_delegate.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_DELEGATE_H_
 
 #include "base/strings/string16.h"
+#include "content/public/browser/allow_service_worker_result.h"
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_throttle.h"
@@ -127,6 +128,12 @@
   // embedder and |nullptr| is returned.
   virtual std::unique_ptr<NavigationUIData> GetNavigationUIData(
       NavigationHandle* navigation_handle);
+
+  // Called when a navigation accessed ServiceWorker to check if it should be
+  // handled by the ServiceWorker or not.
+  virtual void OnServiceWorkerAccessed(NavigationHandle* navigation,
+                                       const GURL& scope,
+                                       AllowServiceWorkerResult allowed) {}
 };
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_accessed_callback.h b/content/browser/service_worker/service_worker_accessed_callback.h
new file mode 100644
index 0000000..3c58a8be
--- /dev/null
+++ b/content/browser/service_worker/service_worker_accessed_callback.h
@@ -0,0 +1,22 @@
+// Copyright 2020 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_BROWSER_SERVICE_WORKER_SERVICE_WORKER_ACCESSED_CALLBACK_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_ACCESSED_CALLBACK_H_
+
+#include "base/callback.h"
+#include "content/public/browser/allow_service_worker_result.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// This callback is used by a few places in the code but these places don't
+// share a header which they all include, so this definition has to placed
+// in a dedicated header.
+using ServiceWorkerAccessedCallback =
+    base::RepeatingCallback<void(const GURL&, AllowServiceWorkerResult)>;
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_ACCESSED_CALLBACK_H_
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index 634f0ff..05813fd 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -951,31 +951,40 @@
   return remote_controller;
 }
 
+namespace {
+
+void ReportServiceWorkerAccess(int process_id,
+                               int frame_id,
+                               const GURL& scope,
+                               AllowServiceWorkerResult allowed) {
+  RenderFrameHost* rfh = RenderFrameHost::FromID(process_id, frame_id);
+  if (!rfh)
+    return;
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh));
+  web_contents->OnServiceWorkerAccessed(rfh, scope, allowed);
+}
+
+}  // namespace
+
 bool ServiceWorkerContainerHost::AllowServiceWorker(const GURL& scope,
                                                     const GURL& script_url) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(context_);
+  AllowServiceWorkerResult allowed = AllowServiceWorkerResult::No();
   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
-    return GetContentClient()->browser()->AllowServiceWorkerOnUI(
+    allowed = GetContentClient()->browser()->AllowServiceWorkerOnUI(
         scope, site_for_cookies().RepresentativeUrl(), top_frame_origin(),
-        script_url, context_->wrapper()->browser_context(),
-        base::BindRepeating(
-            [](int process_id, int frame_id) {
-              return WebContentsImpl::FromRenderFrameHostID(process_id,
-                                                            frame_id);
-            },
-            process_id_, frame_id_));
+        script_url, context_->wrapper()->browser_context());
   } else {
-    return GetContentClient()->browser()->AllowServiceWorkerOnIO(
+    allowed = GetContentClient()->browser()->AllowServiceWorkerOnIO(
         scope, site_for_cookies().RepresentativeUrl(), top_frame_origin(),
-        script_url, context_->wrapper()->resource_context(),
-        base::BindRepeating(
-            [](int process_id, int frame_id) {
-              return WebContentsImpl::FromRenderFrameHostID(process_id,
-                                                            frame_id);
-            },
-            process_id_, frame_id_));
+        script_url, context_->wrapper()->resource_context());
   }
+  RunOrPostTaskOnThread(FROM_HERE, BrowserThread::UI,
+                        base::BindOnce(&ReportServiceWorkerAccess, process_id_,
+                                       frame_id_, scope, allowed));
+  return allowed;
 }
 
 bool ServiceWorkerContainerHost::IsContextSecureForServiceWorker() const {
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 397b828b..679d926 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -21,6 +21,8 @@
 #include "content/browser/service_worker/service_worker_object_host.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/common/service_worker/service_worker_utils.h"
+#include "content/public/browser/allow_service_worker_result.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -67,12 +69,15 @@
     base::WeakPtr<ServiceWorkerContextCore> context,
     base::WeakPtr<ServiceWorkerContainerHost> container_host,
     blink::mojom::ResourceType resource_type,
-    bool skip_service_worker)
+    bool skip_service_worker,
+    ServiceWorkerAccessedCallback service_worker_accessed_callback)
     : context_(std::move(context)),
       container_host_(std::move(container_host)),
       resource_type_(resource_type),
       skip_service_worker_(skip_service_worker),
-      force_update_started_(false) {
+      force_update_started_(false),
+      service_worker_accessed_callback_(
+          std::move(service_worker_accessed_callback)) {
   DCHECK(ServiceWorkerUtils::IsMainResourceType(resource_type));
   TRACE_EVENT_WITH_FLOW0("ServiceWorker",
                          "ServiceWorkerControlleeRequestHandler::"
@@ -278,23 +283,29 @@
     return;
   }
 
-  bool allow_service_worker = false;
+  AllowServiceWorkerResult allow_service_worker =
+      AllowServiceWorkerResult::No();
   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
     allow_service_worker =
         GetContentClient()->browser()->AllowServiceWorkerOnUI(
             registration->scope(),
             container_host_->site_for_cookies().RepresentativeUrl(),
             container_host_->top_frame_origin(), /*script_url=*/GURL(),
-            browser_context_, container_host_->web_contents_getter());
+            browser_context_);
   } else {
     allow_service_worker =
         GetContentClient()->browser()->AllowServiceWorkerOnIO(
             registration->scope(),
             container_host_->site_for_cookies().RepresentativeUrl(),
             container_host_->top_frame_origin(), /*script_url=*/GURL(),
-            resource_context_, container_host_->web_contents_getter());
+            resource_context_);
   }
 
+  RunOrPostTaskOnThread(
+      FROM_HERE, BrowserThread::UI,
+      base::BindOnce(service_worker_accessed_callback_, registration->scope(),
+                     allow_service_worker));
+
   if (!allow_service_worker) {
     TRACE_EVENT_WITH_FLOW1(
         "ServiceWorker",
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index 2e0a555..fe3b8be1 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "content/browser/loader/single_request_url_loader_factory.h"
+#include "content/browser/service_worker/service_worker_accessed_callback.h"
 #include "content/browser/service_worker/service_worker_navigation_loader.h"
 #include "content/common/content_export.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -45,7 +46,8 @@
       base::WeakPtr<ServiceWorkerContextCore> context,
       base::WeakPtr<ServiceWorkerContainerHost> container_host,
       blink::mojom::ResourceType resource_type,
-      bool skip_service_worker);
+      bool skip_service_worker,
+      ServiceWorkerAccessedCallback service_worker_accessed_callback);
   ~ServiceWorkerControlleeRequestHandler();
 
   // This could get called multiple times during the lifetime in redirect
@@ -121,6 +123,8 @@
   ServiceWorkerLoaderCallback loader_callback_;
   NavigationLoaderInterceptor::FallbackCallback fallback_callback_;
 
+  ServiceWorkerAccessedCallback service_worker_accessed_callback_;
+
   base::WeakPtrFactory<ServiceWorkerControlleeRequestHandler> weak_factory_{
       this};
 
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 bb2a12e..b751130 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
@@ -59,7 +59,8 @@
               test->context()->AsWeakPtr(),
               test->container_host_,
               type,
-              /*skip_service_worker=*/false)) {}
+              /*skip_service_worker=*/false,
+              base::DoNothing())) {}
 
     void MaybeCreateLoader() {
       network::ResourceRequest resource_request;
@@ -145,25 +146,23 @@
 
 class ServiceWorkerTestContentBrowserClient : public TestContentBrowserClient {
  public:
-  ServiceWorkerTestContentBrowserClient() {}
-  bool AllowServiceWorkerOnIO(
+  ServiceWorkerTestContentBrowserClient() = default;
+  AllowServiceWorkerResult AllowServiceWorkerOnIO(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::ResourceContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
-    return false;
+      content::ResourceContext* context) override {
+    return AllowServiceWorkerResult::No();
   }
 
-  bool AllowServiceWorkerOnUI(
+  AllowServiceWorkerResult AllowServiceWorkerOnUI(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::BrowserContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
-    return false;
+      content::BrowserContext* context) override {
+    return AllowServiceWorkerResult::No();
   }
 };
 
@@ -412,7 +411,7 @@
       std::make_unique<ServiceWorkerControlleeRequestHandler>(
           context()->AsWeakPtr(), container_host_,
           blink::mojom::ResourceType::kMainFrame,
-          /*skip_service_worker=*/true));
+          /*skip_service_worker=*/true, base::DoNothing()));
 
   // Conduct a main resource load.
   test_resources.MaybeCreateLoader();
@@ -453,7 +452,7 @@
       std::make_unique<ServiceWorkerControlleeRequestHandler>(
           context()->AsWeakPtr(), container_host_,
           blink::mojom::ResourceType::kMainFrame,
-          /*skip_service_worker=*/false));
+          /*skip_service_worker=*/false, base::DoNothing()));
 
   // Destroy the context and make a new one.
   helper_->context_wrapper()->DeleteAndStartOver();
diff --git a/content/browser/service_worker/service_worker_main_resource_handle.cc b/content/browser/service_worker/service_worker_main_resource_handle.cc
index 8983898..3fd46f7d 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle.cc
+++ b/content/browser/service_worker/service_worker_main_resource_handle.cc
@@ -18,11 +18,13 @@
 namespace content {
 
 ServiceWorkerMainResourceHandle::ServiceWorkerMainResourceHandle(
-    ServiceWorkerContextWrapper* context_wrapper)
+    ServiceWorkerContextWrapper* context_wrapper,
+    ServiceWorkerAccessedCallback on_service_worker_accessed)
     : context_wrapper_(context_wrapper) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  core_ = new ServiceWorkerMainResourceHandleCore(weak_factory_.GetWeakPtr(),
-                                                  context_wrapper);
+  core_ = new ServiceWorkerMainResourceHandleCore(
+      weak_factory_.GetWeakPtr(), context_wrapper,
+      std::move(on_service_worker_accessed));
 }
 
 ServiceWorkerMainResourceHandle::~ServiceWorkerMainResourceHandle() {
diff --git a/content/browser/service_worker/service_worker_main_resource_handle.h b/content/browser/service_worker/service_worker_main_resource_handle.h
index 0d739eb..c7786a2 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle.h
+++ b/content/browser/service_worker/service_worker_main_resource_handle.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_accessed_callback.h"
 #include "content/common/content_export.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
@@ -59,8 +60,9 @@
 //   ServiceWorkerMainResourceHandleCore on the core thread.
 class CONTENT_EXPORT ServiceWorkerMainResourceHandle {
  public:
-  explicit ServiceWorkerMainResourceHandle(
-      ServiceWorkerContextWrapper* context_wrapper);
+  ServiceWorkerMainResourceHandle(
+      ServiceWorkerContextWrapper* context_wrapper,
+      ServiceWorkerAccessedCallback on_service_worker_accessed);
   ~ServiceWorkerMainResourceHandle();
 
   // Called after a ServiceWorkerProviderHost tied with |provider_info|
diff --git a/content/browser/service_worker/service_worker_main_resource_handle_core.cc b/content/browser/service_worker/service_worker_main_resource_handle_core.cc
index 9048297c3..7cd6d90 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle_core.cc
+++ b/content/browser/service_worker/service_worker_main_resource_handle_core.cc
@@ -12,8 +12,12 @@
 
 ServiceWorkerMainResourceHandleCore::ServiceWorkerMainResourceHandleCore(
     base::WeakPtr<ServiceWorkerMainResourceHandle> ui_handle,
-    ServiceWorkerContextWrapper* context_wrapper)
-    : context_wrapper_(context_wrapper), ui_handle_(ui_handle) {
+    ServiceWorkerContextWrapper* context_wrapper,
+    ServiceWorkerAccessedCallback service_worker_accessed_callback)
+    : context_wrapper_(context_wrapper),
+      ui_handle_(ui_handle),
+      service_worker_accessed_callback_(
+          std::move(service_worker_accessed_callback)) {
   // The ServiceWorkerMainResourceHandleCore is created on the UI thread but
   // should only be accessed from the core thread afterwards.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/service_worker/service_worker_main_resource_handle_core.h b/content/browser/service_worker/service_worker_main_resource_handle_core.h
index 43d2b517..aac0082 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle_core.h
+++ b/content/browser/service_worker/service_worker_main_resource_handle_core.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_accessed_callback.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_controllee_request_handler.h"
 #include "content/common/content_export.h"
@@ -36,7 +37,8 @@
  public:
   ServiceWorkerMainResourceHandleCore(
       base::WeakPtr<ServiceWorkerMainResourceHandle> ui_handle,
-      ServiceWorkerContextWrapper* context_wrapper);
+      ServiceWorkerContextWrapper* context_wrapper,
+      ServiceWorkerAccessedCallback on_service_worker_accessed);
   ~ServiceWorkerMainResourceHandleCore();
 
   // Called by corresponding methods in ServiceWorkerMainResourceHandle. See
@@ -72,6 +74,10 @@
     return interceptor_.get();
   }
 
+  const ServiceWorkerAccessedCallback& service_worker_accessed_callback() {
+    return service_worker_accessed_callback_;
+  }
+
   base::WeakPtr<ServiceWorkerMainResourceHandleCore> AsWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
@@ -81,6 +87,8 @@
   base::WeakPtr<ServiceWorkerMainResourceHandle> ui_handle_;
   base::WeakPtr<ServiceWorkerContainerHost> container_host_;
   std::unique_ptr<ServiceWorkerControlleeRequestHandler> interceptor_;
+  ServiceWorkerAccessedCallback service_worker_accessed_callback_;
+
   base::WeakPtrFactory<ServiceWorkerMainResourceHandleCore> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerMainResourceHandleCore);
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
index d247c30..bc6f148 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
@@ -137,7 +137,8 @@
     handle_core->set_interceptor(
         std::make_unique<ServiceWorkerControlleeRequestHandler>(
             context_core->AsWeakPtr(), container_host, params.resource_type,
-            params.skip_service_worker));
+            params.skip_service_worker,
+            handle_core->service_worker_accessed_callback()));
   }
 
   // If |initialize_container_host_only| is true, we have already determined
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 27952002..4b4a06f 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -71,26 +71,24 @@
 
   ServiceWorkerTestContentBrowserClient() {}
 
-  bool AllowServiceWorkerOnIO(
+  AllowServiceWorkerResult AllowServiceWorkerOnIO(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::ResourceContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
+      content::ResourceContext* context) override {
     logs_.emplace_back(scope, site_for_cookies, top_frame_origin, script_url);
-    return false;
+    return AllowServiceWorkerResult::No();
   }
 
-  bool AllowServiceWorkerOnUI(
+  AllowServiceWorkerResult AllowServiceWorkerOnUI(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::BrowserContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
+      content::BrowserContext* context) override {
     logs_.emplace_back(scope, site_for_cookies, top_frame_origin, script_url);
-    return false;
+    return AllowServiceWorkerResult::No();
   }
 
   const std::vector<AllowServiceWorkerCallLog>& logs() const { return logs_; }
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 97e8163..afe9900 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -77,24 +77,22 @@
 
 class ServiceWorkerTestContentBrowserClient : public TestContentBrowserClient {
  public:
-  bool AllowServiceWorkerOnIO(
+  AllowServiceWorkerResult AllowServiceWorkerOnIO(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::ResourceContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
-    return false;
+      content::ResourceContext* context) override {
+    return AllowServiceWorkerResult::No();
   }
 
-  bool AllowServiceWorkerOnUI(
+  AllowServiceWorkerResult AllowServiceWorkerOnUI(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      content::BrowserContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter) override {
-    return false;
+      content::BrowserContext* context) override {
+    return AllowServiceWorkerResult::No();
   }
 };
 
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 9bfd2c6..e801fd01 100644
--- a/content/browser/service_worker/service_worker_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_request_handler_unittest.cc
@@ -53,8 +53,8 @@
  protected:
   void InitializeHandlerForNavigationSimpleTest(const std::string& url,
                                                 bool expected_handler_created) {
-    auto navigation_handle =
-        std::make_unique<ServiceWorkerMainResourceHandle>(context_wrapper());
+    auto navigation_handle = std::make_unique<ServiceWorkerMainResourceHandle>(
+        context_wrapper(), base::DoNothing());
     GURL gurl(url);
     auto begin_params = mojom::BeginNavigationParams::New();
     begin_params->request_context_type =
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 7e4de17..fde9a4ae9 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -47,6 +47,8 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/result_codes.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/c/system/types.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -2281,7 +2283,7 @@
   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
     if (!GetContentClient()->browser()->AllowServiceWorkerOnUI(
             scope_, scope_, url::Origin::Create(scope_), script_url_,
-            context_->wrapper()->browser_context(), base::NullCallback())) {
+            context_->wrapper()->browser_context())) {
       return false;
     }
   } else {
@@ -2289,7 +2291,7 @@
     if ((context_->wrapper()->resource_context() &&
          !GetContentClient()->browser()->AllowServiceWorkerOnIO(
              scope_, scope_, url::Origin::Create(scope_), script_url_,
-             context_->wrapper()->resource_context(), base::NullCallback()))) {
+             context_->wrapper()->resource_context()))) {
       return false;
     }
   }
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 1ba6488..053ee60 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5121,6 +5121,24 @@
     observer.AppCacheAccessed(manifest_url, blocked_by_policy);
 }
 
+void WebContentsImpl::OnServiceWorkerAccessed(
+    RenderFrameHost* render_frame_host,
+    const GURL& scope,
+    AllowServiceWorkerResult allowed) {
+  for (auto& observer : observers_) {
+    observer.OnServiceWorkerAccessed(render_frame_host, scope, allowed);
+  }
+}
+
+void WebContentsImpl::OnServiceWorkerAccessed(
+    NavigationHandle* navigation,
+    const GURL& scope,
+    AllowServiceWorkerResult allowed) {
+  for (auto& observer : observers_) {
+    observer.OnServiceWorkerAccessed(navigation, scope, allowed);
+  }
+}
+
 void WebContentsImpl::OnColorChooserFactoryReceiver(
     mojo::PendingReceiver<blink::mojom::ColorChooserFactory> receiver) {
   color_chooser_factory_receivers_.Add(this, std::move(receiver));
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index ba495dc..f2e1b85c 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -823,6 +823,9 @@
       NavigationHandle* navigation_handle) override;
   std::unique_ptr<NavigationUIData> GetNavigationUIData(
       NavigationHandle* navigation_handle) override;
+  void OnServiceWorkerAccessed(NavigationHandle* navigation,
+                               const GURL& scope,
+                               AllowServiceWorkerResult allowed) override;
 
   // RenderWidgetHostDelegate --------------------------------------------------
 
@@ -1177,6 +1180,10 @@
   // call this directly.
   void OnAppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy);
 
+  void OnServiceWorkerAccessed(RenderFrameHost* render_frame_host,
+                               const GURL& scope,
+                               AllowServiceWorkerResult allowed);
+
   JavaScriptDialogNavigationDeferrer* GetJavaScriptDialogNavigationDeferrer() {
     return javascript_dialog_navigation_deferrer_.get();
   }
diff --git a/content/browser/web_contents/web_contents_observer_browsertest.cc b/content/browser/web_contents/web_contents_observer_browsertest.cc
new file mode 100644
index 0000000..f811f268
--- /dev/null
+++ b/content/browser/web_contents/web_contents_observer_browsertest.cc
@@ -0,0 +1,217 @@
+// Copyright 2020 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 "base/test/scoped_feature_list.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "content/browser/service_worker/embedded_worker_status.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/allow_service_worker_result.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/test_content_browser_client.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NotNull;
+
+namespace content {
+
+class WebContentsObserverBrowserTest : public ContentBrowserTest {
+ public:
+  WebContentsObserverBrowserTest() = default;
+
+ protected:
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    SetupCrossSiteRedirector(embedded_test_server());
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  WebContentsImpl* web_contents() const {
+    return static_cast<WebContentsImpl*>(shell()->web_contents());
+  }
+
+  RenderFrameHostImpl* top_frame_host() {
+    return static_cast<RenderFrameHostImpl*>(web_contents()->GetMainFrame());
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+namespace {
+
+class ServiceWorkerAccessObserver : public WebContentsObserver {
+ public:
+  ServiceWorkerAccessObserver(WebContentsImpl* web_contents)
+      : WebContentsObserver(web_contents) {}
+
+  MOCK_METHOD3(OnServiceWorkerAccessed,
+               void(NavigationHandle*, const GURL&, AllowServiceWorkerResult));
+  MOCK_METHOD3(OnServiceWorkerAccessed,
+               void(RenderFrameHost*, const GURL&, AllowServiceWorkerResult));
+};
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(WebContentsObserverBrowserTest,
+                       OnServiceWorkerAccessed) {
+  GURL service_worker_scope =
+      embedded_test_server()->GetURL("/service_worker/");
+  {
+    // 1) Navigate to a page and register a ServiceWorker. Expect a notification
+    // to be called when the service worker is accessed from a frame.
+    ServiceWorkerAccessObserver observer(web_contents());
+    base::RunLoop run_loop;
+    EXPECT_CALL(
+        observer,
+        OnServiceWorkerAccessed(
+            testing::Matcher<RenderFrameHost*>(NotNull()), service_worker_scope,
+            AllowServiceWorkerResult::FromPolicy(false, false)))
+        .WillOnce([&]() { run_loop.Quit(); });
+    EXPECT_TRUE(NavigateToURL(
+        web_contents(), embedded_test_server()->GetURL(
+                            "/service_worker/create_service_worker.html")));
+    EXPECT_EQ("DONE",
+              EvalJs(top_frame_host(),
+                     "register('fetch_event.js', '/service_worker/');"));
+    run_loop.Run();
+  }
+
+  {
+    // 2) Navigate to a page in scope of the previously registered ServiceWorker
+    // and expect to get a notification about ServiceWorker being accessed for
+    // a navigation.
+    ServiceWorkerAccessObserver observer(web_contents());
+    base::RunLoop run_loop;
+    EXPECT_CALL(observer,
+                OnServiceWorkerAccessed(
+                    testing::Matcher<NavigationHandle*>(NotNull()),
+                    service_worker_scope,
+                    AllowServiceWorkerResult::FromPolicy(false, false)))
+        .WillOnce([&]() { run_loop.Quit(); });
+    EXPECT_TRUE(NavigateToURL(
+        web_contents(),
+        embedded_test_server()->GetURL("/service_worker/empty.html")));
+    run_loop.Run();
+  }
+}
+
+namespace {
+
+class ServiceWorkerAccessContentBrowserClient
+    : public TestContentBrowserClient {
+ public:
+  ServiceWorkerAccessContentBrowserClient() = default;
+
+  void SetJavascriptAllowed(bool allowed) { javascript_allowed_ = allowed; }
+
+  void SetCookiesAllowed(bool allowed) { cookies_allowed_ = allowed; }
+
+  AllowServiceWorkerResult AllowServiceWorkerOnUI(
+      const GURL& scope,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin,
+      const GURL& script_url,
+      BrowserContext* context) override {
+    return AllowServiceWorkerResult::FromPolicy(!javascript_allowed_,
+                                                !cookies_allowed_);
+  }
+
+ private:
+  bool cookies_allowed_ = true;
+  bool javascript_allowed_ = true;
+};
+
+}  // namespace
+
+class WebContentsObserverWithSWonUIBrowserTest
+    : public WebContentsObserverBrowserTest {
+ public:
+  WebContentsObserverWithSWonUIBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kServiceWorkerOnUI);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsObserverWithSWonUIBrowserTest,
+                       OnServiceWorkerAccessed_ContentClientBlocked) {
+  GURL service_worker_scope =
+      embedded_test_server()->GetURL("/service_worker/");
+  {
+    // 1) Navigate to a page and register a ServiceWorker. Expect a notification
+    // to be called when the service worker is accessed from a frame.
+    ServiceWorkerAccessObserver observer(web_contents());
+    base::RunLoop run_loop;
+    EXPECT_CALL(
+        observer,
+        OnServiceWorkerAccessed(
+            testing::Matcher<RenderFrameHost*>(NotNull()), service_worker_scope,
+            AllowServiceWorkerResult::FromPolicy(false, false)))
+        .WillOnce([&]() { run_loop.Quit(); });
+    EXPECT_TRUE(NavigateToURL(
+        web_contents(), embedded_test_server()->GetURL(
+                            "/service_worker/create_service_worker.html")));
+    EXPECT_EQ("DONE",
+              EvalJs(top_frame_host(),
+                     "register('fetch_event.js', '/service_worker/');"));
+    run_loop.Run();
+  }
+
+  // 2) Set content client and disallow javascript.
+  ServiceWorkerAccessContentBrowserClient content_browser_client;
+  ContentBrowserClient* old_client =
+      SetBrowserClientForTesting(&content_browser_client);
+  content_browser_client.SetJavascriptAllowed(false);
+
+  {
+    // 2) Navigate to a page in scope of the previously registered ServiceWorker
+    // and expect to get a notification about ServiceWorker being accessed for
+    // a navigation. Javascript should be blocked according to the policy.
+    ServiceWorkerAccessObserver observer(web_contents());
+    base::RunLoop run_loop;
+    EXPECT_CALL(observer, OnServiceWorkerAccessed(
+                              testing::Matcher<NavigationHandle*>(NotNull()),
+                              service_worker_scope,
+                              AllowServiceWorkerResult::FromPolicy(
+                                  /* javascript_blocked=*/true,
+                                  /* cookies_blocked=*/false)))
+        .WillOnce([&]() { run_loop.Quit(); });
+    EXPECT_TRUE(NavigateToURL(
+        web_contents(),
+        embedded_test_server()->GetURL("/service_worker/empty.html")));
+    run_loop.Run();
+  }
+
+  content_browser_client.SetJavascriptAllowed(true);
+  content_browser_client.SetCookiesAllowed(false);
+
+  {
+    // 3) Navigate to a page in scope of the previously registered ServiceWorker
+    // and expect to get a notification about ServiceWorker being accessed for
+    // a navigation. Cookies should be blocked according to the policy.
+    ServiceWorkerAccessObserver observer(web_contents());
+    base::RunLoop run_loop;
+    EXPECT_CALL(observer, OnServiceWorkerAccessed(
+                              testing::Matcher<NavigationHandle*>(NotNull()),
+                              service_worker_scope,
+                              AllowServiceWorkerResult::FromPolicy(
+                                  /* javascript_blocked=*/false,
+                                  /* cookies_blocked=*/true)))
+        .WillOnce([&]() { run_loop.Quit(); });
+    EXPECT_TRUE(NavigateToURL(
+        web_contents(),
+        embedded_test_server()->GetURL("/service_worker/empty.html")));
+    run_loop.Run();
+  }
+
+  SetBrowserClientForTesting(old_client);
+}
+
+}  // namespace content
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index f77943c..61a18f3 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -203,7 +203,7 @@
   file_url_support_ = creator_origin_.scheme() == url::kFileScheme;
 
   service_worker_handle_ = std::make_unique<ServiceWorkerMainResourceHandle>(
-      storage_partition_impl->GetServiceWorkerContext());
+      storage_partition_impl->GetServiceWorkerContext(), base::DoNothing());
 
   WorkerScriptFetchInitiator::Start(
       worker_process_host_->GetID(), script_url, creator_render_frame_host,
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index 0922ede..8f23948 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -95,7 +95,7 @@
     // Set up for service worker.
     auto service_worker_handle =
         std::make_unique<ServiceWorkerMainResourceHandle>(
-            helper_->context_wrapper());
+            helper_->context_wrapper(), base::DoNothing());
     mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerContainer>
         client_remote;
     mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost>
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index ba2aba9..eb2fa83c1 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -306,7 +306,7 @@
 
   auto service_worker_handle =
       std::make_unique<ServiceWorkerMainResourceHandle>(
-          storage_partition_->GetServiceWorkerContext());
+          storage_partition_->GetServiceWorkerContext(), base::DoNothing());
   auto* service_worker_handle_raw = service_worker_handle.get();
   host->SetServiceWorkerHandle(std::move(service_worker_handle));
 
diff --git a/content/browser/worker_host/worker_script_loader_factory_unittest.cc b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
index 88304ef..eecbe5801 100644
--- a/content/browser/worker_host/worker_script_loader_factory_unittest.cc
+++ b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
@@ -57,7 +57,7 @@
 
     // Set up a service worker host for the shared worker.
     service_worker_handle_ = std::make_unique<ServiceWorkerMainResourceHandle>(
-        helper_->context_wrapper());
+        helper_->context_wrapper(), base::DoNothing());
   }
 
  protected:
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index c783678..9cdae86 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -26,6 +26,8 @@
   visibility = [ "//content/*" ]
   sources = [
     "accessibility_tree_formatter.h",
+    "allow_service_worker_result.cc",
+    "allow_service_worker_result.h",
     "android/android_overlay_provider.h",
     "android/child_process_importance.h",
     "android/compositor.h",
diff --git a/content/public/browser/allow_service_worker_result.cc b/content/public/browser/allow_service_worker_result.cc
new file mode 100644
index 0000000..d3e0aaa
--- /dev/null
+++ b/content/public/browser/allow_service_worker_result.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 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/public/browser/allow_service_worker_result.h"
+
+namespace content {
+
+AllowServiceWorkerResult AllowServiceWorkerResult::Yes() {
+  return AllowServiceWorkerResult(true, false, false);
+}
+
+AllowServiceWorkerResult AllowServiceWorkerResult::No() {
+  return AllowServiceWorkerResult(false, false, false);
+}
+
+AllowServiceWorkerResult AllowServiceWorkerResult::FromPolicy(
+    bool javascript_blocked_by_policy,
+    bool cookies_blocked_by_policy) {
+  return AllowServiceWorkerResult(
+      !javascript_blocked_by_policy && !cookies_blocked_by_policy,
+      javascript_blocked_by_policy, cookies_blocked_by_policy);
+}
+
+AllowServiceWorkerResult::AllowServiceWorkerResult(
+    bool allowed,
+    bool javascript_blocked_by_policy,
+    bool cookies_blocked_by_policy)
+    : allowed_(allowed),
+      javascript_blocked_by_policy_(javascript_blocked_by_policy),
+      cookies_blocked_by_policy_(cookies_blocked_by_policy) {}
+
+bool AllowServiceWorkerResult::operator==(
+    const AllowServiceWorkerResult& other) const {
+  return allowed_ == other.allowed_ &&
+         javascript_blocked_by_policy_ == other.javascript_blocked_by_policy_ &&
+         cookies_blocked_by_policy_ == other.cookies_blocked_by_policy_;
+}
+
+}  // namespace content
diff --git a/content/public/browser/allow_service_worker_result.h b/content/public/browser/allow_service_worker_result.h
new file mode 100644
index 0000000..e46bd7a
--- /dev/null
+++ b/content/public/browser/allow_service_worker_result.h
@@ -0,0 +1,41 @@
+// Copyright 2020 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_PUBLIC_BROWSER_ALLOW_SERVICE_WORKER_RESULT_H_
+#define CONTENT_PUBLIC_BROWSER_ALLOW_SERVICE_WORKER_RESULT_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+class CONTENT_EXPORT AllowServiceWorkerResult {
+ public:
+  static AllowServiceWorkerResult Yes();
+  static AllowServiceWorkerResult No();
+  static AllowServiceWorkerResult FromPolicy(bool javascript_blocked_by_policy,
+                                             bool cookies_blocked_by_policy);
+
+  operator bool() { return allowed_; }
+
+  bool javascript_blocked_by_policy() const {
+    return javascript_blocked_by_policy_;
+  }
+
+  bool cookies_blocked_by_policy() const { return cookies_blocked_by_policy_; }
+
+  bool operator==(const AllowServiceWorkerResult& other) const;
+
+ private:
+  AllowServiceWorkerResult(bool allowed,
+                           bool javacript_blocked_by_policy,
+                           bool cookies_blocked_by_policy);
+
+  bool allowed_ = false;
+  bool javascript_blocked_by_policy_ = false;
+  bool cookies_blocked_by_policy_ = false;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_ALLOW_SERVICE_WORKER_RESULT_H_
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index c49222a1..599022823 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -304,24 +304,22 @@
   return true;
 }
 
-bool ContentBrowserClient::AllowServiceWorkerOnIO(
+AllowServiceWorkerResult ContentBrowserClient::AllowServiceWorkerOnIO(
     const GURL& scope,
     const GURL& site_for_cookies,
     const base::Optional<url::Origin>& top_frame_origin,
     const GURL& script_url,
-    ResourceContext* context,
-    base::RepeatingCallback<WebContents*()> wc_getter) {
-  return true;
+    ResourceContext* context) {
+  return AllowServiceWorkerResult::Yes();
 }
 
-bool ContentBrowserClient::AllowServiceWorkerOnUI(
+AllowServiceWorkerResult ContentBrowserClient::AllowServiceWorkerOnUI(
     const GURL& scope,
     const GURL& site_for_cookies,
     const base::Optional<url::Origin>& top_frame_origin,
     const GURL& script_url,
-    BrowserContext* context,
-    base::RepeatingCallback<WebContents*()> wc_getter) {
-  return true;
+    BrowserContext* context) {
+  return AllowServiceWorkerResult::Yes();
 }
 
 bool ContentBrowserClient::AllowSharedWorker(
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 728f71d7..7c7a70b9 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -24,6 +24,7 @@
 #include "base/util/type_safety/strong_alias.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/allow_service_worker_result.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/generated_code_cache_settings.h"
 #include "content/public/common/page_visibility_state.h"
@@ -50,6 +51,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 #include "url/origin.h"
+#include "url/url_constants.h"
 
 #if (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA)
 #include "base/posix/global_descriptors.h"
@@ -591,24 +593,20 @@
   // made to access the registration but there is no specific service worker in
   // the registration being acted on.
   //
-  // A null |wc_getter| callback indicates this is for starting a service
-  // worker, which is not necessarily associated with a particular tab.
   // This is called on the IO thread.
-  virtual bool AllowServiceWorkerOnIO(
+  virtual AllowServiceWorkerResult AllowServiceWorkerOnIO(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      ResourceContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter);
+      ResourceContext* context);
   // Same but for the UI thread.
-  virtual bool AllowServiceWorkerOnUI(
+  virtual AllowServiceWorkerResult AllowServiceWorkerOnUI(
       const GURL& scope,
       const GURL& site_for_cookies,
       const base::Optional<url::Origin>& top_frame_origin,
       const GURL& script_url,
-      BrowserContext* context,
-      base::RepeatingCallback<WebContents*()> wc_getter);
+      BrowserContext* context);
 
   // Allow the embedder to control if a Shared Worker can be connected from a
   // given tab.
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index 317c46f4..5435b9f7 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -11,8 +11,10 @@
 #include "base/optional.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
+#include "base/threading/thread_restrictions.h"
 #include "components/viz/common/vertical_scroll_direction.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/allow_service_worker_result.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/browser/visibility.h"
@@ -613,6 +615,18 @@
   // subsequently be destroyed if it is not adopted.
   virtual void DidActivatePortal(WebContents* predecessor_contents) {}
 
+  // Called when the RenderFrameHost tries to use a ServiceWorker
+  // (e.g. via navigation.serviceWorker API).
+  virtual void OnServiceWorkerAccessed(RenderFrameHost* render_frame_host,
+                                       const GURL& scope,
+                                       AllowServiceWorkerResult allowed) {}
+  // Called when the NavigationHandle accesses ServiceWorker to see if the
+  // network request should be handled by the ServiceWorker instead
+  // (e.g. for navigations to URLs which are in scope of a ServiceWorker).
+  virtual void OnServiceWorkerAccessed(NavigationHandle* navigation_handle,
+                                       const GURL& scope,
+                                       AllowServiceWorkerResult allowed) {}
+
   // IPC::Listener implementation.
   // DEPRECATED: Use (i.e. override) the other overload instead:
   //     virtual bool OnMessageReceived(const IPC::Message& message,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index b3a81d2a..d2ccdde 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1075,6 +1075,7 @@
     "../browser/vibration_browsertest.cc",
     "../browser/web_contents/opened_by_dom_browsertest.cc",
     "../browser/web_contents/web_contents_impl_browsertest.cc",
+    "../browser/web_contents/web_contents_observer_browsertest.cc",
     "../browser/web_contents/web_contents_view_aura_browsertest.cc",
     "../browser/web_contents_receiver_set_browsertest.cc",
     "../browser/web_package/save_page_as_web_bundle_browsertest.cc",