// Copyright 2017 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_WORKER_HOST_DEDICATED_WORKER_HOST_H_
#define CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_

#include <memory>
#include <optional>

#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/supports_user_data.h"
#include "build/build_config.h"
#include "content/browser/browser_interface_broker_impl.h"
#include "content/browser/buckets/bucket_context.h"
#include "content/browser/locks/lock_manager.h"
#include "content/browser/renderer_host/code_cache_host_impl.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/security/dip/document_isolation_policy_reporter.h"
#include "content/browser/worker_host/worker_script_fetcher.h"
#include "content/common/content_export.h"
#include "content/public/browser/dedicated_worker_creator.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/weak_document_ptr.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "net/base/isolation_info.h"
#include "net/storage_access_api/status.h"
#include "services/device/public/cpp/compute_pressure/buildflags.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/blob/blob_url_store.mojom-forward.h"
#include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom.h"
#include "third_party/blink/public/mojom/idle/idle_manager.mojom-forward.h"
#include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
#include "third_party/blink/public/mojom/loader/content_security_notifier.mojom.h"
#include "third_party/blink/public/mojom/serial/serial.mojom-forward.h"
#include "third_party/blink/public/mojom/usb/web_usb_service.mojom-forward.h"
#include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom-forward.h"
#include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-forward.h"
#include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom-forward.h"
#include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom.h"
#include "third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom.h"
#include "third_party/blink/public/mojom/worker/subresource_loader_updater.mojom.h"
#include "url/origin.h"

#if !BUILDFLAG(IS_ANDROID)
#include "third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom-forward.h"
#include "third_party/blink/public/mojom/hid/hid.mojom-forward.h"
#endif

#if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
#include "content/browser/compute_pressure/pressure_service_for_dedicated_worker.h"
#include "third_party/blink/public/mojom/compute_pressure/web_pressure_manager.mojom.h"
#endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)

namespace network {

struct CrossOriginEmbedderPolicy;

}  // namespace network

namespace content {

class CrossOriginEmbedderPolicyReporter;
class DedicatedWorkerServiceImpl;
class ServiceWorkerClient;
class ServiceWorkerMainResourceHandle;
class StoragePartitionImpl;
struct WorkerScriptFetcherResult;

// A host for a single dedicated worker. It deletes itself upon Mojo
// disconnection from the worker in the renderer or when the RenderProcessHost
// of the worker is destroyed. This lives on the UI thread.
// TODO(crbug.com/40807127): Align this class's lifetime with the associated
// frame.
class CONTENT_EXPORT DedicatedWorkerHost final
    : public blink::mojom::DedicatedWorkerHost,
      public blink::mojom::BackForwardCacheControllerHost,
      public RenderProcessHostObserver,
      public LockManager<storage::BucketId>::Observer,
      public BucketContext,
      public base::SupportsUserData {
 public:
  // Creates a new browser-side host for a single dedicated worker.
  //
  // See the class-level comment for lifetime considerations.
  //
  // - `service` must not be nullptr and must outlive this instance.
  // - `worker_process_host` must not be nullptr and must outlive this instance.
  //   It must be initialized and not be dead - see
  //   `RenderProcessHost::IsInitializedAndNotDead()`.
  // - Exactly one of `creator_render_frame_host_id` or `creator_worker_token`
  //   must be specified.
  // - `creator_client_security_state` specifies the client security state of
  //   the creator frame or worker. It must not be nullptr.
  // - `creator_policies` specifies the security policies of the creator.
  // - `creator_network_restrictions_id` specifies the network restrictions of
  //    the creator as per its connection allowlists.
  DedicatedWorkerHost(
      DedicatedWorkerServiceImpl* service,
      const blink::DedicatedWorkerToken& token,
      RenderProcessHost* worker_process_host,
      DedicatedWorkerCreator creator,
      WeakDocumentPtr ancestor_document,
      const url::Origin& creator_origin,
      const blink::StorageKey& worker_storage_key,
      const url::Origin& renderer_origin,
      const net::IsolationInfo& isolation_info,
      network::mojom::ClientSecurityStatePtr creator_client_security_state,
      const PolicyContainerPolicies& creator_policies,
      base::WeakPtr<CrossOriginEmbedderPolicyReporter> creator_coep_reporter,
      const base::UnguessableToken& creator_network_restrictions_id,
      mojo::PendingReceiver<blink::mojom::DedicatedWorkerHost> host,
      net::StorageAccessApiStatus storage_access_api_status);

  DedicatedWorkerHost(const DedicatedWorkerHost&) = delete;
  DedicatedWorkerHost& operator=(const DedicatedWorkerHost&) = delete;

  ~DedicatedWorkerHost() final;

  void BindBrowserInterfaceBrokerReceiver(
      mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver);

  const blink::DedicatedWorkerToken& GetToken() const { return token_; }
  RenderProcessHost* GetProcessHost() const { return worker_process_host_; }
  const blink::StorageKey& GetWorkerStorageKey() const {
    return worker_storage_key_;
  }
  GlobalRenderFrameHostId GetAncestorRenderFrameHostId() const;
  // Returns the ancestor RenderFrameHost if it still hosts the document that
  // created this worker, or nullptr otherwise.
  RenderFrameHostImpl* GetAncestorRenderFrameHost() const;
  DedicatedWorkerCreator GetCreator() const { return creator_; }
  const std::optional<GURL>& GetFinalResponseURL() const {
    return final_response_url_;
  }

  const base::UnguessableToken& network_restrictions_id() const {
    return network_restrictions_id_;
  }

  const base::UnguessableToken& creator_network_restrictions_id() const {
    return creator_network_restrictions_id_;
  }

  const PolicyContainerPolicies& creator_policies() const {
    return creator_policies_;
  }

  void CreateContentSecurityNotifier(
      mojo::PendingReceiver<blink::mojom::ContentSecurityNotifier> receiver);
  void CreateIdleManager(
      mojo::PendingReceiver<blink::mojom::IdleManager> receiver);
  void CreateNestedDedicatedWorker(
      mojo::PendingReceiver<blink::mojom::DedicatedWorkerHostFactory> receiver);
#if !BUILDFLAG(IS_ANDROID)
  void CreateDirectSocketsService(
      mojo::PendingReceiver<blink::mojom::DirectSocketsService> receiver);
#endif
  void CreateWebUsbService(
      mojo::PendingReceiver<blink::mojom::WebUsbService> receiver);
  void CreateWebSocketConnector(
      mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver);
  void CreateWebTransportConnector(
      mojo::PendingReceiver<blink::mojom::WebTransportConnector> receiver);
  void CreateWakeLockService(
      mojo::PendingReceiver<blink::mojom::WakeLockService> receiver);
  void BindCacheStorage(
      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
  void BindCacheStorageInternal(
      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver,
      const storage::BucketLocator& bucket_locator);
  void CreateCodeCacheHost(
      mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver);
  void CreateBroadcastChannelProvider(
      mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver);
  bool WasStorageAccessGranted();
  void CreateBlobUrlStoreProvider(
      mojo::PendingReceiver<blink::mojom::BlobURLStore> receiver);
  void CreateBucketManagerHost(
      mojo::PendingReceiver<blink::mojom::BucketManagerHost> receiver);
  void CreateLockManager(
      mojo::PendingReceiver<blink::mojom::LockManager> receiver);
  void GetFileSystemAccessManager(
      mojo::PendingReceiver<blink::mojom::FileSystemAccessManager> receiver);

#if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
  void BindPressureService(
      mojo::PendingReceiver<blink::mojom::WebPressureManager> receiver);
#endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)

  void BindSerialService(
      mojo::PendingReceiver<blink::mojom::SerialService> receiver);
#if !BUILDFLAG(IS_ANDROID)
  void BindHidService(mojo::PendingReceiver<blink::mojom::HidService> receiver);
#endif

  void StartScriptLoad(
      const GURL& script_url,
      network::mojom::CredentialsMode credentials_mode,
      blink::mojom::FetchClientSettingsObjectPtr
          outside_fetch_client_settings_object,
      mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token,
      mojo::Remote<blink::mojom::DedicatedWorkerHostFactoryClient> client,
      net::StorageAccessApiStatus storage_access_api_status);

  void ReportNoBinderForInterface(const std::string& error);

  const net::NetworkIsolationKey& GetNetworkIsolationKey() const {
    return isolation_info_.network_isolation_key();
  }

  const net::NetworkAnonymizationKey& GetNetworkAnonymizationKey() const {
    return isolation_info_.network_anonymization_key();
  }

  const base::UnguessableToken& GetReportingSource() const {
    return reporting_source_;
  }

  // Returns the client security state applied to subresource fetches.
  // May return nullptr before the script is loaded.
  const network::mojom::ClientSecurityState* client_security_state() const {
    return worker_client_security_state_.get();
  }

  const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy()
      const {
    DCHECK(worker_client_security_state_);
    return worker_client_security_state_->cross_origin_embedder_policy;
  }

  ServiceWorkerMainResourceHandle* service_worker_handle() {
    return service_worker_handle_.get();
  }

  bool file_url_support() const { return file_url_support_; }

#if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
  PressureServiceForDedicatedWorker* pressure_service() {
    return pressure_service_.get();
  }
#endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)

  // Exposed so that tests can swap the implementation and intercept calls.
  mojo::Receiver<blink::mojom::BrowserInterfaceBroker>&
  browser_interface_broker_receiver_for_testing() {
    return broker_receiver_;
  }

  // blink::mojom::BackForwardCacheControllerHost:
  void EvictFromBackForwardCache(
      blink::mojom::RendererEvictionReason reason,
      blink::mojom::ScriptSourceLocationPtr source) override;
  using BackForwardCacheBlockingDetails =
      std::vector<blink::mojom::BlockingDetailsPtr>;
  void DidChangeBackForwardCacheDisablingFeatures(
      BackForwardCacheBlockingDetails details) override;

  // BucketContext:
  blink::StorageKey GetBucketStorageKey() override;
  blink::mojom::PermissionStatus GetPermissionStatus(
      blink::PermissionType permission_type) override;
  void BindCacheStorageForBucket(
      const storage::BucketInfo& bucket,
      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) override;
  void GetSandboxedFileSystemForBucket(
      const storage::BucketInfo& bucket,
      const std::vector<std::string>& directory_path_components,
      blink::mojom::FileSystemAccessManager::GetSandboxedFileSystemCallback
          callback) override;
  storage::BucketClientInfo GetBucketClientInfo() const override;

  // Returns the features set that disable back-forward cache.
  blink::scheduler::WebSchedulerTrackedFeatures
  GetBackForwardCacheDisablingFeatures() const;

  const BackForwardCacheBlockingDetails& GetBackForwardCacheBlockingDetails()
      const;

  // This is called when out-of-process Network Service crashes,
  // or when DevTools updates its network interception.
  void UpdateSubresourceLoaderFactories();

  base::WeakPtr<ServiceWorkerClient> GetServiceWorkerClient();

  mojo::PendingRemote<blink::mojom::BackForwardCacheControllerHost>
  BindAndPassRemoteForBackForwardCacheControllerHost();

  base::WeakPtr<DedicatedWorkerHost> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

 private:
  // RenderProcessHostObserver:
  void RenderProcessExited(RenderProcessHost* render_process_host,
                           const ChildProcessTerminationInfo& info) override;
  void InProcessRendererExiting(RenderProcessHost* host) override;
  void RenderProcessHostDestroyed(RenderProcessHost* host) override;

  // LockObserver
  bool OnLockContention() override;

  // Called from `WorkerScriptFetcher`. Continues starting the dedicated worker
  // in the renderer process.
  void DidStartScriptLoad(std::optional<WorkerScriptFetcherResult> result);

  void ScriptLoadStartFailed(const network::URLLoaderCompletionStatus& status);

  StoragePartitionImpl* GetStoragePartitionImpl();

  // Sets up the observer of network service crash.
  void ObserveNetworkServiceCrash(StoragePartitionImpl* storage_partition_impl);

  // Creates a network factory for subresource requests from this worker. The
  // network factory is meant to be passed to the renderer.
  mojo::PendingRemote<network::mojom::URLLoaderFactory>
  CreateNetworkFactoryForSubresources(
      RenderFrameHostImpl* ancestor_render_frame_host,
      bool* bypass_redirect_checks);

  void OnNetworkServiceCrash();

  void OnMojoDisconnect();

  // Returns whether creator and worker's COEP values are compatible.
  bool CheckCOEP();

  base::WeakPtr<CrossOriginEmbedderPolicyReporter> GetWorkerCoepReporter();

  // This outlives `this` as follows:
  //  - StoragePartitionImpl owns DedicatedWorkerServiceImpl until its dtor.
  //  - StoragePartitionImpl outlives RenderProcessHostImpl.
  //  - RenderProcessHostImpl outlives DedicatedWorkerHost.
  // As the conclusion of the above, DedicatedWorkerServiceImpl outlives
  // DedicatedWorkerHost.
  const raw_ptr<DedicatedWorkerServiceImpl> service_;

  // The renderer generated ID of this worker, unique across all processes.
  const blink::DedicatedWorkerToken token_;

  // The RenderProcessHost that hosts this worker. This outlives `this`.
  const raw_ptr<RenderProcessHost> worker_process_host_;

  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
      scoped_process_host_observation_{this};

  // The creator of this worker. Holds a GlobalRenderFrameHostId if this worker
  // was created by a frame, or holds a blink::DedicatedWorkerToken if this
  // worker is nested.
  const DedicatedWorkerCreator creator_;

  // The document that owns this worker, either directly, or (in the case of
  // nested workers) indirectly via a tree of dedicated workers.
  const WeakDocumentPtr ancestor_document_;

  // The origin of the frame or dedicated worker that starts this worker.
  const url::Origin creator_origin_;

  // The storage key of this worker. This is used for storage partitioning and
  // for retrieving the origin of this worker
  // (https://html.spec.whatwg.org/C/#concept-settings-object-origin).
  const blink::StorageKey worker_storage_key_;

  // The origin used by this dedicated worker on the renderer side. This will
  // almost always be the same as `worker_storage_key_`'s origin, except in the
  // case of data: URL workers when the feature is disabled.
  // TODO(crbug.com/40051700): Remove this when the feature is enabled by
  // default.
  const url::Origin renderer_origin_;

  // The IsolationInfo associated with this worker. Same as that of the
  // frame or the worker that created this worker.
  const net::IsolationInfo isolation_info_;

  const base::UnguessableToken reporting_source_;

  // The client security state of the creator execution context. Never nullptr.
  // Copied at construction time.
  //
  // TODO(crbug.com/40054797): Consider removing this member once the
  // creator always outlives this instance. In that case, we could copy the
  // creator's client security state lazily instead of eagerly.
  const network::mojom::ClientSecurityStatePtr creator_client_security_state_;

  // The client security state of this worker, used for subresource fetches. It
  // is nullptr until the script's response head is loaded, at which point it is
  // calculated based on the response info. If the response is loaded from a URL
  // with a local scheme, then the worker inherits its creator's client security
  // state.
  network::mojom::ClientSecurityStatePtr worker_client_security_state_;

  // This is kept alive during the lifetime of the dedicated worker, since it's
  // associated with Mojo interfaces (ServiceWorkerContainer and
  // URLLoaderFactory) that are needed to stay alive while the worker is
  // starting or running.
  mojo::Remote<blink::mojom::DedicatedWorkerHostFactoryClient> client_;

  std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_;

#if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
  std::unique_ptr<PressureServiceForDedicatedWorker> pressure_service_;
#endif  // BUILDFLAG(ENABLE_COMPUTE_PRESSURE)

  // Script request URL used, only for DevTools and tracing. Only set after
  // `StartScriptLoad()`.
  GURL script_request_url_;

  // BrowserInterfaceBroker implementation through which this
  // DedicatedWorkerHost exposes worker-scoped Mojo services to the
  // corresponding worker in the renderer.
  //
  // The interfaces that can be requested from this broker are defined in the
  // content/browser/browser_interface_binders.cc file, in the functions which
  // take a `DedicatedWorkerHost*` parameter.
  BrowserInterfaceBrokerImpl<DedicatedWorkerHost, const url::Origin&> broker_{
      this};
  mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{
      &broker_};
  mojo::Receiver<blink::mojom::DedicatedWorkerHost> host_receiver_;
  mojo::Receiver<blink::mojom::BackForwardCacheControllerHost>
      back_forward_cache_controller_host_receiver_{this};

  // Indicates if subresource loaders of this worker support file URLs.
  bool file_url_support_ = false;

  // For observing Network Service connection errors only.
  mojo::Remote<network::mojom::URLLoaderFactory>
      network_service_connection_error_handler_holder_;
  mojo::Remote<blink::mojom::SubresourceLoaderUpdater>
      subresource_loader_updater_;

  // `coep_reporter_` is valid after DidStartScriptLoad() and remains non-null
  // for the lifetime of `this`.
  std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
  // TODO(crbug.com/40054797): Remove `creator_coep_reporter_` after this
  // class's lifetime is aligned with the associated frame.
  base::WeakPtr<CrossOriginEmbedderPolicyReporter> creator_coep_reporter_;

  // This is valid after DidStartScriptLoad() and remains non-null for the
  // lifetime of `this`.
  std::unique_ptr<DocumentIsolationPolicyReporter> dip_reporter_;

  // Will be set once the worker script started loading.
  std::optional<GURL> final_response_url_;

  // CodeCacheHost processes requests to fetch / write generated code for
  // JavaScript / WebAssembly resources.
  CodeCacheHostImpl::ReceiverSet code_cache_host_receivers_;

  BackForwardCacheBlockingDetails bfcache_blocking_details_;

  // This tracks whether the document that created this dedicated worker had
  // been granted storage access when the dedicated worker was created, which
  // also grants storage access to the dedicated worker.
  net::StorageAccessApiStatus storage_access_api_status_;

  // Nonce used to restrict network traffic for this worker.
  const base::UnguessableToken network_restrictions_id_;

  // Nonce used to restrict network traffic for the main script fetch of this
  // worker.
  const base::UnguessableToken creator_network_restrictions_id_;

  // The policies of the creator context. Used for inheritance.
  const PolicyContainerPolicies creator_policies_;

  base::WeakPtrFactory<DedicatedWorkerHost> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
