/* -*- 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 "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 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
{
  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_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)

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

  bool Init(const HttpChannelCreationArgs& aOpenArgs);

  // ADivertableParentChannel functions.
  void DivertTo(nsIStreamListener *aListener) override;
  nsresult SuspendForDiversion() override;
  nsresult SuspendMessageDiversion() override;
  nsresult ResumeMessageDiversion() 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, bool aSkipResume = true);

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

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

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

  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 uint16_t&            priority,
                   const uint32_t&            classOfService,
                   const uint8_t&             redirectionLimit,
                   const bool&                allowPipelining,
                   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 OptionalLoadInfoArgs& aLoadInfoArgs,
                   const OptionalHttpResponseHead& aSynthesizedResponseHead,
                   const nsCString&           aSecurityInfoSerialization,
                   const uint32_t&            aCacheKey,
                   const nsCString&           aRequestContextID,
                   const OptionalCorsPreflightArgs& aCorsPreflightArgs,
                   const uint32_t&            aInitialRwin,
                   const bool&                aBlockAuthPrompt,
                   const bool&                aSuspendAfterSynthesizeResponse,
                   const bool&                aAllowStaleCacheContent,
                   const nsCString&           aContentTypeHint,
                   const nsCString&           aChannelId,
                   const uint64_t&            aContentWindowId,
                   const nsCString&           aPreferredAlternativeType);

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

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

  // Asynchronously calls NotifyDiversionFailed.
  void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);

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

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

  // Calls SendDeleteSelf and sets mIPCClosed to true because we should not
  // send any more messages after that. Bug 1274886
  bool DoSendDeleteSelf();
  // Called to notify the parent channel to not send any more IPC messages.
  virtual bool RecvDeletingChannel() override;
  virtual bool 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();

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

  RefPtr<nsHttpChannel>       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;

  // state for combining OnStatus/OnProgress with OnDataAvailable
  // into one IPDL call to child.
  nsresult mStoredStatus;
  int64_t mStoredProgress;
  int64_t mStoredProgressMax;

  bool mSentRedirect1Begin          : 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;
};

NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
                              HTTP_CHANNEL_PARENT_IID)

} // namespace net
} // namespace mozilla

#endif // mozilla_net_HttpChannelParent_h