/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_PrefetchRecordParent_h
#define mozilla_dom_PrefetchRecordParent_h

#include "mozilla/Maybe.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/PPrefetchRecordParent.h"
#include "mozilla/dom/PrefetchTypes.h"
#include "mozilla/dom/ReferrerPolicyBinding.h"
#include "nsCOMPtr.h"
#include "nsIChannel.h"
#include "nsIChannelEventSink.h"
#include "nsIInputStream.h"
#include "nsIInterfaceRequestor.h"
#include "nsIReferrerInfo.h"
#include "nsIRequestObserver.h"
#include "nsIStreamListener.h"
#include "nsIURI.h"
#include "nsString.h"
#include "nsTArray.h"

namespace mozilla::dom {

class WindowGlobalParent;

// Parent-side actor for a speculation rules prefetch record.
// Authoritative: holds the prefetch record fields, opens the HTTP channel,
// and serves cache-key lookup at navigation activation.
//
// Spec:
// https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record
class PrefetchRecordParent final : public PPrefetchRecordParent,
                                   public nsIStreamListener,
                                   public nsIInterfaceRequestor,
                                   public nsIChannelEventSink {
 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIREQUESTOBSERVER
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSICHANNELEVENTSINK

  // Called from WindowGlobalParent::AllocPPrefetchRecordParent.
  // aWGP is passed explicitly because Manager() is not yet set at alloc time.
  // Implements "start a referrer-initiated navigational prefetch".
  // Spec:
  // https://wicg.github.io/nav-speculation/prefetch.html#start-a-referrer-initiated-navigational-prefetch
  void Init(WindowGlobalParent* aWGP, const SpeculativePrefetchArgs& aArgs);

  // Accessors for prefetch record fields
  // (https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record) used
  // by Find / Wait / Activation algorithms.
  nsIURI* URL() const { return mURL; }
  PrefetchAnonymizationPolicy AnonymizationPolicy() const {
    return mAnonPolicy;
  }
  PrefetchState State() const { return mState; }
  PrefetchSource Source() const { return mSource; }
  const nsTArray<ExchangeRecord>& RedirectChain() const {
    return mRedirectChain;
  }
  const TimeStamp& ExpiryTime() const { return mExpiryTime; }
  const nsString& SourcePartitionKey() const { return mSourcePartitionKey; }
  const nsString& IsolatedPartitionKey() const { return mIsolatedPartitionKey; }
  bool HadConflictingCredentials() const { return mHadConflictingCredentials; }
  const nsTArray<Maybe<nsString>>& Tags() const { return mTags; }
  const nsString& NoVarySearchHint() const { return mNoVarySearchHint; }

  // (https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record-cancel-and-discard).
  mozilla::ipc::IPCResult RecvCancel();

  // Implements "trigger a prefetch status updated event".
  // Spec:
  // https://wicg.github.io/nav-speculation/prefetch.html#trigger-a-prefetch-status-updated-event
  void FirePrefetchStatusUpdated(bool aSuccess);

  // Cancels the fetch and marks state=Canceled. Idempotent.
  // Called by RecvCancel and WindowGlobalParent::DedupePrefetchRecords.
  void MarkCanceled();

  // Implements "prefetch record matches a URL" using the completed response's
  // No-Vary-Search header.
  // Spec:
  // https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record-matches-a-url
  bool MatchesURL(nsIURI* aURL) const;

  // Implements "prefetch record is expected to match a URL": the pre-response
  // variant used by "wait for a matching prefetch record" to check ongoing
  // records. Uses the rule's noVarySearchHint instead of the response header.
  // Spec:
  // https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record-is-expected-to-match-a-url
  bool IsExpectedToMatch(nsIURI* aURL) const;

  void ActorDestroy(ActorDestroyReason aReason) override;

 private:
  bool IsCrossOriginToDocument(nsIURI* aURI) const;
  void PopulateIsolatedPartitionKey(mozilla::OriginAttributes& aAttrs);
  void ConfigureSecPurpose(nsIChannel* aChannel);
  bool PrefetchIPAnonymizationPolicyRequiresAnonymity(nsIURI* aURI) const;
  void AppendRedirectChainEntry(nsIURI* aURI);
  void FillResponseOnLastEntry(nsIChannel* aChannel);
  nsString ComputePartitionKeyForChannel(nsIChannel* aChannel) const;
  bool IsReferrerPolicySufficientlyStrict() const;

  // Prefetch record fields:
  // https://wicg.github.io/nav-speculation/prefetch.html#prefetch-record
  nsTArray<Maybe<nsString>> mTags;
  nsCOMPtr<nsIURI> mURL;
  PrefetchAnonymizationPolicy mAnonPolicy = PrefetchAnonymizationPolicy::None;
  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
  nsString mNoVarySearchHint;
  PrefetchSource mSource = PrefetchSource::SpeculationRules;
  PrefetchState mState = PrefetchState::Ongoing;
  nsCOMPtr<nsIChannel> mChannel;
  nsTArray<ExchangeRecord> mRedirectChain;
  TimeStamp mStartTime;
  TimeStamp mExpiryTime;
  nsString mSourcePartitionKey;
  nsString mIsolatedPartitionKey;
  bool mHadConflictingCredentials = false;

  // Tracks incoming body bytes for the body cap.
  uint64_t mBytesReceived = 0;

 protected:
  ~PrefetchRecordParent() = default;
};

}  // namespace mozilla::dom

#endif  // mozilla_dom_PrefetchRecordParent_h
