Bug 1432429 - Create a DelayHttpChannelQueue class to queue network requests until FuzzyFox indicates they should fire. r=mayhemer,baku
authorTom Ritter <tom@mozilla.com>
Tue, 18 Sep 2018 13:05:54 -0500
changeset 502405 3d52af18f5c684e30d720941c811ea8bb5213626
parent 502404 b65a35258367e497ee8026513a4deb2a3f3124fa
child 502406 77626c8d6bee03863c84285bfdd71538c396aeb5
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer, baku
bugs1432429
milestone65.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 1432429 - Create a DelayHttpChannelQueue class to queue network requests until FuzzyFox indicates they should fire. r=mayhemer,baku
netwerk/protocol/http/DelayHttpChannelQueue.cpp
netwerk/protocol/http/DelayHttpChannelQueue.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/DelayHttpChannelQueue.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set et cin ts=4 sw=4 sts=4: */
+/* 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/. */
+
+#include "DelayHttpChannelQueue.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+namespace {
+StaticRefPtr<DelayHttpChannelQueue> sDelayHttpChannelQueue;
+}
+
+bool
+DelayHttpChannelQueue::AttemptQueueChannel(nsHttpChannel* aChannel)
+{
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!TimeStamp::GetFuzzyfoxEnabled()) {
+    return false;
+  }
+
+  if (!sDelayHttpChannelQueue) {
+    RefPtr<DelayHttpChannelQueue> queue = new DelayHttpChannelQueue();
+    if (!queue->Initialize()) {
+      return false;
+    }
+
+    sDelayHttpChannelQueue = queue;
+  }
+
+  if (NS_WARN_IF(!sDelayHttpChannelQueue->mQueue.AppendElement(aChannel, fallible))) {
+    return false;
+  }
+
+  return true;
+}
+
+DelayHttpChannelQueue::DelayHttpChannelQueue()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+DelayHttpChannelQueue::~DelayHttpChannelQueue()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+bool
+DelayHttpChannelQueue::Initialize()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_WARN_IF(!obs)) {
+    return false;
+  }
+
+  nsresult rv = obs->AddObserver(this, "fuzzyfox-fire-outbound", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  rv = obs->AddObserver(this, "xpcom-shutdown", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+NS_IMETHODIMP
+DelayHttpChannelQueue::Observe(nsISupports* aSubject, const char* aTopic,
+                               const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!strcmp(aTopic, "fuzzyfox-fire-outbound")) {
+    FireQueue();
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_WARN_IF(!obs)) {
+    return NS_OK;
+  }
+
+  obs->RemoveObserver(this, "fuzzyfox-fire-outbound");
+  obs->RemoveObserver(this, "xpcom-shutdown");
+
+  return NS_OK;
+}
+
+void
+DelayHttpChannelQueue::FireQueue()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mQueue.IsEmpty()) {
+    return;
+  }
+
+  //TODO: get this from the DOM clock?
+  TimeStamp ts = TimeStamp::Now();
+
+  FallibleTArray<RefPtr<nsHttpChannel>> queue;
+  queue.SwapElements(mQueue);
+
+  for (RefPtr<nsHttpChannel>& channel : queue) {
+    channel->AsyncOpenFinal(ts);
+  }
+}
+
+NS_INTERFACE_MAP_BEGIN(DelayHttpChannelQueue)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(DelayHttpChannelQueue)
+NS_IMPL_RELEASE(DelayHttpChannelQueue)
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/DelayHttpChannelQueue.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set et cin ts=4 sw=4 sts=4: */
+/* 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_DelayHttpChannelQueue_h
+#define mozilla_net_DelayHttpChannelQueue_h
+
+#include "nsIObserver.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace net {
+
+class nsHttpChannel;
+
+/**
+ * DelayHttpChannelQueue stores a set of nsHttpChannels that
+ * are ready to fire out onto the network. However, with FuzzyFox,
+ * we can only fire those events at a specific interval, so we
+ * delay them here, in an instance of this class, until we observe
+ * the topic notificaion that we can send them outbound.
+ */
+class DelayHttpChannelQueue final : public nsIObserver
+{
+public:
+  static bool
+  AttemptQueueChannel(nsHttpChannel* aChannel);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+private:
+  DelayHttpChannelQueue();
+  ~DelayHttpChannelQueue();
+
+  bool
+  Initialize();
+
+  void
+  FireQueue();
+
+  FallibleTArray<RefPtr<nsHttpChannel>> mQueue;
+};
+
+} // net namespace
+} // mozilla namespace
+
+#endif // mozilla_net_DelayHttpChannelQueue_h
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -25,16 +25,17 @@
 
 #include "AltDataOutputStreamChild.h"
 #include "CookieServiceChild.h"
 #include "HttpBackgroundChannelChild.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsPrimitives.h"
 #include "nsChannelClassifier.h"
 #include "nsContentPolicyUtils.h"
+#include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsStringStream.h"
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsSerializationHelper.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PerformanceStorage.h"
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -64,16 +64,17 @@ SOURCES += [
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
     'AltDataOutputStreamParent.cpp',
     'AlternateServices.cpp',
     'ASpdySession.cpp',
     'BackgroundChannelRegistrar.cpp',
     'CacheControlParser.cpp',
     'ConnectionDiagnostics.cpp',
+    'DelayHttpChannelQueue.cpp',
     'Http2Compression.cpp',
     'Http2Push.cpp',
     'Http2Session.cpp',
     'Http2Stream.cpp',
     'HttpAuthUtils.cpp',
     'HttpBackgroundChannelChild.cpp',
     'HttpBackgroundChannelParent.cpp',
     'HttpBaseChannel.cpp',
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6416,25 +6416,40 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     }
 
     mIsPending = true;
     mWasOpened = true;
 
     mListener = listener;
     mListenerContext = context;
 
+    // PauseTask/DelayHttpChannel queuing
+    if (!DelayHttpChannelQueue::AttemptQueueChannel(this)) {
+        // If fuzzyfox is disabled; or adding to the queue failed, the channel must continue.
+        AsyncOpenFinal(TimeStamp::Now());
+    }
+
+    return NS_OK;
+}
+
+nsresult
+nsHttpChannel::AsyncOpenFinal(TimeStamp aTimeStamp)
+{
+    // Added due to PauseTask/DelayHttpChannel
+    nsresult rv;
+
     if (mLoadGroup)
         mLoadGroup->AddRequest(this, nullptr);
 
     // record asyncopen time unconditionally and clear it if we
     // don't want it after OnModifyRequest() weighs in. But waiting for
     // that to complete would mean we don't include proxy resolution in the
     // timing.
     if (!mAsyncOpenTimeOverriden) {
-      mAsyncOpenTime = TimeStamp::Now();
+      mAsyncOpenTime = aTimeStamp;
     }
 
     // Remember we have Authorization header set here.  We need to check on it
     // just once and early, AsyncOpen is the best place.
     mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
 
     // The common case for HTTP channels is to begin proxy resolution and return
     // at this point. The only time we know mProxyInfo already is if we're
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -2,16 +2,17 @@
 /* vim:set et cin ts=4 sw=4 sts=4: */
 /* 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 nsHttpChannel_h__
 #define nsHttpChannel_h__
 
+#include "DelayHttpChannelQueue.h"
 #include "HttpBaseChannel.h"
 #include "nsTArray.h"
 #include "nsICachingChannel.h"
 #include "nsICacheEntry.h"
 #include "nsICacheEntryOpenCallback.h"
 #include "nsIDNSListener.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIChannelWithDivertableParentListener.h"
@@ -200,16 +201,18 @@ public:
     HttpChannelSecurityWarningReporter* GetWarningReporter();
 public: /* internal necko use only */
 
     uint32_t GetRequestTime() const
     {
         return mRequestTime;
     }
 
+    nsresult AsyncOpenFinal(TimeStamp aTimeStamp);
+
     MOZ_MUST_USE nsresult OpenCacheEntry(bool usingSSL);
     MOZ_MUST_USE nsresult OpenCacheEntryInternal(bool isHttps,
                                                  nsIApplicationCache *applicationCache,
                                                  bool noAppCache);
     MOZ_MUST_USE nsresult ContinueConnect();
 
     MOZ_MUST_USE nsresult StartRedirectChannelToURI(nsIURI *, uint32_t);