// Copyright 2014 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_SHARED_WORKER_HOST_H_
#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_HOST_H_

#include <list>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/supports_user_data.h"
#include "base/unguessable_token.h"
#include "content/browser/back_forward_cache/back_forward_cache_metrics.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/common/content_export.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/shared_worker_instance.h"
#include "media/mojo/mojom/video_decode_perf_history.mojom.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/network_isolation_key.h"
#include "services/device/public/cpp/compute_pressure/buildflags.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/cross_origin_embedder_policy.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.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/buckets/bucket_manager_host.mojom.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
#include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom-forward.h"
#include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
#include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom.h"
#include "third_party/blink/public/mojom/worker/shared_worker.mojom.h"
#include "third_party/blink/public/mojom/worker/shared_worker_client.mojom.h"
#include "third_party/blink/public/mojom/worker/shared_worker_factory.mojom.h"
#include "third_party/blink/public/mojom/worker/shared_worker_host.mojom.h"
#include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"

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

class GURL;

namespace blink {
class MessagePortChannel;
class StorageKey;
}  // namespace blink

namespace content {

class ContentBrowserClient;
class CrossOriginEmbedderPolicyReporter;
class DocumentIsolationPolicyReporter;
class ServiceWorkerMainResourceHandle;
class SharedWorkerContentSettingsProxyImpl;
class SharedWorkerServiceImpl;
class SiteInstanceImpl;
class StoragePartitionImpl;
struct WorkerScriptFetcherResult;

// SharedWorkerHost is the browser-side host of a single shared worker running
// in the renderer. This class is owned by the SharedWorkerServiceImpl of the
// current BrowserContext.
class CONTENT_EXPORT SharedWorkerHost
    : public blink::mojom::SharedWorkerHost,
      public RenderProcessHostObserver,
      public LockManager<storage::BucketId>::Observer,
      public BucketContext,
      public base::SupportsUserData {
 public:
  SharedWorkerHost(
      SharedWorkerServiceImpl* service,
      const SharedWorkerInstance& instance,
      scoped_refptr<SiteInstanceImpl> site_instance,
      std::vector<network::mojom::ContentSecurityPolicyPtr>
          content_security_policies,
      scoped_refptr<PolicyContainerHost> creator_policy_container_host);

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

  ~SharedWorkerHost() override;

  // Returns the RenderProcessHost where this shared worker lives.
  // SharedWorkerHost can't outlive the RenderProcessHost so this can't be null.
  RenderProcessHost* GetProcessHost() const;

  // Starts the SharedWorker in the renderer process.
  //
  // |outside_fetch_client_settings_object| is used for loading the shared
  // worker main script by the browser process, sent to the renderer process,
  // and then used to load the script.
  //
  // |client| is used to determine the IP address space of the worker if the
  // script is fetched from a URL with a special scheme known only to the
  // embedder.
  //
  // `result` contains the worker main script fetch result.
  void Start(mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory,
             blink::mojom::FetchClientSettingsObjectPtr
                 outside_fetch_client_settings_object,
             ContentBrowserClient* client,
             WorkerScriptFetcherResult result);

  void AllowFileSystem(const GURL& url,
                       base::OnceCallback<void(bool)> callback);
  void AllowIndexedDB(const GURL& url, base::OnceCallback<void(bool)> callback);
  void AllowCacheStorage(const GURL& url,
                         base::OnceCallback<void(bool)> callback);
  void AllowWebLocks(const GURL& url, base::OnceCallback<void(bool)> callback);

  void CreateWebTransportConnector(
      mojo::PendingReceiver<blink::mojom::WebTransportConnector> receiver);
  void CreateWebSocketConnector(
      mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver);
  void BindCacheStorage(
      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
  void CreateBroadcastChannelProvider(
      mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver);
  void CreateBlobUrlStoreProvider(
      mojo::PendingReceiver<blink::mojom::BlobURLStore> receiver);
  void CreateBucketManagerHost(
      mojo::PendingReceiver<blink::mojom::BucketManagerHost> receiver);
  void CreateLockManager(
      mojo::PendingReceiver<blink::mojom::LockManager> receiver);

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

  // Causes this instance to be deleted, which will terminate the worker. May
  // be done based on a UI action.
  void Destruct();

  void DestructIfNoClients();

  void AddClient(mojo::PendingRemote<blink::mojom::SharedWorkerClient> client,
                 GlobalRenderFrameHostId client_render_frame_host_id,
                 const blink::MessagePortChannel& port);

  void SetServiceWorkerHandle(
      std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle);

  // Removes all clients whose RenderFrameHost has been destroyed before the
  // shared worker was started.
  void PruneNonExistentClients();

  // Returns true if this worker is connected to at least one client.
  bool HasClients() const;

  // Returns true if the worker has a client with `render_frame_host`.
  bool ContainsClient(const RenderFrameHostImpl* render_frame_host) const;

  // Evicts other BFCached clients and returns true if `render_frame_host` is
  // the last active client.
  bool EvictBFCachedClientsIfLastActive(RenderFrameHostImpl* render_frame_host);

  // Updates the worker's freeze state based on client status. This is called
  // when a client transitions to/from the BackForwardCache, or when a client is
  // added or removed.
  void OnClientStateChanged();

  // Returns the frame ids of this worker's clients.
  std::vector<GlobalRenderFrameHostId> GetRenderFrameIDsForWorker();

  SiteInstanceImpl* site_instance() { return site_instance_.get(); }

  bool started() const { return started_; }

  const GURL& final_response_url() const { return final_response_url_; }

  const blink::SharedWorkerToken& token() const { return token_; }

  const SharedWorkerInstance& instance() const { return instance_; }

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

  const network::mojom::ClientSecurityStatePtr& client_security_state() const {
    return worker_client_security_state_;
  }

  const std::vector<network::mojom::ContentSecurityPolicyPtr>&
  content_security_policies() const {
    return content_security_policies_;
  }

#if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
  PressureServiceForSharedWorker* 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_;
  }

  ukm::SourceId ukm_source_id() const { return ukm_source_id_; }

  const base::UnguessableToken& GetDevToolsToken() const;

  // Signals the remote worker to terminate and returns the mojo::Remote
  // instance so the caller can be notified when the connection is lost. Should
  // be called right before deleting this instance.
  mojo::Remote<blink::mojom::SharedWorker> TerminateRemoteWorkerForTesting();

  void set_client_security_state_for_testing(
      network::mojom::ClientSecurityStatePtr client_security_state) {
    worker_client_security_state_ = std::move(client_security_state);
  }

  // Computes the IsolationInfo used for WebSocket connections from this worker.
  net::IsolationInfo ComputeIsolationInfoForWebSocket() const;

  base::WeakPtr<SharedWorkerHost> AsWeakPtr();

  net::NetworkIsolationKey GetNetworkIsolationKey() const;

  net::NetworkAnonymizationKey GetNetworkAnonymizationKey() const;

  const blink::StorageKey& GetWorkerStorageKey() const;

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

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

  const PolicyContainerPolicies& creator_policies() const {
    return creator_policy_container_host_->policies();
  }

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

  void CreateCodeCacheHost(
      mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver);

  // Creates a network factory params for subresource requests from this worker.
  network::mojom::URLLoaderFactoryParamsPtr
  CreateNetworkFactoryParamsForSubresources();

  // 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::BucketHost::GetDirectoryCallback callback) override;
  storage::BucketClientInfo GetBucketClientInfo() const override;

 private:
  friend class SharedWorkerHostTest;

  class ScopedDevToolsHandle;
  class ScopedProcessHostRef;

  // Contains information about a client connecting to this shared worker.
  struct ClientInfo {
    ClientInfo(mojo::Remote<blink::mojom::SharedWorkerClient> client,
               int connection_request_id,
               GlobalRenderFrameHostId render_frame_host_id);
    ~ClientInfo();
    mojo::Remote<blink::mojom::SharedWorkerClient> client;
    const int connection_request_id;
    const GlobalRenderFrameHostId render_frame_host_id;
  };

  using ClientList = std::list<ClientInfo>;

  // Returns true if the COEP policy of the worker and the creator are
  // compatible.
  bool CheckCrossOriginEmbedderPolicy(
      network::CrossOriginEmbedderPolicy creator_cross_origin_embedder_policy,
      network::CrossOriginEmbedderPolicy worker_cross_origin_embedder_policy);

  bool HasActiveClients() const;

  // blink::mojom::SharedWorkerHost methods:
  void OnConnected(int connection_request_id) override;
  void OnContextClosed() override;
  void OnReadyForInspection(
      mojo::PendingRemote<blink::mojom::DevToolsAgent>,
      mojo::PendingReceiver<blink::mojom::DevToolsAgentHost>) override;
  void OnScriptLoadFailed(const std::string& error_message) override;
  void OnReportException(
      blink::mojom::SharedWorkerExceptionDetailsPtr details) override;
  void OnFeatureUsed(blink::mojom::WebFeature feature) override;

  // RenderProcessHostObserver methods:
  void RenderProcessHostDestroyed(RenderProcessHost* host) override;

  void AllowFileSystemResponse(base::OnceCallback<void(bool)> callback,
                               bool allowed);
  void OnClientConnectionLost();
  void OnWorkerConnectionLost();

  // LockObserver
  bool OnLockContention() override;

  void BindCacheStorageInternal(
      mojo::PendingReceiver<blink::mojom::CacheStorage> receiver,
      const storage::BucketLocator& bucket_locator);

  void FreezeIfNoActiveClient();

  // 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(bool* bypass_redirect_checks);

  StoragePartitionImpl* GetStoragePartitionImpl();

  mojo::Receiver<blink::mojom::SharedWorkerHost> receiver_{this};

  // |service_| owns |this|.
  const raw_ptr<SharedWorkerServiceImpl> service_;

  // An identifier for this worker that is unique across all workers. This is
  // generated by this object in the browser process.
  const blink::SharedWorkerToken token_;

  // This holds information used to match a shared worker connection request to
  // this shared worker.
  SharedWorkerInstance instance_;
  ClientList clients_;

  std::vector<network::mojom::ContentSecurityPolicyPtr>
      content_security_policies_;

  mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver_;
  mojo::Remote<blink::mojom::SharedWorker> worker_;

  // A SiteInstance whose process the shared worker runs in.
  const scoped_refptr<SiteInstanceImpl> site_instance_;

  // Keep alive the renderer process that will be hosting the shared worker.
  std::unique_ptr<ScopedProcessHostRef> scoped_process_host_ref_;

  int next_connection_request_id_;

  std::unique_ptr<ScopedDevToolsHandle> devtools_handle_;

  // This is the set of features that this worker has used.
  std::set<blink::mojom::WebFeature> used_features_;

  std::unique_ptr<SharedWorkerContentSettingsProxyImpl> content_settings_;

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

  // This is kept alive during the lifetime of the shared 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::SharedWorkerFactory> factory_;

  BrowserInterfaceBrokerImpl<SharedWorkerHost, const url::Origin&> broker_{
      this};
  mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{
      &broker_};

  std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_;

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

  // Indicates if Start() was invoked on this instance.
  bool started_ = false;

  // Indicates if the worker is frozen.
  bool is_frozen_ = false;

  GURL final_response_url_;

  const ukm::SourceId ukm_source_id_;

  const base::UnguessableToken reporting_source_;

  // Set at construction time and should not change afterwards.
  const scoped_refptr<PolicyContainerHost> creator_policy_container_host_;

  // The worker's own client security state, applied to subresource fetches.
  // This is nullptr until it is computed in `DidStartScriptLoad()`.
  network::mojom::ClientSecurityStatePtr worker_client_security_state_;

  std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;

  std::unique_ptr<DocumentIsolationPolicyReporter> dip_reporter_;

  const base::UnguessableToken network_restrictions_id_;

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

}  // namespace content

#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_HOST_H_
