Bug 1618287 - PreloaderBase class, r=smaug
authorHonza Bambas <honzab.moz@firemni.cz>
Mon, 11 May 2020 14:20:39 +0000
changeset 529092 86c478d114ec223ecf3698ddbb52977bf786f10c
parent 529091 5c64c1dfb318158b4d5ffffa04e48686d150d29e
child 529093 49d286ded8dc4e2f5ec011605db317de729969fe
push id37404
push usercsabou@mozilla.com
push dateMon, 11 May 2020 21:47:06 +0000
treeherdermozilla-central@61a83cc0b74b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1618287
milestone78.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 1618287 - PreloaderBase class, r=smaug Depends on D71800 Differential Revision: https://phabricator.services.mozilla.com/D67479
netwerk/base/nsURIHashKey.h
uriloader/moz.build
uriloader/preload/PreloadHashKey.cpp
uriloader/preload/PreloadHashKey.h
uriloader/preload/PreloaderBase.cpp
uriloader/preload/PreloaderBase.h
uriloader/preload/moz.build
--- a/netwerk/base/nsURIHashKey.h
+++ b/netwerk/base/nsURIHashKey.h
@@ -16,25 +16,31 @@
 /**
  * Hashtable key class to use with nsTHashtable/nsBaseHashtable
  */
 class nsURIHashKey : public PLDHashEntryHdr {
  public:
   typedef nsIURI* KeyType;
   typedef const nsIURI* KeyTypePointer;
 
+  nsURIHashKey() { MOZ_COUNT_CTOR(nsURIHashKey); }
   explicit nsURIHashKey(const nsIURI* aKey) : mKey(const_cast<nsIURI*>(aKey)) {
     MOZ_COUNT_CTOR(nsURIHashKey);
   }
   nsURIHashKey(nsURIHashKey&& toMove)
       : PLDHashEntryHdr(std::move(toMove)), mKey(std::move(toMove.mKey)) {
     MOZ_COUNT_CTOR(nsURIHashKey);
   }
   MOZ_COUNTED_DTOR(nsURIHashKey)
 
+  nsURIHashKey& operator=(const nsURIHashKey& aOther) {
+    mKey = aOther.mKey;
+    return *this;
+  }
+
   nsIURI* GetKey() const { return mKey; }
 
   bool KeyEquals(const nsIURI* aKey) const {
     bool eq;
     if (!mKey) {
       return !aKey;
     }
     if (NS_SUCCEEDED(mKey->Equals(const_cast<nsIURI*>(aKey), &eq))) {
--- a/uriloader/moz.build
+++ b/uriloader/moz.build
@@ -8,9 +8,10 @@ SPHINX_TREES['/uriloader'] = 'docs'
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'File Handling')
 
 DIRS += [
     'base',
     'exthandler',
     'prefetch',
+    'preload',
 ]
new file mode 100644
--- /dev/null
+++ b/uriloader/preload/PreloadHashKey.cpp
@@ -0,0 +1,108 @@
+/* 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 "PreloadHashKey.h"
+
+#include "nsIPrincipal.h"
+#include "nsIReferrerInfo.h"
+
+namespace mozilla {
+
+PreloadHashKey::PreloadHashKey(const PreloadHashKey* aKey)
+    : nsURIHashKey(aKey->mKey) {
+  *this = *aKey;
+}
+
+PreloadHashKey::PreloadHashKey(PreloadHashKey&& aToMove)
+    : nsURIHashKey(std::move(aToMove)) {
+  mAs = std::move(aToMove.mAs);
+  mCORSMode = std::move(aToMove.mCORSMode);
+  mReferrerPolicy = std::move(aToMove.mReferrerPolicy);
+
+  switch (mAs) {
+    case ResourceType::SCRIPT:
+      break;
+    case ResourceType::STYLE:
+      break;
+    case ResourceType::IMAGE:
+      break;
+    case ResourceType::FONT:
+      break;
+    case ResourceType::FETCH:
+      break;
+    case ResourceType::NONE:
+      break;
+  }
+}
+
+PreloadHashKey& PreloadHashKey::operator=(const PreloadHashKey& aOther) {
+  MOZ_ASSERT(mAs == ResourceType::NONE || aOther.mAs == ResourceType::NONE,
+             "Assigning more than once, only reset is allowed");
+
+  nsURIHashKey::operator=(aOther);
+
+  mAs = aOther.mAs;
+  mCORSMode = aOther.mCORSMode;
+  mReferrerPolicy = aOther.mReferrerPolicy;
+
+  switch (mAs) {
+    case ResourceType::SCRIPT:
+      break;
+    case ResourceType::STYLE:
+      break;
+    case ResourceType::IMAGE:
+      break;
+    case ResourceType::FONT:
+      break;
+    case ResourceType::FETCH:
+      break;
+    case ResourceType::NONE:
+      break;
+  }
+
+  return *this;
+}
+
+bool PreloadHashKey::KeyEquals(KeyTypePointer aOther) const {
+  if (mAs != aOther->mAs || mCORSMode != aOther->mCORSMode ||
+      mReferrerPolicy != aOther->mReferrerPolicy) {
+    return false;
+  }
+
+  if (!nsURIHashKey::KeyEquals(
+          static_cast<const nsURIHashKey*>(aOther)->GetKey())) {
+    return false;
+  }
+
+  switch (mAs) {
+    case ResourceType::SCRIPT:
+      break;
+    case ResourceType::STYLE:
+      break;
+    case ResourceType::IMAGE:
+      break;
+    case ResourceType::FONT:
+      break;
+    case ResourceType::FETCH:
+      break;
+    case ResourceType::NONE:
+      break;
+  }
+
+  return true;
+}
+
+// static
+PLDHashNumber PreloadHashKey::HashKey(KeyTypePointer aKey) {
+  PLDHashNumber hash = nsURIHashKey::HashKey(aKey->mKey);
+
+  // Enough to use the common attributes
+  hash = AddToHash(hash, static_cast<uint32_t>(aKey->mAs));
+  hash = AddToHash(hash, static_cast<uint32_t>(aKey->mCORSMode));
+  hash = AddToHash(hash, static_cast<uint32_t>(aKey->mReferrerPolicy));
+
+  return hash;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/uriloader/preload/PreloadHashKey.h
@@ -0,0 +1,74 @@
+/* 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 PreloadHashKey_h__
+#define PreloadHashKey_h__
+
+#include "mozilla/CORSMode.h"
+#include "mozilla/dom/ReferrerPolicyBinding.h"
+#include "nsURIHashKey.h"
+
+class nsIPrincipal;
+class nsIReferrerInfo;
+
+namespace mozilla {
+
+/**
+ * This key is used for coalescing and lookup of preloading or regular
+ * speculative loads.  It consists of:
+ * - the resource type, which is the value of the 'as' attribute
+ * - the URI of the resource
+ * - set of attributes that is common to all resource types
+ * - resource-type-specific attributes that we use to distinguish loads that has
+ *   to be treated separately, some of these attributes may remain at their
+ *   default values
+ */
+class PreloadHashKey : public nsURIHashKey {
+ public:
+  enum class ResourceType : uint8_t { NONE, SCRIPT, STYLE, IMAGE, FONT, FETCH };
+
+  typedef PreloadHashKey* KeyType;
+  typedef const PreloadHashKey* KeyTypePointer;
+
+  PreloadHashKey() = default;
+  explicit PreloadHashKey(const PreloadHashKey* aKey);
+  PreloadHashKey(PreloadHashKey&& aToMove);
+  ~PreloadHashKey() = default;
+
+  PreloadHashKey& operator=(const PreloadHashKey& aOther);
+
+  // TODO
+  // static CreateAsScript(...);
+  // static CreateAsStyle(...);
+  // static CreateAsImage(...);
+  // static CreateAsFont(...);
+  // static CreateAsFetch(...);
+
+  KeyType GetKey() const { return const_cast<PreloadHashKey*>(this); }
+  KeyTypePointer GetKeyPointer() const { return this; }
+  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+
+  bool KeyEquals(KeyTypePointer aOther) const;
+  static PLDHashNumber HashKey(KeyTypePointer aKey);
+
+#ifdef MOZILLA_INTERNAL_API
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+    // Bug 1627752
+    return 0;
+  }
+#endif
+
+  enum { ALLOW_MEMMOVE = true };
+
+ private:
+  // Attributes common to all resource types
+  ResourceType mAs = ResourceType::NONE;
+
+  CORSMode mCORSMode = CORS_NONE;
+  enum dom::ReferrerPolicy mReferrerPolicy = dom::ReferrerPolicy::_empty;
+};
+
+}  // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/uriloader/preload/PreloaderBase.cpp
@@ -0,0 +1,227 @@
+/* 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 "PreloaderBase.h"
+
+#include "mozilla/dom/Document.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIChannel.h"
+#include "nsILoadGroup.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+namespace mozilla {
+
+PreloaderBase::RedirectSink::RedirectSink(PreloaderBase* aPreloader,
+                                          nsIInterfaceRequestor* aCallbacks)
+    : mPreloader(new nsMainThreadPtrHolder<PreloaderBase>(
+          "RedirectSink.mPreloader", aPreloader)),
+      mCallbacks(aCallbacks) {}
+
+NS_IMPL_ISUPPORTS(PreloaderBase::RedirectSink, nsIInterfaceRequestor,
+                  nsIChannelEventSink, nsIRedirectResultListener)
+
+NS_IMETHODIMP PreloaderBase::RedirectSink::AsyncOnChannelRedirect(
+    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
+    nsIAsyncVerifyRedirectCallback* aCallback) {
+  mRedirectChannel = aNewChannel;
+
+  if (mCallbacks) {
+    nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mCallbacks));
+    if (sink) {
+      return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags,
+                                          aCallback);
+    }
+  }
+
+  aCallback->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+NS_IMETHODIMP PreloaderBase::RedirectSink::OnRedirectResult(bool proceeding) {
+  if (proceeding && mRedirectChannel) {
+    mPreloader->mChannel = std::move(mRedirectChannel);
+  } else {
+    mRedirectChannel = nullptr;
+  }
+
+  if (mCallbacks) {
+    nsCOMPtr<nsIRedirectResultListener> sink(do_GetInterface(mCallbacks));
+    if (sink) {
+      return sink->OnRedirectResult(proceeding);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP PreloaderBase::RedirectSink::GetInterface(const nsIID& aIID,
+                                                        void** aResult) {
+  NS_ENSURE_ARG_POINTER(aResult);
+
+  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) ||
+      aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
+    return QueryInterface(aIID, aResult);
+  }
+
+  if (mCallbacks) {
+    return mCallbacks->GetInterface(aIID, aResult);
+  }
+
+  *aResult = nullptr;
+  return NS_ERROR_NO_INTERFACE;
+}
+
+PreloaderBase::~PreloaderBase() { MOZ_ASSERT(NS_IsMainThread()); }
+
+// static
+void PreloaderBase::AddLoadBackgroundFlag(nsIChannel* aChannel) {
+  nsLoadFlags loadFlags;
+  aChannel->GetLoadFlags(&loadFlags);
+  aChannel->SetLoadFlags(loadFlags | nsIRequest::LOAD_BACKGROUND);
+}
+
+void PreloaderBase::NotifyOpen(PreloadHashKey* aKey, nsIChannel* aChannel,
+                               dom::Document* aDocument, bool aIsPreload) {
+  // * Register this preload in document's preload service.
+
+  mChannel = aChannel;
+  mKey = *aKey;
+  mIsUsed = !aIsPreload;
+
+  // * Start the usage timer if aIsPreload.
+
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  mChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  RefPtr<RedirectSink> sink(new RedirectSink(this, callbacks));
+  mChannel->SetNotificationCallbacks(sink);
+}
+
+void PreloaderBase::NotifyUsage() {
+  if (!mIsUsed && mChannel) {
+    nsLoadFlags loadFlags;
+    mChannel->GetLoadFlags(&loadFlags);
+
+    // Preloads are initially set the LOAD_BACKGROUND flag.  When becoming
+    // regular loads by hitting its consuming tag, we need to drop that flag,
+    // which also means to re-add the request from/to it's loadgroup to reflect
+    // that flag change.
+    if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
+      nsCOMPtr<nsILoadGroup> loadGroup;
+      mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+
+      if (loadGroup) {
+        nsresult status;
+        mChannel->GetStatus(&status);
+
+        nsresult rv = loadGroup->RemoveRequest(mChannel, nullptr, status);
+        mChannel->SetLoadFlags(loadFlags & ~nsIRequest::LOAD_BACKGROUND);
+        if (NS_SUCCEEDED(rv)) {
+          loadGroup->AddRequest(mChannel, nullptr);
+        }
+      }
+    }
+  }
+
+  mIsUsed = true;
+
+  // * Cancel the usage timer.
+}
+
+void PreloaderBase::NotifyRestart(dom::Document* aDocument,
+                                  PreloaderBase* aNewPreloader) {
+  // * Deregister this preload from Document's preloads
+  mKey = PreloadHashKey();
+
+  if (aNewPreloader) {
+    aNewPreloader->mNodes = std::move(mNodes);
+  }
+}
+
+void PreloaderBase::NotifyStart(nsIRequest* aRequest) {
+  if (!SameCOMIdentity(aRequest, mChannel)) {
+    return;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+  if (!httpChannel) {
+    return;
+  }
+
+  // if the load is cross origin without CORS, or the CORS access is rejected,
+  // always fire load event to avoid leaking site information.
+  nsresult rv;
+  nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
+  mShouldFireLoadEvent =
+      loadInfo->GetTainting() == LoadTainting::Opaque ||
+      (loadInfo->GetTainting() == LoadTainting::CORS &&
+       (NS_FAILED(httpChannel->GetStatus(&rv)) || NS_FAILED(rv)));
+}
+
+void PreloaderBase::NotifyStop(nsIRequest* aRequest, nsresult aStatus) {
+  // Filter out notifications that may be arriving from the old channel before
+  // restarting this request.
+  if (!SameCOMIdentity(aRequest, mChannel)) {
+    return;
+  }
+
+  NotifyStop(aStatus);
+}
+
+void PreloaderBase::NotifyStop(nsresult aStatus) {
+  mOnStopStatus.emplace(aStatus);
+
+  nsTArray<nsWeakPtr> nodes;
+  nodes.SwapElements(mNodes);
+
+  for (nsWeakPtr& weak : nodes) {
+    nsCOMPtr<nsINode> node = do_QueryReferent(weak);
+    if (node) {
+      NotifyNodeEvent(node);
+    }
+  }
+
+  mChannel = nullptr;
+}
+
+void PreloaderBase::NotifyValidating() { mOnStopStatus.reset(); }
+
+void PreloaderBase::NotifyValidated(nsresult aStatus) {
+  NotifyStop(nullptr, aStatus);
+}
+
+void PreloaderBase::AddLinkPreloadNode(nsINode* aNode) {
+  if (mOnStopStatus) {
+    return NotifyNodeEvent(aNode);
+  }
+
+  mNodes.AppendElement(do_GetWeakReference(aNode));
+}
+
+void PreloaderBase::RemoveLinkPreloadNode(nsINode* aNode) {
+  // Note that do_GetWeakReference returns the internal weak proxy, which is
+  // always the same, so we can use it to search the array using default
+  // comparator.
+  nsWeakPtr node = do_GetWeakReference(aNode);
+  mNodes.RemoveElement(node);
+
+  if (mNodes.Length() == 0 && !mIsUsed) {
+    // * Deregister this preload from document's preloads
+
+    if (mChannel) {
+      mChannel->Cancel(NS_BINDING_ABORTED);
+    }
+  }
+}
+
+void PreloaderBase::NotifyNodeEvent(nsINode* aNode) {
+  // * Notify load or error event on the node
+}
+
+nsresult PreloaderBase::AsyncConsume(nsIStreamListener* aListener) {
+  // We want to return an error so that consumers can't ever use a preload to
+  // consume data unless it's properly implemented.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/uriloader/preload/PreloaderBase.h
@@ -0,0 +1,166 @@
+/* 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 PreloaderBase_h__
+#define PreloaderBase_h__
+
+#include "mozilla/Maybe.h"
+#include "mozilla/PreloadHashKey.h"
+#include "mozilla/WeakPtr.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIRedirectResultListener.h"
+#include "nsIURI.h"
+#include "nsTArray.h"
+#include "nsProxyRelease.h"
+#include "nsWeakReference.h"
+
+class nsIChannel;
+class nsINode;
+class nsIRequest;
+class nsIStreamListener;
+
+namespace mozilla {
+
+namespace dom {
+
+class Document;
+
+}  // namespace dom
+
+/**
+ * A half-abstract base class that resource loaders' respective
+ * channel-listening classes should derive from.  Provides a unified API to
+ * register the load or preload in a document scoped service, links <link
+ * rel="preload"> DOM nodes with the load progress and provides API to possibly
+ * consume the data by later, dynamically discovered consumers.
+ *
+ * This class is designed to be used only on the main thread.
+ */
+class PreloaderBase : public SupportsWeakPtr<PreloaderBase>,
+                      public nsISupports {
+ public:
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PreloaderBase)
+  PreloaderBase() = default;
+
+  // Called by resource loaders to register this preload in the document's
+  // preload service to provide coalescing, and access to the preload when it
+  // should be used for an actual load.
+  void NotifyOpen(PreloadHashKey* aKey, nsIChannel* aChannel,
+                  dom::Document* aDocument, bool aIsPreload);
+
+  // Called when the load is about to be started all over again and thus this
+  // PreloaderBase will be registered again with the same key.  This method
+  // taks care to deregister this preload prior to that.
+  // @param aNewPreloader: If there is a new request and loader created for the
+  // restarted load, we will pass any necessary information into it.
+  void NotifyRestart(dom::Document* aDocument,
+                     PreloaderBase* aNewPreloader = nullptr);
+
+  // Called by the loading object when the channel started to load
+  // (OnStartRequest or equal) and when it finished (OnStopRequest or equal)
+  void NotifyStart(nsIRequest* aRequest);
+  void NotifyStop(nsIRequest* aRequest, nsresult aStatus);
+
+  // Called when this currently existing load has to be asynchronously
+  // revalidated before it can be used.  This prevents link preload DOM nodes
+  // being notified until the validation is resolved.
+  void NotifyValidating();
+  // Called when the validation process has been done.  This will notify
+  // associated link DOM nodes.
+  void NotifyValidated(nsresult aStatus);
+
+  // Called by resource loaders or any suitable component to notify the preload
+  // has been used for an actual load.  This is intended to stop any usage
+  // timers.
+  void NotifyUsage();
+  // Whether this preloader has been used for a regular/actual load or not.
+  bool IsUsed() const { return mIsUsed; }
+
+  // When a loader starting an actual load finds a preload, the data can be
+  // delivered using this method.  It will deliver stream listener notifications
+  // as if it were coming from the resource loading channel.  The |request|
+  // argument will be the channel that loaded/loads the resource.
+  // This method must keep to the nsIChannel.AsyncOpen contract.  A loader is
+  // not obligated to re-implement this method when not necessarily needed.
+  virtual nsresult AsyncConsume(nsIStreamListener* aListener);
+
+  // Accessor to the resource loading channel.
+  nsIChannel* Channel() const { return mChannel; }
+
+  // May change priority of the resource loading channel so that it's treated as
+  // preload when this was initially representing a normal speculative load but
+  // later <link rel="preload"> was found for this resource.
+  virtual void PrioritizeAsPreload() = 0;
+
+  // Helper function to set the LOAD_BACKGROUND flag on channel initiated by
+  // <link rel=preload>.  This MUST be used before the channel is AsyncOpen'ed.
+  static void AddLoadBackgroundFlag(nsIChannel* aChannel);
+
+  // These are linking this preload to <link rel="preload"> DOM nodes.  If we
+  // are already loaded, immediately notify events on the node, otherwise wait
+  // for NotifyStop() call.
+  void AddLinkPreloadNode(nsINode* aNode);
+  void RemoveLinkPreloadNode(nsINode* aNode);
+
+ protected:
+  virtual ~PreloaderBase();
+
+ private:
+  void NotifyStop(nsresult aStatus);
+  void NotifyNodeEvent(nsINode* aNode);
+
+  // A helper class that will update the PreloaderBase.mChannel member when a
+  // redirect happens, so that we can reprioritize or cancel when needed.
+  // Having a separate class instead of implementing this on PreloaderBase
+  // directly is to keep PreloaderBase as simple as possible so that derived
+  // classes don't have to deal with calling super when implementing these
+  // interfaces from some reason as well.
+  class RedirectSink final : public nsIInterfaceRequestor,
+                             public nsIChannelEventSink,
+                             public nsIRedirectResultListener {
+    RedirectSink() = delete;
+    virtual ~RedirectSink() = default;
+
+   public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSIINTERFACEREQUESTOR
+    NS_DECL_NSICHANNELEVENTSINK
+    NS_DECL_NSIREDIRECTRESULTLISTENER
+
+    RedirectSink(PreloaderBase* aPreloader, nsIInterfaceRequestor* aCallbacks);
+
+   private:
+    nsMainThreadPtrHandle<PreloaderBase> mPreloader;
+    nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+    nsCOMPtr<nsIChannel> mRedirectChannel;
+  };
+
+ private:
+  // Reference to HTMLLinkElement DOM nodes to deliver onload and onerror
+  // notifications to.
+  nsTArray<nsWeakPtr> mNodes;
+
+  // The loading channel.  This will update when a redirect occurs.
+  nsCOMPtr<nsIChannel> mChannel;
+
+  // The key this preload has been registered under.  We want to remember it to
+  // be able to deregister itself from the document's preloads.
+  PreloadHashKey mKey;
+
+  // This overrides the final event we send to DOM nodes to be always 'load'.
+  // Modified in NotifyStart based on LoadInfo data of the loading channel.
+  bool mShouldFireLoadEvent = false;
+
+  // True after call to NotifyUsage.
+  bool mIsUsed = false;
+
+  // Emplaced when the data delivery has finished, in NotifyStop, holds the
+  // result of the load.
+  Maybe<nsresult> mOnStopStatus;
+};
+
+}  // namespace mozilla
+
+#endif  // !PreloaderBase_h__
new file mode 100644
--- /dev/null
+++ b/uriloader/preload/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Networking")
+
+EXPORTS.mozilla += [
+    'PreloaderBase.h',
+    'PreloadHashKey.h',
+]
+
+UNIFIED_SOURCES += [
+    'PreloaderBase.cpp',
+    'PreloadHashKey.cpp',
+]
+
+FINAL_LIBRARY = 'xul'