// Copyright 2015 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_MAIN_RESOURCE_HANDLE_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_MAIN_RESOURCE_HANDLE_H_

#include "base/memory/weak_ptr.h"
#include "content/browser/service_worker/service_worker_accessed_callback.h"
#include "content/common/content_export.h"
#include "net/base/isolation_info.h"

namespace network {
struct ResourceRequest;
}

namespace content {

class ScopedServiceWorkerClient;
class ServiceWorkerClient;
class ServiceWorkerContextWrapper;

// The lifetime of the ServiceWorkerMainResourceHandle:
//   1) We create a ServiceWorkerMainResourceHandle without populating the
//   member service worker client.
//
//   2) If we pre-create a ServiceWorkerClient for this navigation, it
//   is passed to `set_service_worker_client()`.
//
//   3) When the navigation is ready to commit, the NavigationRequest will
//   call ScopedServiceWorkerClient::CommitResponse() to
//     - complete the initialization for the ServiceWorkerClient.
//     - take out the container info to be sent as part of navigation commit
//       IPC.
//
//   4) When the navigation finishes, the ServiceWorkerMainResourceHandle is
//   destroyed. The destructor of the ServiceWorkerMainResourceHandle destroys
//   the ScopedServiceWorkerClient which in turn leads to the destruction of an
//   unclaimed ServiceWorkerClient.
class CONTENT_EXPORT ServiceWorkerMainResourceHandle {
 public:
  ServiceWorkerMainResourceHandle(
      scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
      ServiceWorkerAccessedCallback on_service_worker_accessed,
      std::string fetch_event_client_id,
      base::WeakPtr<ServiceWorkerClient> parent_service_worker_client =
          nullptr);

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

  ~ServiceWorkerMainResourceHandle();

  ScopedServiceWorkerClient* scoped_service_worker_client() {
    return scoped_service_worker_client_.get();
  }

  void set_service_worker_client(
      ScopedServiceWorkerClient scoped_service_worker_client,
      const net::IsolationInfo& isolation_info);

  base::WeakPtr<ServiceWorkerClient> service_worker_client();

  base::WeakPtr<ServiceWorkerClient> parent_service_worker_client() {
    return parent_service_worker_client_;
  }
  const std::string& fetch_event_client_id() const {
    return fetch_event_client_id_;
  }

  const ServiceWorkerAccessedCallback& service_worker_accessed_callback() {
    return service_worker_accessed_callback_;
  }

  ServiceWorkerContextWrapper* context_wrapper() {
    return context_wrapper_.get();
  }

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

  static std::optional<url::Origin> TopFrameOriginForInitializeForRequest(
      const network::ResourceRequest& resource_request);

  // Updates `ServiceWorkerClient` and `isolation_info_` for the next request.
  // Must be called on the initial request and every redirect requests.
  // `url` and `top_frame_origin` must come from the same ResourceRequest
  // through network::ResourceRequest::url and the above
  // TopFrameOriginForInitializeForRequest();
  // `client_for_prefetch` is non-null when serving the request from prefetch
  // and inheriting the controller from `client_for_prefetch`.
  // Returns `false` if `client_for_prefetch` is set but can't be used. In such
  // cases, `this` and underlying `service_worker_client()` remains unchanged.
  bool InitializeForRequest(const GURL& url,
                            std::optional<url::Origin> top_frame_origin,
                            const ServiceWorkerClient* client_for_prefetch);

 private:
  // In term of the spec, this is the request's reserved client
  // https://fetch.spec.whatwg.org/#concept-request-reserved-client
  // that works as the service worker client during the main resource fetch
  // https://w3c.github.io/ServiceWorker/#dfn-service-worker-client
  // and subsequently passed as navigation param's reserved environment
  // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigation-params-reserved-environment
  //
  // The controller of `service_worker_client` can change during navigation
  // fetch (e.g. when controller is lost or `skipWaiting()` is called) and thus
  // can be different from the `ServiceWorkerVersion` that intercepted the main
  // resource request, and the latest controller should be used as the initial
  // controller of the to-be-created global scope.
  std::unique_ptr<ScopedServiceWorkerClient> scoped_service_worker_client_;

  // Only set and used for workers with a blob URL.
  const base::WeakPtr<ServiceWorkerClient> parent_service_worker_client_;

  // FetchEvent.clientId
  // https://w3c.github.io/ServiceWorker/#fetch-event-clientid
  //
  // TODO(crbug.com/368087661): In the spec, this should be navigation request's
  // client's ID, so `fetch_event_client_id_` and
  // `parent_service_worker_client_` should be merged as e.g.
  // `fetch_request_client_`.
  // https://fetch.spec.whatwg.org/#concept-request-client
  // But this hasn't been the case in the implementation, so currently they are
  // plumbed separately here.
  const std::string fetch_event_client_id_;

  // Updated on redirects.
  // TODO(https://crbug.com/367755492): This is managed separately from
  // `network::ResourceRequest::TrustedParams::isolation_info` but both of the
  // two `IsolationInfo`s are used/mixed in the code. Investigate why and
  // clarify the logic.
  net::IsolationInfo isolation_info_;

  const ServiceWorkerAccessedCallback service_worker_accessed_callback_;

  const scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;

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

}  // namespace content

#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_MAIN_RESOURCE_HANDLE_H_
