/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */

/* 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_net_HttpChannelParent_h
#define mozilla_net_HttpChannelParent_h

#include "ADivertableParentChannel.h"
#include "nsHttp.h"
#include "mozilla/net/PHttpChannelParent.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/MozPromise.h"
#include "nsIObserver.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIProgressEventSink.h"
#include "nsHttpChannel.h"
#include "nsIAuthPromptProvider.h"
#include "mozilla/dom/ipc/IdType.h"
#include "nsIDeprecationWarner.h"

class nsICacheEntry;
class nsIAssociatedContentSecurity;

#define HTTP_CHANNEL_PARENT_IID \
  { 0x982b2372, 0x7aa5, 0x4e8a, \
      { 0xbd, 0x9f, 0x89, 0x74, 0xd7, 0xf0, 0x58, 0xeb } }

namespace mozilla {

namespace dom{
class TabParent;
class PBrowserOrId;
} // namespace dom

namespace net {

class HttpBackgroundChannelParent;
class HttpChannelParentListener;
class ChannelEventQueue;

// Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
// works correctly on this object, as it's needed to compute a void* pointing to
// the beginning of this object.

class HttpChannelParent final : public nsIInterfaceRequestor
                              , public PHttpChannelParent
                              , public nsIParentRedirectingChannel
                              , public nsIProgressEventSink
                              , public ADivertableParentChannel
                              , public nsIAuthPromptProvider
                              , public nsIDeprecationWarner
                              , public HttpChannelSecurityWarningReporter
                              , public nsIAsyncVerifyRedirectReadyCallback
{
  virtual ~HttpChannelParent();

public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIREQUESTOBSERVER
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSIPARENTCHANNEL
  NS_DECL_NSIPARENTREDIRECTINGCHANNEL
  NS_DECL_NSIPROGRESSEVENTSINK
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSIAUTHPROMPTPROVIDER
  NS_DECL_NSIDEPRECATIONWARNER
  NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK

  NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)

  HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
                    nsILoadContext* aLoadContext,
                    PBOverrideStatus aStatus);

  MOZ_MUST_USE bool Init(const HttpChannelCreationArgs& aOpenArgs);

  // ADivertableParentChannel functions.
  void DivertTo(nsIStreamListener *aListener) override;
  MOZ_MUST_USE nsresult SuspendForDiversion() override;
  MOZ_MUST_USE nsresult SuspendMessageDiversion() override;
  MOZ_MUST_USE nsresult ResumeMessageDiversion() override;
  MOZ_MUST_USE nsresult CancelDiversion() override;

  // Calls OnStartRequest for "DivertTo" listener, then notifies child channel
  // that it should divert OnDataAvailable and OnStopRequest calls to this
  // parent channel.
  void StartDiversion();

  // Handles calling OnStart/Stop if there are errors during diversion.
  // Called asynchronously from FailDiversion.
  void NotifyDiversionFailed(nsresult aErrorCode);

  // Forwarded to nsHttpChannel::SetApplyConversion.
  void SetApplyConversion(bool aApplyConversion) {
    if (mChannel) {
      mChannel->SetApplyConversion(aApplyConversion);
    }
  }

  MOZ_MUST_USE nsresult OpenAlternativeOutputStream(const nsACString & type,
                                                    nsIOutputStream * *_retval);

  // Callbacks for each asynchronous tasks required in AsyncOpen
  // procedure, will call InvokeAsyncOpen when all the expected
  // tasks is finished successfully or when any failure happened.
  // @see mAsyncOpenBarrier.
  void TryInvokeAsyncOpen(nsresult aRv);

  void InvokeAsyncOpen(nsresult rv);

  // Calls SendSetPriority if mIPCClosed is false.
  void DoSendSetPriority(int16_t aValue);

  // Callback while background channel is ready.
  void OnBackgroundParentReady(HttpBackgroundChannelParent* aBgParent);
  // Callback while background channel is destroyed.
  void OnBackgroundParentDestroyed();

  base::ProcessId OtherPid() const override;

protected:
  // used to connect redirected-to channel in parent with just created
  // ChildChannel.  Used during redirects.
  MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                   const bool& shouldIntercept);

  MOZ_MUST_USE bool
  DoAsyncOpen(const URIParams&           uri,
              const OptionalURIParams&   originalUri,
              const OptionalURIParams&   docUri,
              const OptionalURIParams&   referrerUri,
              const uint32_t&            referrerPolicy,
              const OptionalURIParams&   internalRedirectUri,
              const OptionalURIParams&   topWindowUri,
              const uint32_t&            loadFlags,
              const RequestHeaderTuples& requestHeaders,
              const nsCString&           requestMethod,
              const OptionalIPCStream&   uploadStream,
              const bool&                uploadStreamHasHeaders,
              const int16_t&             priority,
              const uint32_t&            classOfService,
              const uint8_t&             redirectionLimit,
              const bool&                allowSTS,
              const uint32_t&            thirdPartyFlags,
              const bool&                doResumeAt,
              const uint64_t&            startPos,
              const nsCString&           entityID,
              const bool&                chooseApplicationCache,
              const nsCString&           appCacheClientID,
              const bool&                allowSpdy,
              const bool&                allowAltSvc,
              const bool&                beConservative,
              const uint32_t&            tlsFlags,
              const OptionalLoadInfoArgs& aLoadInfoArgs,
              const OptionalHttpResponseHead& aSynthesizedResponseHead,
              const nsCString&           aSecurityInfoSerialization,
              const uint32_t&            aCacheKey,
              const uint64_t&            aRequestContextID,
              const OptionalCorsPreflightArgs& aCorsPreflightArgs,
              const uint32_t&            aInitialRwin,
              const bool&                aBlockAuthPrompt,
              const bool&                aSuspendAfterSynthesizeResponse,
              const bool&                aAllowStaleCacheContent,
              const nsCString&           aContentTypeHint,
              const uint64_t&            aChannelId,
              const uint64_t&            aContentWindowId,
              const nsCString&           aPreferredAlternativeType,
              const uint64_t&            aTopLevelOuterContentWindowId,
              const TimeStamp&           aLaunchServiceWorkerStart,
              const TimeStamp&           aLaunchServiceWorkerEnd,
              const TimeStamp&           aDispatchFetchEventStart,
              const TimeStamp&           aDispatchFetchEventEnd,
              const TimeStamp&           aHandleFetchEventStart,
              const TimeStamp&           aHandleFetchEventEnd,
              const bool&                aForceMainDocumentChannel);

  virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
  virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;
  virtual mozilla::ipc::IPCResult RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
  virtual mozilla::ipc::IPCResult RecvSuspend() override;
  virtual mozilla::ipc::IPCResult RecvResume() override;
  virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override;
  virtual mozilla::ipc::IPCResult RecvRedirect2Verify(const nsresult& result,
                                                      const RequestHeaderTuples& changedHeaders,
                                                      const uint32_t& loadFlags,
                                                      const uint32_t& referrerPolicy,
                                                      const OptionalURIParams& aReferrerURI,
                                                      const OptionalURIParams& apiRedirectUri,
                                                      const OptionalCorsPreflightArgs& aCorsPreflightArgs,
                                                      const bool& aChooseAppcache) override;
  virtual mozilla::ipc::IPCResult RecvUpdateAssociatedContentSecurity(const int32_t& broken,
                                                   const int32_t& no) override;
  virtual mozilla::ipc::IPCResult RecvDocumentChannelCleanup(const bool& clearCacheEntry) override;
  virtual mozilla::ipc::IPCResult RecvMarkOfflineCacheEntryAsForeign() override;
  virtual mozilla::ipc::IPCResult RecvDivertOnDataAvailable(const nsCString& data,
                                         const uint64_t& offset,
                                         const uint32_t& count) override;
  virtual mozilla::ipc::IPCResult RecvDivertOnStopRequest(const nsresult& statusCode) override;
  virtual mozilla::ipc::IPCResult RecvDivertComplete() override;
  virtual mozilla::ipc::IPCResult RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
                                                                    const mozilla::ipc::PrincipalInfo& requestingPrincipal) override;
  virtual void ActorDestroy(ActorDestroyReason why) override;

  // Supporting function for ADivertableParentChannel.
  MOZ_MUST_USE nsresult ResumeForDiversion();

  // Asynchronously calls NotifyDiversionFailed.
  void FailDiversion(nsresult aErrorCode);

  friend class HttpChannelParentListener;
  RefPtr<mozilla::dom::TabParent> mTabParent;

  MOZ_MUST_USE nsresult
  ReportSecurityMessage(const nsAString& aMessageTag,
                        const nsAString& aMessageCategory) override;
  nsresult LogBlockedCORSRequest(const nsAString& aMessage) override;

  // Calls SendDeleteSelf and sets mIPCClosed to true because we should not
  // send any more messages after that. Bug 1274886
  MOZ_MUST_USE bool DoSendDeleteSelf();
  // Called to notify the parent channel to not send any more IPC messages.
  virtual mozilla::ipc::IPCResult RecvDeletingChannel() override;
  virtual mozilla::ipc::IPCResult RecvFinishInterceptedRedirect() override;

private:
  void UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut);

  void DivertOnDataAvailable(const nsCString& data,
                             const uint64_t& offset,
                             const uint32_t& count);
  void DivertOnStopRequest(const nsresult& statusCode);
  void DivertComplete();
  void MaybeFlushPendingDiversion();
  void ResponseSynthesized();

  // final step for Redirect2Verify procedure, will be invoked while both
  // redirecting and redirected channel are ready or any error happened.
  // OnRedirectVerifyCallback will be invoked for finishing the async
  // redirect verification procedure.
  void ContinueRedirect2Verify(const nsresult& aResult);

  void AsyncOpenFailed(nsresult aRv);

  // Request to pair with a HttpBackgroundChannelParent with the same channel
  // id, a promise will be returned so the caller can append callbacks on it.
  // If called multiple times before mBgParent is available, the same promise
  // will be returned and the callbacks will be invoked in order.
  already_AddRefed<GenericPromise> WaitForBgParent();

  // Remove the association with background channel after main-thread IPC
  // is about to be destroyed or no further event is going to be sent, i.e.,
  // DocumentChannelCleanup.
  void CleanupBackgroundChannel();

  friend class HttpBackgroundChannelParent;
  friend class DivertDataAvailableEvent;
  friend class DivertStopRequestEvent;
  friend class DivertCompleteEvent;

  RefPtr<HttpBaseChannel>       mChannel;
  nsCOMPtr<nsICacheEntry>       mCacheEntry;
  nsCOMPtr<nsIAssociatedContentSecurity>  mAssociatedContentSecurity;
  bool mIPCClosed;                // PHttpChannel actor has been Closed()

  nsCOMPtr<nsIChannel> mRedirectChannel;
  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;

  nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;

  // OnStatus is always called before OnProgress.
  // Set true in OnStatus if next OnProgress can be ignored
  // since the information can be recontructed from ODA.
  bool mIgnoreProgress              : 1;

  bool mSentRedirect1BeginFailed    : 1;
  bool mReceivedRedirect2Verify     : 1;

  PBOverrideStatus mPBOverride;

  nsCOMPtr<nsILoadContext> mLoadContext;
  RefPtr<nsHttpHandler>  mHttpHandler;

  RefPtr<HttpChannelParentListener> mParentListener;
  // The listener we are diverting to or will divert to if mPendingDiversion
  // is set.
  nsCOMPtr<nsIStreamListener> mDivertListener;
  // Set to the canceled status value if the main channel was canceled.
  nsresult mStatus;
  // Indicates that diversion has been requested, but we could not start it
  // yet because the channel is still being opened with a synthesized response.
  bool mPendingDiversion;
  // Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
  // must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
  // received from the child channel.
  bool mDivertingFromChild;

  // Set if OnStart|StopRequest was called during a diversion from the child.
  bool mDivertedOnStartRequest;

  bool mSuspendedForDiversion;

  // Set if this channel should be suspended after synthesizing a response.
  bool mSuspendAfterSynthesizeResponse;
  // Set if this channel will synthesize its response.
  bool mWillSynthesizeResponse;

  dom::TabId mNestedFrameId;

  RefPtr<ChannelEventQueue> mEventQ;

  RefPtr<HttpBackgroundChannelParent> mBgParent;

  // Number of events to wait before actually invoking AsyncOpen on the main
  // channel. For each asynchronous step required before InvokeAsyncOpen, should
  // increase 1 to mAsyncOpenBarrier and invoke TryInvokeAsyncOpen after
  // finished. This attribute is main thread only.
  uint8_t mAsyncOpenBarrier = 0;

  // Corresponding redirect channel registrar Id. 0 means redirection is not started.
  uint32_t mRedirectRegistrarId = 0;

  MozPromiseHolder<GenericPromise> mPromise;
  MozPromiseRequestHolder<GenericPromise> mRequest;
};

NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
                              HTTP_CHANNEL_PARENT_IID)

} // namespace net
} // namespace mozilla

#endif // mozilla_net_HttpChannelParent_h
