blob: 457f420b04a1c4a331fdff1b58fe14d311e0cbc3 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// 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_CLIENT_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CLIENT_H_
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/back_forward_cache_metrics.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/common/content_export.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/public/browser/service_worker_client_info.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h"
#include "services/network/public/mojom/document_isolation_policy.mojom-forward.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom-forward.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_running_status_callback.mojom-forward.h"
namespace network {
class SharedURLLoaderFactory;
struct ResourceRequest;
} // namespace network
namespace content {
class ScopedServiceWorkerClient;
class ServiceWorkerClientOwner;
class ServiceWorkerContainerHostForClient;
class ServiceWorkerContextCore;
class ServiceWorkerVersion;
class StoragePartitionImpl;
struct GlobalRenderFrameHostId;
struct PolicyContainerPolicies;
// `ServiceWorkerClient` is owned by `ServiceWorkerContextCore` and represents a
// service worker client in the spec.
// https://w3c.github.io/ServiceWorker/#dfn-service-worker-client
//
// Example:
// When a new service worker registration is created, the browser process
// iterates over all ServiceWorkerClients to find clients (frames,
// dedicated workers, and shared workers) with a URL inside the registration's
// scope, and has the container host watch the registration in order to resolve
// navigator.serviceWorker.ready once the registration settles, if need.
class CONTENT_EXPORT ServiceWorkerClient final
: public ServiceWorkerRegistration::Listener {
public:
using ExecutionReadyCallback = base::OnceClosure;
// Constructor for window clients.
ServiceWorkerClient(base::WeakPtr<ServiceWorkerContextCore> context,
bool is_parent_frame_secure,
FrameTreeNodeId ongoing_navigation_frame_tree_node_id);
// Constructor for window clients for prefetch.
ServiceWorkerClient(base::WeakPtr<ServiceWorkerContextCore> context,
bool is_parent_frame_secure,
scoped_refptr<network::SharedURLLoaderFactory>
network_url_loader_factory_for_prefetch);
// Constructor for worker clients.
ServiceWorkerClient(base::WeakPtr<ServiceWorkerContextCore> context,
int process_id,
ServiceWorkerClientInfo client_info);
ServiceWorkerClient(const ServiceWorkerClient& other) = delete;
ServiceWorkerClient& operator=(const ServiceWorkerClient& other) = delete;
~ServiceWorkerClient();
ServiceWorkerContainerHostForClient* container_host() {
return container_host_.get();
}
const ServiceWorkerContainerHostForClient* container_host() const {
return container_host_.get();
}
// ServiceWorkerRegistration::Listener overrides.
void OnVersionAttributesChanged(
ServiceWorkerRegistration* registration,
blink::mojom::ChangedServiceWorkerObjectsMaskPtr changed_mask) override;
void OnRegistrationFailed(ServiceWorkerRegistration* registration) override;
void OnRegistrationFinishedUninstalling(
ServiceWorkerRegistration* registration) override;
void OnSkippedWaiting(ServiceWorkerRegistration* registration) override;
// For service worker clients. The host keeps track of all the prospective
// longest-matching registrations, in order to resolve .ready or respond to
// claim() attempts.
//
// This is subtle: it doesn't keep all registrations (e.g., from storage) in
// memory, but just the ones that are possibly the longest-matching one. The
// best match from storage is added at load time. That match can't uninstall
// while this host is a controllee, so all the other stored registrations can
// be ignored. Only a newly installed registration can claim it, and new
// installing registrations are added as matches.
void AddMatchingRegistration(ServiceWorkerRegistration* registration);
void RemoveMatchingRegistration(ServiceWorkerRegistration* registration);
// An optimized implementation of [[Match Service Worker Registration]]
// for the current client.
ServiceWorkerRegistration* MatchRegistration() const;
// For service worker clients. Called when |version| is the active worker upon
// the main resource request for this client. Remembers |version| as needing
// a Soft Update. To avoid affecting page load performance, the update occurs
// when we get a HintToUpdateServiceWorker message from the renderer, or when
// |this| is destroyed before receiving that message.
//
// Corresponds to the Handle Fetch algorithm:
// "If request is a non-subresource request...invoke Soft Update algorithm
// with registration."
// https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
//
// This can be called multiple times due to redirects during a main resource
// load. All service workers are updated.
void AddServiceWorkerToUpdate(scoped_refptr<ServiceWorkerVersion> version);
// Notifies the client that its controller used a feature, for UseCounter
// purposes. This can only be called if IsContainerForClient() is true.
void CountFeature(blink::mojom::WebFeature feature);
// Called when this container host's controller has been terminated and doomed
// due to an exceptional condition like it could no longer be read from the
// script cache.
void NotifyControllerLost();
// Returns the client type of this container host. Can only be called when
// IsContainerForClient() is true.
blink::mojom::ServiceWorkerClientType GetClientType() const;
// Returns true if this container host is specifically for a window client.
bool IsContainerForWindowClient() const;
// Returns true if this container host is specifically for a worker client.
bool IsContainerForWorkerClient() const;
// Returns the client info for this container host.
ServiceWorkerClientInfo GetServiceWorkerClientInfo() const;
// Transitions to `kResponseCommitted`.
// `rfh_id` is given only for window clients.
// Must be called from `ScopedServiceWorkerClient` to centralize the
// lifetime control there.
// TODO(falken): Pass in an RenderFrameHostImpl instead of an ID. Some
// tests use a fake id.
blink::mojom::ServiceWorkerContainerInfoForClientPtr CommitResponse(
base::PassKey<ScopedServiceWorkerClient>,
std::optional<GlobalRenderFrameHostId> rfh_id,
const PolicyContainerPolicies& policy_container_policies,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter>
dip_reporter,
ukm::SourceId ukm_source_id);
// For service worker window clients. Called after the navigation commits to a
// RenderFrameHost. At this point, the previous ServiceWorkerContainerHost
// for that RenderFrameHost no longer exists.
void OnEndNavigationCommit();
// Must be called before `CommitResponse()`.
void UpdateUrls(const GURL& creation_url,
const std::optional<url::Origin>& top_frame_origin,
const blink::StorageKey& storage_key);
// TODO(crbug.com/336154571): For some tests that want UpdateUrls() after
// response commit. Investigate whether this can be removed and related
// condition checks can be turned to CHECK()s.
void UpdateUrlsAfterCommitResponseForTesting(
const GURL& url,
const std::optional<url::Origin>& top_frame_origin,
const blink::StorageKey& storage_key);
// The storage key to be used for `UpdateUrls()`.
// For other purposes, use `key()` instead.
// `isolation_info_from_handle` is
// `ServiceWorkerMainResourceHandle::isolation_info_`.
blink::StorageKey CalculateStorageKeyForUpdateUrls(
const GURL& url,
const net::IsolationInfo& isolation_info_from_handle) const;
// For service worker clients. Makes this client be controlled by
// |registration|'s active worker, or makes this client be not
// controlled if |registration| is null. If |notify_controllerchange| is true,
// instructs the renderer to dispatch a 'controllerchange' event.
void SetControllerRegistration(
scoped_refptr<ServiceWorkerRegistration> controller_registration,
bool notify_controllerchange);
// Create a receiver to notice on ServiceWorker running status change.
mojo::PendingReceiver<blink::mojom::ServiceWorkerRunningStatusCallback>
GetRunningStatusCallbackReceiver();
// `registration` claims the client (document, dedicated worker, or shared
// worker) to be controlled.
void ClaimedByRegistration(
scoped_refptr<ServiceWorkerRegistration> registration);
// The URL of the main resource of the client: the document URL (for
// documents) or script URL (for workers).
//
// url() may be empty if loading has not started, or our custom loading
// handler didn't see the load (because e.g. another handler did first, or the
// initial request URL was such that OriginCanAccessServiceWorkers returned
// false).
//
// The URL may also change on redirects during loading. Once
// is_response_committed() is true, the URL should no longer change.
const GURL& url() const { return url_; }
// The creation_url is the same as the url property above, but without
// having the URL fragment removed. This is necessary for the
// JS ServiceWorkerClient.url() property.
// See https://html.spec.whatwg.org/C/#concept-environment-creation-url
//
// TODO(crbug.com/384759487): Consider merging `url()` into `creation_url()`.
const GURL& creation_url() const { return creation_url_; }
// The origin of the top frame of the client. This is more specific than the
// `top_frame_site` in the storage key, so must be passed separately.
// For shared worker it is the origin of the document that created the worker.
// For dedicated worker it is the top-frame origin of the document that owns
// the worker.
std::optional<url::Origin> top_frame_origin() const {
return top_frame_origin_;
}
// The StorageKey for this context. Any service worker registrations/versions
// that are persisted from this context (e.x., via `register()`) are
// associated with this particular StorageKey. Corresponds to
// https://storage.spec.whatwg.org/#obtain-a-storage-key.
// Note: This doesn't hold true when "disable-web-security" is active, see
// `service_worker_security_utils::GetCorrectStorageKeyForWebSecurityState()`
// and its usages for more details.
const blink::StorageKey& key() const { return key_; }
// Returns whether this container host is secure enough to have a service
// worker controller.
// Analogous to Blink's Document::IsSecureContext. Because of how service
// worker intercepts main resource requests, this check must be done
// browser-side once the URL is known (see comments in
// ServiceWorkerNetworkProviderForFrame::Create). This function uses
// |url_| and |is_parent_frame_secure_| to determine context security, so they
// must be set properly before calling this function.
bool IsEligibleForServiceWorkerController() const;
// For service worker clients. True if the response for the main resource load
// was committed to the renderer. When this is false, the client's URL may
// still change due to redirects.
bool is_response_committed() const;
// For service worker clients. |callback| is called when this client becomes
// execution ready or if it is destroyed first.
void AddExecutionReadyCallback(ExecutionReadyCallback callback);
// For service worker clients. True if the client is execution ready and
// therefore can be exposed to JavaScript. Execution ready implies response
// committed.
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
bool is_execution_ready() const;
bool is_container_ready() const;
const base::UnguessableToken& fetch_request_window_id() const {
return fetch_request_window_id_;
}
base::TimeTicks create_time() const { return create_time_; }
// For service worker window clients. The RFH ID is set only after navigation
// commit. See also comments for RenderFrameHost::GetFrameTreeNodeId()
// for more details.
GlobalRenderFrameHostId GetRenderFrameHostId() const;
// For service worker clients. For window clients, this is not populated until
// after navigation commit.
int GetProcessId() const;
// For service worker window clients.
// Returns the ongoing navigation request before the navigation commit starts.
// Returns a nullptr if the clients was discarded, e.g., the WebContents was
// closed.
// Never call this function if `GetRenderFrameHostId` can return a valid
// value, since the client can change to another FrameTreeNode(FTN) over its
// lifetime while its RFH ID never changes, and and function uses the FTN ID
// to find the NavigationRequest. See also comments for
// RenderFrameHost::GetFrameTreeNodeId() for more details.
NavigationRequest* GetOngoingNavigationRequestBeforeCommit(
base::PassKey<StoragePartitionImpl>) const;
// Creates a navigational network URLLoaderFactory for window client. This
// should be called before the navigation is committed.
enum class CreateNetworkURLLoaderFactoryType {
kNavigationPreload,
kRaceNetworkRequest,
kSyntheticNetworkRequest,
};
scoped_refptr<network::SharedURLLoaderFactory> CreateNetworkURLLoaderFactory(
CreateNetworkURLLoaderFactoryType type,
StoragePartitionImpl* storage_partition,
const network::ResourceRequest& resource_request);
// For service worker clients.
// The type of `ongoing_navigation_frame_tree_node_id_` (if any) for metrics.
std::string GetFrameTreeNodeTypeStringBeforeCommit() const;
// For service worker clients.
const std::string& client_uuid() const;
// The client ID used as `FetchEvent.resultingClientID`.
// https://w3c.github.io/ServiceWorker/#fetch-event-resultingclientid
//
// Prefetch expects the value to be empty.
// See: crbug.com/404294123
std::string client_uuid_for_resulting_client_id() const;
// For service worker clients. Returns this client's controller.
ServiceWorkerVersion* controller() const;
// For service worker clients. Returns this client's controller's
// registration.
ServiceWorkerRegistration* controller_registration() const;
// BackForwardCache:
// For service worker clients that are windows.
bool IsInBackForwardCache() const;
void EvictFromBackForwardCache(
BackForwardCacheMetrics::NotRestoredReason reason);
// Called when this container host's frame goes into BackForwardCache.
void OnEnterBackForwardCache();
// Called when a frame gets restored from BackForwardCache. Note that a
// BackForwardCached frame can be deleted while in the cache but in this case
// OnRestoreFromBackForwardCache will not be called.
void OnRestoreFromBackForwardCache();
bool navigation_commit_ended() const { return navigation_commit_ended_; }
void EnterBackForwardCacheForTesting() { is_in_back_forward_cache_ = true; }
void LeaveBackForwardCacheForTesting() { is_in_back_forward_cache_ = false; }
bool is_in_back_forward_cache() const { return is_in_back_forward_cache_; }
// For service worker clients. Returns the URL that is used for scope matching
// algorithm. This can be different from url() in the case of blob URL
// workers or srcdoc/about:blank iframes. In that case, url() may be like
// "blob://https://a.test" or "about:srcdoc" and the scope matching URL is
// "https://a.test", inherited from the parent container host.
const GURL& GetUrlForScopeMatch() const;
// For service worker clients that are dedicated workers and srcdoc iframe.
// Inherits the controller of the creator document or worker. Used when the
// client was created with a blob, about:srcdoc or about:blank URL.
void InheritControllerFrom(ServiceWorkerClient& creator_host,
const GURL& client_url);
void SetContainerReady();
bool is_inherited() const { return is_inherited_; }
void SetInherited() { is_inherited_ = true; }
const base::WeakPtr<ServiceWorkerContextCore>& context() const {
return context_;
}
ServiceWorkerClientOwner& owner() { return *owner_; }
// Implements blink::mojom::ServiceWorkerContainerHost and called from
// ServiceWorkerContainerHostForClient.
void EnsureControllerServiceWorker(
mojo::PendingReceiver<blink::mojom::ControllerServiceWorker> receiver,
blink::mojom::ControllerServiceWorkerPurpose purpose);
void HintToUpdateServiceWorker();
void EnsureFileAccess(
const std::vector<base::FilePath>& file_paths,
blink::mojom::ServiceWorkerContainerHost::EnsureFileAccessCallback
callback);
void OnExecutionReady();
// Sets execution ready flag and runs execution ready callbacks.
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
void SetExecutionReady();
// Sets test url loader factory. When set, |url_loader_factory| will be
// returned when CreateNetworkURLLoaderFactory is called.
void SetNetworkURLLoaderFactoryForTesting(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
base::WeakPtr<ServiceWorkerClient> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
class ServiceWorkerRunningStatusObserver;
friend class ServiceWorkerContainerHostTest;
void UpdateUrlsInternal(const GURL& creation_url,
const std::optional<url::Origin>& top_frame_origin,
const blink::StorageKey& storage_key);
// Syncs matching registrations with live registrations.
void SyncMatchingRegistrations();
#if DCHECK_IS_ON()
bool IsMatchingRegistration(ServiceWorkerRegistration* registration) const;
#endif // DCHECK_IS_ON()
// Discards all references to matching registrations.
void RemoveAllMatchingRegistrations();
void RunExecutionReadyCallbacks();
// For service worker clients. The flow is:
// - kInitial -> kResponseCommitted -> kContainerReady -> kExecutionReady
// - kInitial -> kResponseNotCommitted (client initialization failure cases)
// - kInitial (no transitions, client initialization failure cases)
//
// - kInitial: The initial phase. Container host mojo messages are buffered
// because the message pipe is piggy-backed and isn't associated to the
// existing message pipe yet.
//
// - kResponseCommitted: `CommitResponse()` is called, i.e. the response for
// the main resource is about to be committed to the renderer and container
// host's mojo endpoints are about to be passed to the renderer. This
// client's URL should no longer change. The client should immediately
// transition to kContainerReady and thus the kResponseCommitted state
// shouldn't be observed (except for the code for response commit and
// transitioning to kContainerReady).
//
// - kContainerReady: `SetContainerReady()` is called. The response commit has
// completed. The container host's mojo pipes are ready to use.
//
// - kExecutionReady: `SetExecutionReady()` is called. This client can be
// exposed to JavaScript as a Client object.
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
//
// - kResponseNotCommitted: `CommitResponse()` is not called (and worker
// script loading is considered failed) but `SetExecutionReady()` is called.
// This can happen e.g. on COEP errors because its caller
// (`WorkerScriptLoader::CommitCompleted()`) doesn't take COEP errors into
// account. `CommitResponse()` is never called after reaching this state
// (i.e. it's not a race condition between `CommitResponse()` and
// `SetExecutionReady()`).
enum class ClientPhase {
kInitial,
kResponseCommitted,
kContainerReady,
kExecutionReady,
kResponseNotCommitted
};
void TransitionToClientPhase(ClientPhase new_phase);
// Sets the controller to |controller_registration_->active_version()| or null
// if there is no associated registration.
//
// If |notify_controllerchange| is true, instructs the renderer to dispatch a
// 'controller' change event.
void UpdateController(bool notify_controllerchange);
#if DCHECK_IS_ON()
void CheckControllerConsistency(bool should_crash) const;
#endif // DCHECK_IS_ON()
// Flushes features stored, when it gets ready to send.
// If it is still not ready to send, the features are buffered again.
void FlushFeatures();
const base::WeakPtr<ServiceWorkerContextCore> context_;
// The `ServiceWorkerClientOwner` that owns `this`.
// On construction, `owner_` is the same as
// `context_->service_worker_client_owner()`.
// After `ServiceWorkerContextWrapper::DidDeleteAndStartOver()`,
// - `context_` is cleared (as the old `ServiceWorkerContextCore` is
// destroyed) so that subsequent e.g. registration fails.
// - `owner_` remains the same and valid (the `ServiceWorkerClientOwner`
// is now owned by the new `ServiceWorkerContextCore`), in order to
// continue lifetime management of `this`.
const raw_ref<ServiceWorkerClientOwner> owner_;
// The corresponding container host.
// Always valid and non-null except for initialization/destruction.
std::unique_ptr<ServiceWorkerContainerHostForClient> container_host_;
// The time when the container host is created.
const base::TimeTicks create_time_;
// See comments for the getter functions.
GURL url_;
GURL creation_url_;
std::optional<url::Origin> top_frame_origin_;
blink::StorageKey key_;
// For all service worker clients --------------------------------------------
// A GUID that is web-exposed as FetchEvent.clientId.
std::string client_uuid_;
// |is_parent_frame_secure_| is false if the container host is created for a
// document whose parent frame is not secure. 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. If the container is not created for a document, or
// the document does not have a parent frame, is_parent_frame_secure_| is
// true.
const bool is_parent_frame_secure_;
// |is_initiated_by_prefetch_| is true if ServiceWorkerClient is initiated
// by prefetch. This is used for changing the resulting client ID behavior
// on prefetch.
// See: crbug.com/404294123
const bool is_initiated_by_prefetch_;
// The phase that this container host is on.
ClientPhase client_phase_ = ClientPhase::kInitial;
// Callbacks to run upon transition to kExecutionReady.
std::vector<ExecutionReadyCallback> execution_ready_callbacks_;
// The controller service worker (i.e., ServiceWorkerContainer#controller) and
// its registration. The controller is typically the same as the
// registration's active version, but during algorithms such as the update,
// skipWaiting(), and claim() steps, the active version and controller may
// temporarily differ. For example, to perform skipWaiting(), the
// registration's active version is updated first and then the container
// host's controller is updated to match it.
scoped_refptr<ServiceWorkerVersion> controller_;
scoped_refptr<ServiceWorkerRegistration> controller_registration_;
// Keyed by registration scope URL length.
using ServiceWorkerRegistrationMap =
std::map<size_t, scoped_refptr<ServiceWorkerRegistration>>;
// Contains all living registrations whose scope this client's URL starts
// with and whose keys match this client's key, used for .ready and claim().
// It is empty if IsEligibleForServiceWorkerController() is false. See also
// AddMatchingRegistration().
ServiceWorkerRegistrationMap matching_registrations_;
// The service workers in the chain of redirects during the main resource
// request for this client. These workers should be updated "soon". See
// AddServiceWorkerToUpdate() documentation.
class PendingUpdateVersion;
base::flat_set<PendingUpdateVersion> versions_to_update_;
// The type of client.
ServiceWorkerClientInfo client_info_;
// The URL used for service worker scope matching. It is empty except in the
// case of a service worker client with a blob, about:blank or about:srcdoc
// URL.
GURL scope_match_url_for_client_;
// Become true if the container is inherited by other container.
bool is_inherited_ = false;
// The observer for the running status change.
// It is used for notifying the ServiceWorker running status change to
// the ServiceWorkerContainerHost in the renderer.
std::unique_ptr<ServiceWorkerRunningStatusObserver> running_status_observer_;
// Until |container_| gets associated, its method cannot be used.
// If CountFeature() is called before |container_| gets ready, features are
// kept here, and flushed in SetContainerReady().
std::set<blink::mojom::WebFeature> buffered_used_features_;
// Test url loader factory used for testing. When set, it is returned from
// CreateNetworkURLLoaderFactory instead of a real one.
scoped_refptr<network::SharedURLLoaderFactory>
network_url_loader_factory_override_for_testing_;
// For worker clients only ---------------------------------------------------
// The ID of the process where the container lives for worker clients. It is
// set during initialization. Use `GetProcessId()` instead of directly
// accessing `process_id_for_worker_client_` to avoid using a wrong process
// id.
const int process_id_for_worker_client_;
// For window clients only ---------------------------------------------------
// A token used internally to identify this context in requests. Corresponds
// to the Fetch specification's concept of a request's associated window:
// https://fetch.spec.whatwg.org/#concept-request-window. This gets reset on
// redirects, unlike |client_uuid_|.
//
// TODO(falken): Consider using this for |client_uuid_| as well. We can't
// right now because this gets reset on redirects, and potentially sites rely
// on the GUID format.
base::UnguessableToken fetch_request_window_id_;
// Indicates if this container host is in the back-forward cache.
//
// TODO(yuzus): This bit will be unnecessary once ServiceWorkerContainerHost
// and RenderFrameHost have the same lifetime.
bool is_in_back_forward_cache_ = false;
// Indicates if OnEndNavigationCommit() was called on this container host.
bool navigation_commit_ended_ = false;
// The frame tree node ID that is set in the constructor and is reset in
// CommitResponse().
FrameTreeNodeId ongoing_navigation_frame_tree_node_id_;
// URLLoaderFactory used for navigation preload etc.
// Only set/used for clients for prefetch.
scoped_refptr<network::SharedURLLoaderFactory>
network_url_loader_factory_for_prefetch_;
// For all instances --------------------------------------------------------
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<ServiceWorkerClient> weak_ptr_factory_{this};
};
CONTENT_EXPORT BASE_DECLARE_FEATURE(kSharedWorkerBlobURLFix);
} // namespace content
#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CLIENT_H_