Bug 1600254 - P6: Make H2 push work r=dragana
authorKershaw Chang <kershaw@mozilla.com>
Thu, 30 Jan 2020 13:52:22 +0000
changeset 512139 0925f61718bbfde97fcd4c3052696be917b73d3c
parent 512138 1d7b2d12113cd3cefe79ab0cfe0a536f3b5c5946
child 512140 1d2e243f15816f932d86999b548c73512b49ca1d
push id37073
push userdvarga@mozilla.com
push dateThu, 30 Jan 2020 21:38:07 +0000
treeherdermozilla-central@fc760793ad44 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1600254
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1600254 - P6: Make H2 push work r=dragana Differential Revision: https://phabricator.services.mozilla.com/D56208
netwerk/protocol/http/Http2Push.cpp
netwerk/protocol/http/Http2Push.h
netwerk/protocol/http/Http3Session.cpp
netwerk/protocol/http/HttpTransactionChild.cpp
netwerk/protocol/http/HttpTransactionChild.h
netwerk/protocol/http/HttpTransactionParent.cpp
netwerk/protocol/http/HttpTransactionParent.h
netwerk/protocol/http/HttpTransactionShell.h
netwerk/protocol/http/PHttpTransaction.ipdl
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
--- a/netwerk/protocol/http/Http2Push.cpp
+++ b/netwerk/protocol/http/Http2Push.cpp
@@ -11,54 +11,22 @@
 #undef LOG
 #define LOG(args) LOG5(args)
 #undef LOG_ENABLED
 #define LOG_ENABLED() LOG5_ENABLED()
 
 #include <algorithm>
 
 #include "Http2Push.h"
-#include "nsHttpChannel.h"
 #include "nsIHttpPushListener.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace net {
 
-class CallChannelOnPush final : public Runnable {
- public:
-  CallChannelOnPush(nsIHttpChannelInternal* associatedChannel,
-                    const nsACString& pushedURI, Http2PushedStream* pushStream)
-      : Runnable("net::CallChannelOnPush"),
-        mAssociatedChannel(associatedChannel),
-        mPushedURI(pushedURI) {
-    mPushedStreamWrapper = new Http2PushedStreamWrapper(pushStream);
-  }
-
-  NS_IMETHOD Run() override {
-    MOZ_ASSERT(NS_IsMainThread());
-    RefPtr<nsHttpChannel> channel;
-    CallQueryInterface(mAssociatedChannel, channel.StartAssignment());
-    MOZ_ASSERT(channel);
-    if (channel &&
-        NS_SUCCEEDED(channel->OnPush(mPushedURI, mPushedStreamWrapper))) {
-      return NS_OK;
-    }
-
-    LOG3(("Http2PushedStream Orphan %p failed OnPush\n", this));
-    mPushedStreamWrapper->OnPushFailed();
-    return NS_OK;
-  }
-
- private:
-  nsCOMPtr<nsIHttpChannelInternal> mAssociatedChannel;
-  const nsCString mPushedURI;
-  RefPtr<Http2PushedStreamWrapper> mPushedStreamWrapper;
-};
-
 // Because WeakPtr isn't thread-safe we must ensure that the object is destroyed
 // on the socket thread, so any Release() called on a different thread is
 // dispatched to the socket thread.
 bool Http2PushedStreamWrapper::DispatchRelease() {
   if (OnSocketThread()) {
     return false;
   }
 
@@ -95,16 +63,18 @@ Http2PushedStreamWrapper::Release() {
 NS_INTERFACE_MAP_BEGIN(Http2PushedStreamWrapper)
 NS_INTERFACE_MAP_END
 
 Http2PushedStreamWrapper::Http2PushedStreamWrapper(
     Http2PushedStream* aPushStream) {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   mStream = aPushStream;
   mRequestString = aPushStream->GetRequestString();
+  mResourceUrl = aPushStream->GetResourceUrl();
+  mStreamID = aPushStream->StreamID();
 }
 
 Http2PushedStreamWrapper::~Http2PushedStreamWrapper() {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 }
 
 Http2PushedStream* Http2PushedStreamWrapper::GetStream() {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
@@ -212,29 +182,27 @@ bool Http2PushedStream::DeferCleanup(nsr
 
 // return true if channel implements nsIHttpPushListener
 bool Http2PushedStream::TryOnPush() {
   nsHttpTransaction* trans = mAssociatedTransaction->QueryHttpTransaction();
   if (!trans) {
     return false;
   }
 
-  nsCOMPtr<nsIHttpChannelInternal> associatedChannel =
-      do_QueryInterface(trans->HttpChannel());
-  if (!associatedChannel) {
-    return false;
-  }
-
   if (!(trans->Caps() & NS_HTTP_ONPUSH_LISTENER)) {
     return false;
   }
 
   mDeferCleanupOnPush = true;
-  nsCString uri = Origin() + Path();
-  NS_DispatchToMainThread(new CallChannelOnPush(associatedChannel, uri, this));
+  mResourceUrl = Origin() + Path();
+  RefPtr<Http2PushedStreamWrapper> stream = new Http2PushedStreamWrapper(this);
+  RefPtr<nsHttpTransaction> transaction = trans;
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "net::nsHttpTransaction::OnPush",
+      [transaction, stream]() { transaction->OnPush(stream); }));
   return true;
 }
 
 // side effect free static method to determine if Http2Stream implements
 // nsIHttpPushListener
 bool Http2PushedStream::TestOnPush(Http2Stream* stream) {
   if (!stream) {
     return false;
@@ -242,22 +210,17 @@ bool Http2PushedStream::TestOnPush(Http2
   nsAHttpTransaction* abstractTransaction = stream->Transaction();
   if (!abstractTransaction) {
     return false;
   }
   nsHttpTransaction* trans = abstractTransaction->QueryHttpTransaction();
   if (!trans) {
     return false;
   }
-  nsCOMPtr<nsIHttpChannelInternal> associatedChannel =
-      do_QueryInterface(trans->HttpChannel());
-  if (!associatedChannel) {
-    return false;
-  }
-  return (trans->Caps() & NS_HTTP_ONPUSH_LISTENER);
+  return trans->Caps() & NS_HTTP_ONPUSH_LISTENER;
 }
 
 nsresult Http2PushedStream::ReadSegments(nsAHttpSegmentReader* reader, uint32_t,
                                          uint32_t* count) {
   nsresult rv = NS_OK;
   *count = 0;
 
   mozilla::OriginAttributes originAttributes;
--- a/netwerk/protocol/http/Http2Push.h
+++ b/netwerk/protocol/http/Http2Push.h
@@ -67,16 +67,17 @@ class Http2PushedStream final : public H
                                         uint32_t* countWritten);
 
   // overload of Http2Stream
   virtual bool HasSink() override { return !!mConsumerStream; }
   virtual void SetPushComplete() override { mPushCompleted = true; }
   virtual void TopLevelOuterContentWindowIdChanged(uint64_t) override;
 
   nsCString& GetRequestString() { return mRequestString; }
+  nsCString& GetResourceUrl() { return mResourceUrl; }
 
  private:
   Http2Stream*
       mConsumerStream;  // paired request stream that consumes from
                         // real http/2 one.. null until a match is made.
 
   nsCOMPtr<nsIRequestContext> mRequestContext;
 
@@ -94,16 +95,17 @@ class Http2PushedStream final : public H
   // destroying the push stream on an error code during the period between
   // when we need to do OnPush() on another thread and the time it takes
   // for that event to create a synthetic pull stream attached to this
   // object. That synthetic pull will become mConsuemerStream.
   // Ths is essentially a delete protecting reference.
   bool mDeferCleanupOnPush;
   bool mOnPushFailed;
   nsCString mRequestString;
+  nsCString mResourceUrl;
 
   uint32_t mDefaultPriorityDependency;
 };
 
 class Http2PushTransactionBuffer final : public nsAHttpTransaction {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSAHTTPTRANSACTION
@@ -134,22 +136,26 @@ class Http2PushTransactionBuffer final :
 class Http2PushedStreamWrapper : public nsISupports {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   bool DispatchRelease();
 
   explicit Http2PushedStreamWrapper(Http2PushedStream* aPushStream);
 
   nsCString& GetRequestString() { return mRequestString; }
+  nsCString& GetResourceUrl() { return mResourceUrl; }
   Http2PushedStream* GetStream();
   void OnPushFailed();
+  uint32_t StreamID() { return mStreamID; }
 
  private:
   virtual ~Http2PushedStreamWrapper();
 
   nsCString mRequestString;
+  nsCString mResourceUrl;
+  uint32_t mStreamID;
   WeakPtr<Http2Stream> mStream;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_Http2Push_Internal_h
--- a/netwerk/protocol/http/Http3Session.cpp
+++ b/netwerk/protocol/http/Http3Session.cpp
@@ -9,16 +9,17 @@
 #include "Http3Stream.h"
 #include "mozilla/net/DNS.h"
 #include "nsHttpHandler.h"
 #include "mozilla/RefPtr.h"
 #include "ASpdySession.h"  // because of SoftStreamError()
 #include "nsIOService.h"
 #include "nsISSLSocketControl.h"
 #include "ScopedNSSTypes.h"
+#include "nsQueryObject.h"
 #include "nsSocketTransportService2.h"
 #include "nsThreadUtils.h"
 #include "QuicSocketControl.h"
 #include "SSLServerCertVerification.h"
 //#include "cert.h"
 #include "sslerr.h"
 
 namespace mozilla {
--- a/netwerk/protocol/http/HttpTransactionChild.cpp
+++ b/netwerk/protocol/http/HttpTransactionChild.cpp
@@ -110,31 +110,57 @@ static already_AddRefed<nsIRequestContex
 }
 
 nsresult HttpTransactionChild::InitInternal(
     uint32_t caps, const HttpConnectionInfoCloneArgs& infoArgs,
     nsHttpRequestHead* requestHead, nsIInputStream* requestBody,
     uint64_t requestContentLength, bool requestBodyHasHeaders,
     uint64_t topLevelOuterContentWindowId, uint8_t httpTrafficCategory,
     uint64_t requestContextID, uint32_t classOfService, uint32_t initialRwin,
-    bool responseTimeoutEnabled, uint64_t channelId) {
+    bool responseTimeoutEnabled, uint64_t channelId,
+    const Maybe<H2PushedStreamArg>& aPushedStreamArg) {
   LOG(("HttpTransactionChild::InitInternal [this=%p caps=%x]\n", this, caps));
 
   RefPtr<nsHttpConnectionInfo> cinfo =
       DeserializeHttpConnectionInfoCloneArgs(infoArgs);
   nsCOMPtr<nsIRequestContext> rc = CreateRequestContext(requestContextID);
 
+  HttpTransactionShell::OnPushCallback pushCallback = nullptr;
+  if (caps & NS_HTTP_ONPUSH_LISTENER) {
+    RefPtr<HttpTransactionChild> self = this;
+    pushCallback = [self](uint32_t aPushedStreamId, const nsACString& aUrl,
+                          const nsACString& aRequestString) {
+      bool res = false;
+      if (self->CanSend()) {
+        res =
+            self->SendOnH2PushStream(aPushedStreamId, PromiseFlatCString(aUrl),
+                                     PromiseFlatCString(aRequestString));
+      }
+      return res ? NS_OK : NS_ERROR_FAILURE;
+    };
+  }
+
+  RefPtr<nsHttpTransaction> transWithPushedStream;
+  uint32_t pushedStreamId = 0;
+  if (aPushedStreamArg) {
+    HttpTransactionChild* transChild = static_cast<HttpTransactionChild*>(
+        aPushedStreamArg.ref().transWithPushedStreamChild());
+    transWithPushedStream = transChild->GetHttpTransaction();
+    pushedStreamId = aPushedStreamArg.ref().pushedStreamId();
+  }
+
   nsresult rv = mTransaction->Init(
       caps, cinfo, requestHead, requestBody, requestContentLength,
       requestBodyHasHeaders, GetCurrentThreadEventTarget(),
       nullptr,  // TODO: security callback, fix in bug 1512479.
       this, topLevelOuterContentWindowId,
       static_cast<HttpTrafficCategory>(httpTrafficCategory), rc, classOfService,
       initialRwin, responseTimeoutEnabled, channelId,
-      std::move(mTransactionObserver));
+      std::move(mTransactionObserver), std::move(pushCallback),
+      transWithPushedStream, pushedStreamId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mTransaction = nullptr;
     return rv;
   }
 
   Unused << mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump));
   return rv;
 }
@@ -174,17 +200,18 @@ mozilla::ipc::IPCResult HttpTransactionC
 mozilla::ipc::IPCResult HttpTransactionChild::RecvInit(
     const uint32_t& aCaps, const HttpConnectionInfoCloneArgs& aArgs,
     const nsHttpRequestHead& aReqHeaders, const Maybe<IPCStream>& aRequestBody,
     const uint64_t& aReqContentLength, const bool& aReqBodyIncludesHeaders,
     const uint64_t& aTopLevelOuterContentWindowId,
     const uint8_t& aHttpTrafficCategory, const uint64_t& aRequestContextID,
     const uint32_t& aClassOfService, const uint32_t& aInitialRwin,
     const bool& aResponseTimeoutEnabled, const uint64_t& aChannelId,
-    const bool& aHasTransactionObserver) {
+    const bool& aHasTransactionObserver,
+    const Maybe<H2PushedStreamArg>& aPushedStreamArg) {
   mRequestHead = aReqHeaders;
   if (aRequestBody) {
     mUploadStream = mozilla::ipc::DeserializeIPCStream(aRequestBody);
   }
 
   mTransaction = new nsHttpTransaction();
   mChannelId = aChannelId;
 
@@ -200,17 +227,17 @@ mozilla::ipc::IPCResult HttpTransactionC
       }
     };
   }
 
   nsresult rv = InitInternal(
       aCaps, aArgs, &mRequestHead, mUploadStream, aReqContentLength,
       aReqBodyIncludesHeaders, aTopLevelOuterContentWindowId,
       aHttpTrafficCategory, aRequestContextID, aClassOfService, aInitialRwin,
-      aResponseTimeoutEnabled, aChannelId);
+      aResponseTimeoutEnabled, aChannelId, aPushedStreamArg);
   if (NS_FAILED(rv)) {
     LOG(("HttpTransactionChild::RecvInit: [this=%p] InitInternal failed!\n",
          this));
     mTransaction = nullptr;
     SendOnInitFailed(rv);
   }
   return IPC_OK();
 }
--- a/netwerk/protocol/http/HttpTransactionChild.h
+++ b/netwerk/protocol/http/HttpTransactionChild.h
@@ -41,17 +41,18 @@ class HttpTransactionChild final : publi
       const uint32_t& aCaps, const HttpConnectionInfoCloneArgs& aArgs,
       const nsHttpRequestHead& aReqHeaders,
       const Maybe<IPCStream>& aRequestBody, const uint64_t& aReqContentLength,
       const bool& aReqBodyIncludesHeaders,
       const uint64_t& aTopLevelOuterContentWindowId,
       const uint8_t& aHttpTrafficCategory, const uint64_t& aRequestContextID,
       const uint32_t& aClassOfService, const uint32_t& aInitialRwin,
       const bool& aResponseTimeoutEnabled, const uint64_t& aChannelId,
-      const bool& aHasTransactionObserver);
+      const bool& aHasTransactionObserver,
+      const Maybe<H2PushedStreamArg>& aPushedStreamArg);
   mozilla::ipc::IPCResult RecvUpdateClassOfService(
       const uint32_t& classOfService);
   mozilla::ipc::IPCResult RecvCancelPump(const nsresult& aStatus);
   mozilla::ipc::IPCResult RecvSuspendPump();
   mozilla::ipc::IPCResult RecvResumePump();
   mozilla::ipc::IPCResult RecvSetDNSWasRefreshed();
   mozilla::ipc::IPCResult RecvDontReuseConnection();
   mozilla::ipc::IPCResult RecvSetH2WSConnRefTaken();
@@ -70,17 +71,18 @@ class HttpTransactionChild final : publi
   // for the parameters.
   MOZ_MUST_USE nsresult InitInternal(
       uint32_t caps, const HttpConnectionInfoCloneArgs& aArgs,
       nsHttpRequestHead* reqHeaders,
       nsIInputStream* reqBody,  // use the trick in bug 1277681
       uint64_t reqContentLength, bool reqBodyIncludesHeaders,
       uint64_t topLevelOuterContentWindowId, uint8_t httpTrafficCategory,
       uint64_t requestContextID, uint32_t classOfService, uint32_t initialRwin,
-      bool responseTimeoutEnabled, uint64_t channelId);
+      bool responseTimeoutEnabled, uint64_t channelId,
+      const Maybe<H2PushedStreamArg>& aPushedStreamArg);
 
   bool mCanceled;
   nsresult mStatus;
   uint64_t mChannelId;
   nsHttpRequestHead mRequestHead;
 
   nsCOMPtr<nsIInputStream> mUploadStream;
   RefPtr<nsHttpTransaction> mTransaction;
--- a/netwerk/protocol/http/HttpTransactionParent.cpp
+++ b/netwerk/protocol/http/HttpTransactionParent.cpp
@@ -146,48 +146,60 @@ void HttpTransactionParent::GetStructFro
 nsresult HttpTransactionParent::Init(
     uint32_t caps, nsHttpConnectionInfo* cinfo, nsHttpRequestHead* requestHead,
     nsIInputStream* requestBody, uint64_t requestContentLength,
     bool requestBodyHasHeaders, nsIEventTarget* target,
     nsIInterfaceRequestor* callbacks, nsITransportEventSink* eventsink,
     uint64_t topLevelOuterContentWindowId, HttpTrafficCategory trafficCategory,
     nsIRequestContext* requestContext, uint32_t classOfService,
     uint32_t initialRwin, bool responseTimeoutEnabled, uint64_t channelId,
-    TransactionObserverFunc&& transactionObserver) {
+    TransactionObserverFunc&& transactionObserver,
+    OnPushCallback&& aOnPushCallback,
+    HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId) {
   LOG(("HttpTransactionParent::Init [this=%p caps=%x]\n", this, caps));
 
   if (!CanSend()) {
     return NS_ERROR_FAILURE;
   }
 
   mEventsink = eventsink;
   mTargetThread = GetCurrentThreadEventTarget();
   mChannelId = channelId;
   mTransactionObserver = std::move(transactionObserver);
+  mOnPushCallback = std::move(aOnPushCallback);
 
   HttpConnectionInfoCloneArgs infoArgs;
   GetStructFromInfo(cinfo, infoArgs);
 
   mozilla::ipc::AutoIPCStream autoStream;
   if (requestBody &&
       !autoStream.Serialize(requestBody, SocketProcessParent::GetSingleton())) {
     return NS_ERROR_FAILURE;
   }
 
   uint64_t requestContextID = requestContext ? requestContext->GetID() : 0;
 
+  Maybe<H2PushedStreamArg> pushedStreamArg;
+  if (aTransWithPushedStream && aPushedStreamId) {
+    MOZ_ASSERT(aTransWithPushedStream->AsHttpTransactionParent());
+    pushedStreamArg.emplace();
+    pushedStreamArg.ref().transWithPushedStreamParent() =
+        aTransWithPushedStream->AsHttpTransactionParent();
+    pushedStreamArg.ref().pushedStreamId() = aPushedStreamId;
+  }
+
   // TODO: Figure out if we have to implement nsIThreadRetargetableRequest in
   // bug 1544378.
   if (!SendInit(caps, infoArgs, *requestHead,
                 requestBody ? Some(autoStream.TakeValue()) : Nothing(),
                 requestContentLength, requestBodyHasHeaders,
                 topLevelOuterContentWindowId,
                 static_cast<uint8_t>(trafficCategory), requestContextID,
                 classOfService, initialRwin, responseTimeoutEnabled, mChannelId,
-                !!mTransactionObserver)) {
+                !!mTransactionObserver, pushedStreamArg)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult HttpTransactionParent::AsyncRead(nsIStreamListener* listener,
                                           nsIRequest** pump) {
@@ -314,20 +326,16 @@ void HttpTransactionParent::SetH2WSConnR
 }
 
 void HttpTransactionParent::SetSecurityCallbacks(
     nsIInterfaceRequestor* aCallbacks) {
   // TODO: we might don't need to implement this.
   // Will figure out in bug 1512479.
 }
 
-void HttpTransactionParent::SetPushedStream(Http2PushedStreamWrapper* push) {
-  // TODO: will be implemented later in bug 1600254.
-}
-
 void HttpTransactionParent::SetDomainLookupStart(mozilla::TimeStamp timeStamp,
                                                  bool onlyIfNull) {
   mDomainLookupStart = timeStamp;
   mTimings.domainLookupStart = mDomainLookupStart;
 }
 void HttpTransactionParent::SetDomainLookupEnd(mozilla::TimeStamp timeStamp,
                                                bool onlyIfNull) {
   mDomainLookupEnd = timeStamp;
@@ -557,16 +565,25 @@ mozilla::ipc::IPCResult HttpTransactionP
     const nsresult& aStatus) {
   nsCOMPtr<nsIRequest> request = do_QueryInterface(mEventsink);
   if (request) {
     request->Cancel(aStatus);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult HttpTransactionParent::RecvOnH2PushStream(
+    const uint32_t& aPushedStreamId, const nsCString& aResourceUrl,
+    const nsCString& aRequestString) {
+  MOZ_ASSERT(mOnPushCallback);
+
+  mOnPushCallback(aPushedStreamId, aResourceUrl, aRequestString);
+  return IPC_OK();
+}  // namespace net
+
 //-----------------------------------------------------------------------------
 // HttpTransactionParent <nsIRequest>
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpTransactionParent::GetName(nsACString& aResult) {
   aResult.Truncate();
   return NS_OK;
--- a/netwerk/protocol/http/HttpTransactionParent.h
+++ b/netwerk/protocol/http/HttpTransactionParent.h
@@ -54,16 +54,20 @@ class HttpTransactionParent final : publ
       const Maybe<nsHttpHeaderArray>& responseTrailers,
       const bool& aHasStickyConn,
       const Maybe<TransactionObserverResult>& aTransactionObserverResult);
   mozilla::ipc::IPCResult RecvOnNetAddrUpdate(const NetAddr& aSelfAddr,
                                               const NetAddr& aPeerAddr,
                                               const bool& aResolvedByTRR);
   mozilla::ipc::IPCResult RecvOnInitFailed(const nsresult& aStatus);
 
+  mozilla::ipc::IPCResult RecvOnH2PushStream(const uint32_t& aPushedStreamId,
+                                             const nsCString& aResourceUrl,
+                                             const nsCString& aRequestString);
+
   already_AddRefed<nsIEventTarget> GetNeckoTarget();
 
  private:
   virtual ~HttpTransactionParent();
 
   void GetStructFromInfo(nsHttpConnectionInfo* aInfo,
                          HttpConnectionInfoCloneArgs& aArgs);
   void DoOnStartRequest(const nsresult& aStatus,
@@ -110,14 +114,15 @@ class HttpTransactionParent final : publ
 
   NetAddr mSelfAddr;
   NetAddr mPeerAddr;
   TimingStruct mTimings;
   TimeStamp mDomainLookupStart;
   TimeStamp mDomainLookupEnd;
   TransactionObserverFunc mTransactionObserver;
   TransactionObserverResult mTransactionObserverResult;
+  OnPushCallback mOnPushCallback;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // nsHttpTransactionParent_h__
--- a/netwerk/protocol/http/HttpTransactionShell.h
+++ b/netwerk/protocol/http/HttpTransactionShell.h
@@ -41,16 +41,18 @@ class TransactionObserverResult;
     }                                                \
   }
 
 class HttpTransactionShell : public nsISupports {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(HTTPTRANSACTIONSHELL_IID)
 
   using TransactionObserverFunc = std::function<void()>;
+  using OnPushCallback =
+      std::function<nsresult(uint32_t, const nsACString&, const nsACString&)>;
 
   //
   // called to initialize the transaction
   //
   // @param caps
   //        the transaction capabilities (see nsHttp.h)
   // @param connInfo
   //        the connection type for this transaction.
@@ -71,17 +73,20 @@ class HttpTransactionShell : public nsIS
       uint32_t caps, nsHttpConnectionInfo* connInfo,
       nsHttpRequestHead* reqHeaders, nsIInputStream* reqBody,
       uint64_t reqContentLength, bool reqBodyIncludesHeaders,
       nsIEventTarget* consumerTarget, nsIInterfaceRequestor* callbacks,
       nsITransportEventSink* eventsink, uint64_t topLevelOuterContentWindowId,
       HttpTrafficCategory trafficCategory, nsIRequestContext* requestContext,
       uint32_t classOfService, uint32_t initialRwin,
       bool responseTimeoutEnabled, uint64_t channelId,
-      TransactionObserverFunc&& transactionObserver) = 0;
+      TransactionObserverFunc&& transactionObserver,
+      OnPushCallback&& aOnPushCallback,
+      HttpTransactionShell* aTransWithPushedStream,
+      uint32_t aPushedStreamId) = 0;
 
   // @param aListener
   //        receives notifications.
   // @param pump
   //        the pump that will contain the response data. async wait on this
   //        input stream for data. On first notification, headers should be
   //        available (check transaction status).
   virtual nsresult AsyncRead(nsIStreamListener* listener,
@@ -129,17 +134,16 @@ class HttpTransactionShell : public nsIS
 
   // Called to notify that a requested DNS cache entry was refreshed.
   virtual void SetDNSWasRefreshed() = 0;
 
   virtual void DontReuseConnection() = 0;
   virtual bool HasStickyConnection() const = 0;
 
   virtual void SetH2WSConnRefTaken() = 0;
-  virtual void SetPushedStream(Http2PushedStreamWrapper* push) = 0;
 
   virtual bool ProxyConnectFailed() = 0;
   virtual int32_t GetProxyConnectResponseCode() = 0;
 
   virtual nsresult SetSniffedTypeToChannel(
       nsIRequest* aPump, nsIChannel* aChannel,
       nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) = 0;
 
@@ -157,17 +161,20 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HttpTransa
       uint32_t caps, nsHttpConnectionInfo* connInfo,                           \
       nsHttpRequestHead* reqHeaders, nsIInputStream* reqBody,                  \
       uint64_t reqContentLength, bool reqBodyIncludesHeaders,                  \
       nsIEventTarget* consumerTarget, nsIInterfaceRequestor* callbacks,        \
       nsITransportEventSink* eventsink, uint64_t topLevelOuterContentWindowId, \
       HttpTrafficCategory trafficCategory, nsIRequestContext* requestContext,  \
       uint32_t classOfService, uint32_t initialRwin,                           \
       bool responseTimeoutEnabled, uint64_t channelId,                         \
-      TransactionObserverFunc&& transactionObserver) override;                 \
+      TransactionObserverFunc&& transactionObserver,                           \
+      OnPushCallback&& aOnPushCallback,                                        \
+      HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId)  \
+      override;                                                                \
   virtual nsresult AsyncRead(nsIStreamListener* listener, nsIRequest** pump)   \
       override;                                                                \
   virtual void SetClassOfService(uint32_t classOfService) override;            \
   virtual nsHttpResponseHead* TakeResponseHead() override;                     \
   virtual nsHttpHeaderArray* TakeResponseTrailers() override;                  \
   virtual nsISupports* SecurityInfo() override;                                \
   virtual void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)         \
       override;                                                                \
@@ -189,17 +196,16 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HttpTransa
   virtual const TimingStruct Timings() override;                               \
   virtual bool ResponseIsComplete() override;                                  \
   virtual int64_t GetTransferSize() override;                                  \
   virtual int64_t GetRequestSize() override;                                   \
   virtual void SetDNSWasRefreshed() override;                                  \
   virtual void DontReuseConnection() override;                                 \
   virtual bool HasStickyConnection() const override;                           \
   virtual void SetH2WSConnRefTaken() override;                                 \
-  virtual void SetPushedStream(Http2PushedStreamWrapper* push) override;       \
   virtual bool ProxyConnectFailed() override;                                  \
   virtual int32_t GetProxyConnectResponseCode() override;                      \
   virtual nsresult SetSniffedTypeToChannel(                                    \
       nsIRequest* aPump, nsIChannel* aChannel,                                 \
       nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) override;           \
   virtual void GetTransactionObserverResult(                                   \
       TransactionObserverResult& aResult) override;                            \
   virtual nsHttpTransaction* AsHttpTransaction() override;                     \
--- a/netwerk/protocol/http/PHttpTransaction.ipdl
+++ b/netwerk/protocol/http/PHttpTransaction.ipdl
@@ -15,16 +15,21 @@ include "mozilla/net/NeckoMessageUtils.h
 
 using class mozilla::net::nsHttpRequestHead from "nsHttpRequestHead.h";
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 
 namespace mozilla {
 namespace net {
 
+struct H2PushedStreamArg {
+  PHttpTransaction transWithPushedStream;
+  uint32_t pushedStreamId;
+};
+
 refcounted protocol PHttpTransaction
 {
   manager PSocketProcess;
 
 parent:
   async OnStartRequest(nsresult                   status,
                        nsHttpResponseHead?        responseHead,
                        nsCString                  securityInfoSerialization,
@@ -41,33 +46,37 @@ parent:
                       bool responseIsComplete,
                       int64_t transferSize,
                       TimingStructArgs timings,
                       nsHttpHeaderArray? responseTrailers,
                       bool hasStickyConn,
                       TransactionObserverResult? transactionObserverResult);
   async OnNetAddrUpdate(NetAddr selfAddr, NetAddr peerAddr, bool resolvedByTRR);
   async OnInitFailed(nsresult status);
+  async OnH2PushStream(uint32_t pushedStreamId,
+                       nsCString resourceUrl,
+                       nsCString requestString);
 
 child:
   async __delete__();
   async Init(uint32_t caps,
              HttpConnectionInfoCloneArgs aArgs,
              nsHttpRequestHead reqHeaders,
              IPCStream? requestBody,
              uint64_t reqContentLength,
              bool reqBodyIncludesHeaders,
              uint64_t topLevelOuterContentWindowId,
              uint8_t httpTrafficCategory,
              uint64_t requestContextID,
              uint32_t classOfService,
              uint32_t initialRwin,
              bool responseTimeoutEnabled,
              uint64_t channelId,
-             bool hasTransactionObserver);
+             bool hasTransactionObserver,
+             H2PushedStreamArg? pushedStreamArg);
 
   async UpdateClassOfService(uint32_t classOfService);
   async CancelPump(nsresult status);
   async SuspendPump();
   async ResumePump();
 
   async SetDNSWasRefreshed();
   async DontReuseConnection();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -332,17 +332,17 @@ nsHttpChannel::nsHttpChannel()
       mUsedNetwork(0),
       mAuthConnectionRestartable(0),
       mChannelClassifierCancellationPending(0),
       mAsyncResumePending(0),
       mHasBeenIsolatedChecked(0),
       mIsIsolated(0),
       mTopWindowOriginComputed(0),
       mHasCrossOriginOpenerPolicyMismatch(0),
-      mPushedStream(nullptr),
+      mPushedStreamId(0),
       mLocalBlocklist(false),
       mOnTailUnblock(nullptr),
       mWarningReporter(nullptr),
       mIsReadingFromCache(false),
       mFirstResponseSource(RESPONSE_PENDING),
       mRaceCacheWithNetwork(false),
       mRaceDelay(0),
       mIgnoreCacheEntry(false),
@@ -1313,27 +1313,34 @@ nsresult nsHttpChannel::SetupTransaction
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(),
                                     true);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     mCaps |= NS_HTTP_STICKY_CONNECTION;
     mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
   }
 
-  if (mPushedStream) {
-    mTransaction->SetPushedStream(mPushedStream);
-    mPushedStream = nullptr;
-  }
-
   nsCOMPtr<nsIHttpPushListener> pushListener;
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsIHttpPushListener),
                                 getter_AddRefs(pushListener));
+  HttpTransactionShell::OnPushCallback pushCallback = nullptr;
   if (pushListener) {
     mCaps |= NS_HTTP_ONPUSH_LISTENER;
+    nsWeakPtr weakPtrThis(
+        do_GetWeakReference(static_cast<nsIHttpChannel*>(this)));
+    pushCallback = [weakPtrThis](uint32_t aPushedStreamId,
+                                 const nsACString& aUrl,
+                                 const nsACString& aRequestString) {
+      if (nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis)) {
+        return static_cast<nsHttpChannel*>(channel.get())
+            ->OnPush(aPushedStreamId, aUrl, aRequestString);
+      }
+      return NS_ERROR_NOT_AVAILABLE;
+    };
   }
 
   EnsureTopLevelOuterContentWindowId();
   EnsureRequestContext();
 
   HttpTrafficCategory category = CreateTrafficCategory();
   std::function<void()> observer;
   if (mTransactionObserver) {
@@ -1345,17 +1352,18 @@ nsresult nsHttpChannel::SetupTransaction
       transactionObserver->Complete(result.versionOk(), result.authOk(),
                                     result.closeReason());
     };
   }
   rv = mTransaction->Init(
       mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
       mUploadStreamHasHeaders, GetCurrentThreadEventTarget(), callbacks, this,
       mTopLevelOuterContentWindowId, category, mRequestContext, mClassOfService,
-      mInitialRwin, mResponseTimeoutEnabled, mChannelId, std::move(observer));
+      mInitialRwin, mResponseTimeoutEnabled, mChannelId, std::move(observer),
+      std::move(pushCallback), mTransWithPushedStream, mPushedStreamId);
   if (NS_FAILED(rv)) {
     mTransaction = nullptr;
     return rv;
   }
 
   return rv;
 }
 
@@ -9475,26 +9483,32 @@ nsHttpChannel::SetNotificationCallbacks(
   }
   return rv;
 }
 
 bool nsHttpChannel::AwaitingCacheCallbacks() {
   return mCacheEntriesToWaitFor != 0;
 }
 
-void nsHttpChannel::SetPushedStream(Http2PushedStreamWrapper* stream) {
-  MOZ_ASSERT(stream);
-  MOZ_ASSERT(!mPushedStream);
-  mPushedStream = stream;
-}
-
-nsresult nsHttpChannel::OnPush(const nsACString& url,
-                               Http2PushedStreamWrapper* pushedStream) {
+void nsHttpChannel::SetPushedStreamTransactionAndId(
+    HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId) {
+  MOZ_ASSERT(!mTransWithPushedStream);
+  LOG(("nsHttpChannel::SetPushedStreamTransaction [this=%p] trans=%p", this,
+       aTransWithPushedStream));
+
+  mTransWithPushedStream = aTransWithPushedStream;
+  mPushedStreamId = aPushedStreamId;
+}
+
+nsresult nsHttpChannel::OnPush(uint32_t aPushedStreamId, const nsACString& aUrl,
+                               const nsACString& aRequestString) {
   MOZ_ASSERT(NS_IsMainThread());
-  LOG(("nsHttpChannel::OnPush [this=%p]\n", this));
+  MOZ_ASSERT(mTransaction);
+  LOG(("nsHttpChannel::OnPush [this=%p, trans=%p]\n", this,
+       mTransaction.get()));
 
   MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
   nsCOMPtr<nsIHttpPushListener> pushListener;
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsIHttpPushListener),
                                 getter_AddRefs(pushListener));
 
   if (!pushListener) {
@@ -9504,17 +9518,17 @@ nsresult nsHttpChannel::OnPush(const nsA
          this));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsIURI> pushResource;
   nsresult rv;
 
   // Create a Channel for the Push Resource
-  rv = NS_NewURI(getter_AddRefs(pushResource), url);
+  rv = NS_NewURI(getter_AddRefs(pushResource), aUrl);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIIOService> ioService;
   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -9536,25 +9550,23 @@ nsresult nsHttpChannel::OnPush(const nsA
   RefPtr<nsHttpChannel> channel;
   CallQueryInterface(pushHttpChannel, channel.StartAssignment());
   MOZ_ASSERT(channel);
   if (!channel) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // new channel needs mrqeuesthead and headers from pushedStream
-  channel->mRequestHead.ParseHeaderSet(
-      pushedStream->GetRequestString().BeginWriting());
-
+  channel->mRequestHead.ParseHeaderSet(aRequestString.BeginReading());
   channel->mLoadGroup = mLoadGroup;
   channel->mLoadInfo = mLoadInfo;
   channel->mCallbacks = mCallbacks;
 
-  // Link the pushed stream with the new channel and call listener
-  channel->SetPushedStream(pushedStream);
+  // Link the trans with pushed stream and the new channel and call listener
+  channel->SetPushedStreamTransactionAndId(mTransaction, aPushedStreamId);
   rv = pushListener->OnPush(this, pushHttpChannel);
   return rv;
 }
 
 // static
 bool nsHttpChannel::IsRedirectStatus(uint32_t status) {
   // 305 disabled as a security measure (see bug 187996).
   return status == 300 || status == 301 || status == 302 || status == 303 ||
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -39,17 +39,16 @@ class nsIDNSRecord;
 class nsIHttpChannelAuthProvider;
 class nsInputStreamPump;
 class nsITransportSecurityInfo;
 
 namespace mozilla {
 namespace net {
 
 class nsChannelClassifier;
-class Http2PushedStream;
 class HttpChannelSecurityWarningReporter;
 
 using DNSPromise = MozPromise<nsCOMPtr<nsIDNSRecord>, nsresult, false>;
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel
 //-----------------------------------------------------------------------------
 
@@ -131,18 +130,18 @@ class nsHttpChannel final : public HttpB
 
   nsHttpChannel();
 
   virtual MOZ_MUST_USE nsresult
   Init(nsIURI* aURI, uint32_t aCaps, nsProxyInfo* aProxyInfo,
        uint32_t aProxyResolveFlags, nsIURI* aProxyURI, uint64_t aChannelId,
        nsContentPolicyType aContentPolicyType) override;
 
-  MOZ_MUST_USE nsresult OnPush(const nsACString& uri,
-                               Http2PushedStreamWrapper* pushedStream);
+  MOZ_MUST_USE nsresult OnPush(uint32_t aPushedStreamId, const nsACString& aUrl,
+                               const nsACString& aRequestString);
 
   static bool IsRedirectStatus(uint32_t status);
   static bool WillRedirect(nsHttpResponseHead* response);
 
   // Methods HttpBaseChannel didn't implement for us or that we override.
   //
   // nsIRequest
   NS_IMETHOD Cancel(nsresult status) override;
@@ -547,17 +546,18 @@ class nsHttpChannel final : public HttpB
                              bool ignoreMissingPartialLen = false);
   MOZ_MUST_USE nsresult SetupByteRangeRequest(int64_t partialLen);
   void UntieByteRangeRequest();
   void UntieValidationRequest();
   MOZ_MUST_USE nsresult OpenCacheInputStream(nsICacheEntry* cacheEntry,
                                              bool startBuffering,
                                              bool checkingAppCacheEntry);
 
-  void SetPushedStream(Http2PushedStreamWrapper* stream);
+  void SetPushedStreamTransactionAndId(
+      HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId);
 
   void MaybeWarnAboutAppCache();
 
   void SetOriginHeader();
   void SetDoNotTrack();
 
   bool IsIsolated();
 
@@ -750,17 +750,19 @@ class nsHttpChannel final : public HttpB
   // true.
   nsCString mTopWindowOrigin;
 
   nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
   // Needed for accurate DNS timing
   RefPtr<nsDNSPrefetch> mDNSPrefetch;
 
-  RefPtr<Http2PushedStreamWrapper> mPushedStream;
+  uint32_t mPushedStreamId;
+  RefPtr<HttpTransactionShell> mTransWithPushedStream;
+
   // True if the channel's principal was found on a phishing, malware, or
   // tracking (if tracking protection is enabled) blocklist
   bool mLocalBlocklist;
 
   MOZ_MUST_USE nsresult WaitForRedirectCallback();
   void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
   void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -246,28 +246,31 @@ nsHttpTransaction::~nsHttpTransaction() 
 nsresult nsHttpTransaction::Init(
     uint32_t caps, nsHttpConnectionInfo* cinfo, nsHttpRequestHead* requestHead,
     nsIInputStream* requestBody, uint64_t requestContentLength,
     bool requestBodyHasHeaders, nsIEventTarget* target,
     nsIInterfaceRequestor* callbacks, nsITransportEventSink* eventsink,
     uint64_t topLevelOuterContentWindowId, HttpTrafficCategory trafficCategory,
     nsIRequestContext* requestContext, uint32_t classOfService,
     uint32_t initialRwin, bool responseTimeoutEnabled, uint64_t channelId,
-    TransactionObserverFunc&& transactionObserver) {
+    TransactionObserverFunc&& transactionObserver,
+    OnPushCallback&& aOnPushCallback,
+    HttpTransactionShell* transWithPushedStream, uint32_t aPushedStreamId) {
   nsresult rv;
 
   LOG1(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
 
   MOZ_ASSERT(cinfo);
   MOZ_ASSERT(requestHead);
   MOZ_ASSERT(target);
   MOZ_ASSERT(NS_IsMainThread());
 
   mChannelId = channelId;
   mTransactionObserver = std::move(transactionObserver);
+  mOnPushCallback = std::move(aOnPushCallback);
   mTopLevelOuterContentWindowId = topLevelOuterContentWindowId;
   LOG(("  window-id = %" PRIx64, mTopLevelOuterContentWindowId));
 
   mTrafficCategory = trafficCategory;
 
   mActivityDistributor = services::GetActivityDistributor();
   if (!mActivityDistributor) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -430,16 +433,22 @@ nsresult nsHttpTransaction::Init(
                      : -1;
 
   // create pipe for response stream
   rv = NS_NewPipe2(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut), true,
                    true, nsIOService::gDefaultSegmentSize,
                    nsIOService::gDefaultSegmentCount);
   if (NS_FAILED(rv)) return rv;
 
+  if (transWithPushedStream && aPushedStreamId) {
+    RefPtr<nsHttpTransaction> trans =
+        transWithPushedStream->AsHttpTransaction();
+    MOZ_ASSERT(trans);
+    mPushedStream = trans->TakePushedStreamById(aPushedStreamId);
+  }
   return NS_OK;
 }
 
 nsresult nsHttpTransaction::AsyncRead(nsIStreamListener* listener,
                                       nsIRequest** pump) {
   RefPtr<nsInputStreamPump> transactionPump;
   nsresult rv =
       nsInputStreamPump::Create(getter_AddRefs(transactionPump), mPipeIn);
@@ -1009,18 +1018,49 @@ bool nsHttpTransaction::HasStickyConnect
 }
 
 bool nsHttpTransaction::ResponseIsComplete() { return mResponseIsComplete; }
 
 int64_t nsHttpTransaction::GetTransferSize() { return mTransferSize; }
 
 int64_t nsHttpTransaction::GetRequestSize() { return mRequestSize; }
 
-void nsHttpTransaction::SetPushedStream(Http2PushedStreamWrapper* push) {
-  mPushedStream = push;
+already_AddRefed<Http2PushedStreamWrapper>
+nsHttpTransaction::TakePushedStreamById(uint32_t aStreamId) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aStreamId);
+
+  auto entry = mIDToStreamMap.Lookup(aStreamId);
+  if (entry) {
+    RefPtr<Http2PushedStreamWrapper> stream = entry.Data();
+    entry.Remove();
+    return stream.forget();
+  }
+
+  return nullptr;
+}
+
+void nsHttpTransaction::OnPush(Http2PushedStreamWrapper* aStream) {
+  LOG(("nsHttpTransaction::OnPush %p aStream=%p", this, aStream));
+  MOZ_ASSERT(aStream);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mOnPushCallback);
+
+  RefPtr<Http2PushedStreamWrapper> stream = aStream;
+  auto entry = mIDToStreamMap.LookupForAdd(stream->StreamID());
+  MOZ_ASSERT(!entry);
+  if (!entry) {
+    entry.OrInsert([&stream]() { return stream; });
+  }
+
+  if (NS_FAILED(mOnPushCallback(stream->StreamID(), stream->GetResourceUrl(),
+                                stream->GetRequestString()))) {
+    stream->OnPushFailed();
+    mIDToStreamMap.Remove(stream->StreamID());
+  }
 }
 
 nsHttpTransaction* nsHttpTransaction::AsHttpTransaction() { return this; }
 
 HttpTransactionParent* nsHttpTransaction::AsHttpTransactionParent() {
   return nullptr;
 }
 
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -134,16 +134,21 @@ class nsHttpTransaction final : public n
 
   void SetHttpTrailers(nsCString& aTrailers);
 
   bool IsWebsocketUpgrade();
   void SetH2WSTransaction(SpdyConnectTransaction*);
 
   void OnProxyConnectComplete(int32_t aResponseCode) override;
 
+  // This is only called by Http2PushedStream::TryOnPush when a new pushed
+  // stream is available. The newly added stream will be taken by another
+  // transaction.
+  void OnPush(Http2PushedStreamWrapper* aStream);
+
  private:
   friend class DeleteHttpTransaction;
   virtual ~nsHttpTransaction();
 
   MOZ_MUST_USE nsresult Restart();
   char* LocateHttpStart(char* buf, uint32_t len, bool aAllowPartialMatch);
   MOZ_MUST_USE nsresult ParseLine(nsACString& line);
   MOZ_MUST_USE nsresult ParseLineSegment(char* seg, uint32_t len);
@@ -180,16 +185,19 @@ class nsHttpTransaction final : public n
 
   // Called from WriteSegments.  Checks for conditions whether to throttle
   // reading the content.  When this returns true, WriteSegments returns
   // WOULD_BLOCK.
   bool ShouldThrottle();
 
   void NotifyTransactionObserver(nsresult reason);
 
+  already_AddRefed<Http2PushedStreamWrapper> TakePushedStreamById(
+      uint32_t aStreamId);
+
  private:
   class UpdateSecurityCallbacks : public Runnable {
    public:
     UpdateSecurityCallbacks(nsHttpTransaction* aTrans,
                             nsIInterfaceRequestor* aCallbacks)
         : Runnable("net::nsHttpTransaction::UpdateSecurityCallbacks"),
           mTrans(aTrans),
           mCallbacks(aCallbacks) {}
@@ -410,14 +418,18 @@ class nsHttpTransaction final : public n
   uint8_t mFastOpenStatus;
 
   // H2 websocket support
   RefPtr<SpdyConnectTransaction> mH2WSTransaction;
 
   HttpTrafficCategory mTrafficCategory;
   bool mThroughCaptivePortal;
   int32_t mProxyConnectResponseCode;
+
+  OnPushCallback mOnPushCallback;
+  nsDataHashtable<nsUint32HashKey, RefPtr<Http2PushedStreamWrapper>>
+      mIDToStreamMap;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // nsHttpTransaction_h__