Bug 1331680 - Part 1: Send required cookies to the content process on demand. r=jdm
authorAmy Chung <amchung@mozilla.com>
Thu, 03 Aug 2017 19:00:41 +0800
changeset 424692 5a87c9d62dc4658cfb14daead2cd5c5cff478a51
parent 424691 cbda2eff9f939a0ef34fc45e534a3a86b1fe4afd
child 424693 84714cef1f52826cd7eec252331b4fbe813b33d4
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1331680
milestone57.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 1331680 - Part 1: Send required cookies to the content process on demand. r=jdm
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/html/nsHTMLDocument.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
netwerk/base/nsIRequest.idl
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceChild.h
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/CookieServiceParent.h
netwerk/cookie/PCookieService.ipdl
netwerk/cookie/moz.build
netwerk/cookie/nsCookie.h
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/moz.build
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8634,16 +8634,22 @@ public:
 private:
   nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
     mRestorePresentationEvent;
   RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
 };
 
 } // namespace
 
+bool
+nsDocShell::SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags)
+{
+  return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
+}
+
 nsresult
 nsDocShell::RestoreFromHistory()
 {
   MOZ_ASSERT(mRestorePresentationEvent.IsPending());
   PresentationEventForgetter forgetter(mRestorePresentationEvent);
 
   // This section of code follows the same ordering as CreateContentViewer.
   if (!mLSHE) {
@@ -9294,16 +9300,19 @@ nsDocShell::CreateContentViewer(const ns
      * we don't null-out mLSHE in OnStateChange() for
      * all redirected urls
      */
     aOpenedChannel->SetLoadGroup(mLoadGroup);
 
     // Mark the channel as being a document URI...
     aOpenedChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+    if (SandboxFlagsImplyCookies(mSandboxFlags)) {
+      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+    }
 
     aOpenedChannel->SetLoadFlags(loadFlags);
 
     mLoadGroup->AddRequest(aRequest, nullptr);
     if (currentLoadGroup) {
       currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
     }
 
@@ -11510,16 +11519,19 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
 {
   nsresult rv;
   // Mark the channel as being a document URI and allow content sniffing...
   nsLoadFlags loadFlags = 0;
   (void)aChannel->GetLoadFlags(&loadFlags);
   loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
                nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
 
+  if (SandboxFlagsImplyCookies(mSandboxFlags)) {
+    loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+  }
   // Load attributes depend on load type...
   switch (mLoadType) {
     case LOAD_HISTORY: {
       // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
       // push/replaceState (bug 669671).
       bool uriModified = false;
       if (mLSHE) {
         mLSHE->GetURIWasModified(&uriModified);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -252,16 +252,18 @@ public:
   // ForceRefreshURI method on nsIRefreshURI, but makes sure to take
   // the timer involved out of mRefreshURIList if it's there.
   // aTimer must not be null.
   nsresult ForceRefreshURIFromTimer(nsIURI* aURI, int32_t aDelay,
                                     bool aMetaRefresh, nsITimer* aTimer);
 
   friend class OnLinkClickEvent;
 
+  static bool SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags);
+
   // We need dummy OnLocationChange in some cases to update the UI without
   // updating security info.
   void FireDummyOnLocationChange()
   {
     FireOnLocationChange(this, nullptr, mCurrentURI,
                          LOCATION_CHANGE_SAME_DOCUMENT);
   }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2471,16 +2471,27 @@ WarnIfSandboxIneffective(nsIDocShell* aD
                                     NS_LITERAL_CSTRING("Iframe Sandbox"),
                                     parentDocument,
                                     nsContentUtils::eSECURITY_PROPERTIES,
                                     "BothAllowScriptsAndSameOriginPresent",
                                     nullptr, 0, iframeUri);
   }
 }
 
+bool
+nsDocument::IsSynthesized() {
+  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
+  bool synthesized = false;
+  if (internalChan) {
+    DebugOnly<nsresult> rv = internalChan->GetResponseSynthesized(&synthesized);
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
+  }
+  return synthesized;
+}
+
 nsresult
 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                               nsILoadGroup* aLoadGroup,
                               nsISupports* aContainer,
                               nsIStreamListener **aDocListener,
                               bool aReset, nsIContentSink* aSink)
 {
   if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
@@ -2541,16 +2552,29 @@ nsDocument::StartDocumentLoad(const char
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       mIsSrcdocDocument = true;
     }
   }
 
+  if (mChannel) {
+    nsLoadFlags loadFlags;
+    mChannel->GetLoadFlags(&loadFlags);
+    bool isDocument = false;
+    mChannel->GetIsDocument(&isDocument);
+    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
+        isDocument &&
+        IsSynthesized() &&
+        XRE_IsContentProcess()) {
+      ContentChild::UpdateCookieStatus(mChannel);
+    }
+  }
+
   // If this document is being loaded by a docshell, copy its sandbox flags
   // to the document, and store the fullscreen enabled flag. These are
   // immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   // If this is an error page, don't inherit sandbox flags from docshell
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -662,16 +662,17 @@ public:
   virtual void ScheduleIntersectionObserverNotification() override;
   virtual void NotifyIntersectionObservers() override;
 
   virtual void NotifyLayerManagerRecreated() override;
 
   virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
   virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
   virtual void ResolveScheduledSVGPresAttrs() override;
+  bool IsSynthesized();
 
 private:
   void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
   void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
 
 public:
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2433,16 +2433,19 @@ nsHTMLDocument::CreateAndAddWyciwygChann
   // Use the Parent document's loadgroup to trigger load notifications
   if (loadGroup && channel) {
     rv = channel->SetLoadGroup(loadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsLoadFlags loadFlags = 0;
     channel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+    if (nsDocShell::SandboxFlagsImplyCookies(mSandboxFlags)) {
+      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+    }
     channel->SetLoadFlags(loadFlags);
 
     channel->SetOriginalURI(wcwgURI);
 
     rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
   }
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -56,16 +56,17 @@
 #include "mozilla/jsipc/PJavaScript.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorManagerChild.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/CaptivePortalService.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
@@ -1942,16 +1943,26 @@ ContentChild::GetCPOWManager()
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
 {
   return IPC_OK();
 }
 
+void
+ContentChild::UpdateCookieStatus(nsIChannel   *aChannel)
+{
+  RefPtr<CookieServiceChild> csChild =
+    CookieServiceChild::GetSingleton();
+  NS_ASSERTION(csChild, "Couldn't get CookieServiceChild");
+
+  csChild->TrackCookieLoad(aChannel);
+}
+
 PScriptCacheChild*
 ContentChild::AllocPScriptCacheChild(const FileDescOrError& cacheFile, const bool& wantCacheData)
 {
   return new loader::ScriptCacheChild();
 }
 
 bool
 ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -140,16 +140,18 @@ public:
 #endif
 
   bool IsAlive() const;
 
   bool IsShuttingDown() const;
 
   static void AppendProcessId(nsACString& aName);
 
+  static void UpdateCookieStatus(nsIChannel *aChannel);
+
   mozilla::ipc::IPCResult
   RecvInitContentBridgeChild(Endpoint<PContentBridgeChild>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult
   RecvInitGMPService(Endpoint<PGMPServiceChild>&& aGMPService) override;
 
   mozilla::ipc::IPCResult
   RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -22,16 +22,17 @@
 #include "GeckoProfiler.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #include "mozilla/a11y/AccessibleWrap.h"
 #endif
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
@@ -75,16 +76,18 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/CookieServiceParent.h"
+#include "mozilla/net/PCookieServiceParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -5040,16 +5043,27 @@ void
 ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
 {
   if (!mHangMonitorActor) {
     return;
   }
   ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
 }
 
+void
+ContentParent::UpdateCookieStatus(nsIChannel   *aChannel)
+{
+  PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+  PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+  if (csParent) {
+    auto *cs = static_cast<CookieServiceParent*>(csParent);
+    cs->TrackCookieLoad(aChannel);
+  }
+}
+
 nsresult
 ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
   nsresult rv;
   if (!aChannel->IsDocument()) {
     return NS_OK;
@@ -5064,16 +5078,24 @@ ContentParent::AboutToLoadHttpFtpWyciwyg
 
   nsCOMPtr<nsIPrincipal> principal;
   rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TransmitPermissionsForPrincipal(principal);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsLoadFlags newLoadFlags;
+  aChannel->GetLoadFlags(&newLoadFlags);
+  bool isDocument = false;
+  aChannel->GetIsDocument(&isDocument);
+  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {
+    UpdateCookieStatus(aChannel);
+  }
+
   return NS_OK;
 }
 
 nsresult
 ContentParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal)
 {
 #ifdef MOZ_PERMISSIONS
   // Create the key, and send it down to the content process.
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -341,16 +341,18 @@ public:
   UnregisterRemoteFrame(const TabId& aTabId,
                         const ContentParentId& aCpId,
                         bool aMarkedDestroying);
 
   void ReportChildAlreadyBlocked();
 
   bool RequestRunToCompletion();
 
+  void UpdateCookieStatus(nsIChannel *aChannel);
+
   bool IsAvailable() const
   {
     return mIsAvailable;
   }
   bool IsAlive() const override;
 
   virtual bool IsForBrowser() const override
   {
--- a/netwerk/base/nsIRequest.idl
+++ b/netwerk/base/nsIRequest.idl
@@ -126,16 +126,22 @@ interface nsIRequest : nsISupports
 
     /**
      * This flag marks the request as being made to load the data for an html
      * <object> tag. This means that the LOAD_DOCUMENT_URI flag may be set after
      * the channel has been provided with the MIME type.
      */
     const unsigned long LOAD_HTML_OBJECT_DATA = 1 << 1;
 
+    /**
+     * This flag marks the request as belonging to a document that requires access
+     * to the document.cookies API.
+     */
+    const unsigned long LOAD_DOCUMENT_NEEDS_COOKIE = 1 << 2;
+
     /**************************************************************************
      * The following flags control the flow of data into the cache.
      */
 
     /**
      * This flag prevents caching of any kind.  It does not, however, prevent
      * cached content from being used to satisfy this request.
      */
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -1,26 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "mozilla/net/CookieServiceChild.h"
+#include "mozilla/net/NeckoChannelParams.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
+#include "nsCookie.h"
+#include "nsCookieService.h"
+#include "nsContentUtils.h"
+#include "nsNetCID.h"
 #include "nsIChannel.h"
+#include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla::ipc;
+using mozilla::OriginAttributes;
 
 namespace mozilla {
 namespace net {
 
 // Pref string constants
 static const char kPrefCookieBehavior[] = "network.cookie.cookieBehavior";
 static const char kPrefThirdPartySession[] =
   "network.cookie.thirdparty.sessionOnly";
@@ -56,16 +63,19 @@ CookieServiceChild::CookieServiceChild()
 
   // This corresponds to Release() in DeallocPCookieService.
   NS_ADDREF_THIS();
 
   // Create a child PCookieService actor.
   NeckoChild::InitNeckoChild();
   gNeckoChild->SendPCookieServiceConstructor(this);
 
+  mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+  NS_ASSERTION(mTLDService, "couldn't get TLDService");
+
   // Init our prefs and observer.
   nsCOMPtr<nsIPrefBranch> prefBranch =
     do_GetService(NS_PREFSERVICE_CONTRACTID);
   NS_WARNING_ASSERTION(prefBranch, "no prefservice");
   if (prefBranch) {
     prefBranch->AddObserver(kPrefCookieBehavior, this, true);
     prefBranch->AddObserver(kPrefThirdPartySession, this, true);
     PrefChanged(prefBranch);
@@ -73,16 +83,57 @@ CookieServiceChild::CookieServiceChild()
 }
 
 CookieServiceChild::~CookieServiceChild()
 {
   gCookieService = nullptr;
 }
 
 void
+CookieServiceChild::TrackCookieLoad(nsIChannel *aChannel)
+{
+  bool isForeign = false;
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+  if (RequireThirdPartyCheck()) {
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
+  }
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  mozilla::OriginAttributes attrs;
+  if (loadInfo) {
+    attrs = loadInfo->GetOriginAttributes();
+  }
+  URIParams uriParams;
+  SerializeURI(uri, uriParams);
+  SendPrepareCookieList(uriParams, isForeign, attrs);
+}
+
+mozilla::ipc::IPCResult
+CookieServiceChild::RecvTrackCookiesLoad(nsTArray<CookieStruct>&& aCookiesList,
+                                         const OriginAttributes &aAttrs)
+{
+  for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
+    RefPtr<nsCookie> cookie = nsCookie::Create(aCookiesList[i].name(),
+                                               aCookiesList[i].value(),
+                                               aCookiesList[i].host(),
+                                               aCookiesList[i].path(),
+                                               aCookiesList[i].expiry(),
+                                               aCookiesList[i].lastAccessed(),
+                                               aCookiesList[i].creationTime(),
+                                               aCookiesList[i].isSession(),
+                                               aCookiesList[i].isSecure(),
+                                               false,
+                                               aAttrs);
+    RecordDocumentCookie(cookie, aAttrs);
+  }
+
+  return IPC_OK();
+}
+
+void
 CookieServiceChild::PrefChanged(nsIPrefBranch *aPrefBranch)
 {
   int32_t val;
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieBehavior, &val)))
     mCookieBehavior =
       val >= nsICookieService::BEHAVIOR_ACCEPT &&
       val <= nsICookieService::BEHAVIOR_LIMIT_FOREIGN
         ? val : nsICookieService::BEHAVIOR_ACCEPT;
@@ -100,16 +151,49 @@ CookieServiceChild::PrefChanged(nsIPrefB
 bool
 CookieServiceChild::RequireThirdPartyCheck()
 {
   return mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
     mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
     mThirdPartySession;
 }
 
+void
+CookieServiceChild::RecordDocumentCookie(nsCookie               *aCookie,
+                                         const OriginAttributes &aAttrs)
+{
+  nsAutoCString baseDomain;
+  nsCookieService::
+    GetBaseDomainFromHost(mTLDService, aCookie->Host(), baseDomain);
+
+  nsCookieKey key(baseDomain, aAttrs);
+  CookiesList *cookiesList = nullptr;
+  mCookiesMap.Get(key, &cookiesList);
+
+  if (!cookiesList) {
+    cookiesList = mCookiesMap.LookupOrAdd(key);
+  }
+  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
+    nsCookie *cookie = cookiesList->ElementAt(i);
+    if (cookie->Name().Equals(aCookie->Name()) &&
+        cookie->Host().Equals(aCookie->Host()) &&
+        cookie->Path().Equals(aCookie->Path())) {
+      cookiesList->RemoveElementAt(i);
+      break;
+    }
+  }
+
+  int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
+  if (aCookie->Expiry() <= currentTime) {
+    return;
+  }
+
+  cookiesList->AppendElement(aCookie);
+}
+
 nsresult
 CookieServiceChild::GetCookieStringInternal(nsIURI *aHostURI,
                                             nsIChannel *aChannel,
                                             char **aCookieString)
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG_POINTER(aCookieString);
 
@@ -136,16 +220,17 @@ CookieServiceChild::GetCookieStringInter
     if (loadInfo) {
       attrs = loadInfo->GetOriginAttributes();
     }
   }
 
   // Synchronously call the parent.
   nsAutoCString result;
   SendGetCookieString(uriParams, !!isForeign, attrs, &result);
+
   if (!result.IsEmpty())
     *aCookieString = ToNewCString(result);
 
   return NS_OK;
 }
 
 nsresult
 CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI,
--- a/netwerk/cookie/CookieServiceChild.h
+++ b/netwerk/cookie/CookieServiceChild.h
@@ -2,39 +2,51 @@
 /* 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_CookieServiceChild_h__
 #define mozilla_net_CookieServiceChild_h__
 
 #include "mozilla/net/PCookieServiceChild.h"
+#include "nsClassHashtable.h"
+#include "nsCookieKey.h"
 #include "nsICookieService.h"
 #include "nsIObserver.h"
 #include "nsIPrefBranch.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsWeakReference.h"
 
+class nsCookie;
+class nsIEffectiveTLDService;
+
 namespace mozilla {
 namespace net {
+class CookieStruct;
 
 class CookieServiceChild : public PCookieServiceChild
                          , public nsICookieService
                          , public nsIObserver
                          , public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICOOKIESERVICE
   NS_DECL_NSIOBSERVER
 
+  typedef nsTArray<RefPtr<nsCookie>> CookiesList;
+  typedef nsClassHashtable<nsCookieKey, CookiesList> CookiesMap;
+
   CookieServiceChild();
 
   static CookieServiceChild* GetSingleton();
 
+  void
+  TrackCookieLoad(nsIChannel *aChannel);
+
 protected:
   virtual ~CookieServiceChild();
 
   void SerializeURIs(nsIURI *aHostURI,
                      nsIChannel *aChannel,
                      nsCString &aHostSpec,
                      nsCString &aHostCharset,
                      nsCString &aOriginatingSpec,
@@ -45,21 +57,31 @@ protected:
                                    char **aCookieString);
 
   nsresult SetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    const char *aCookieString,
                                    const char *aServerTime,
                                    bool aFromHttp);
 
+  void
+  RecordDocumentCookie(nsCookie *aCookie,
+                       const OriginAttributes &aAttrs);
+
   void PrefChanged(nsIPrefBranch *aPrefBranch);
 
   bool RequireThirdPartyCheck();
 
+  virtual
+  mozilla::ipc::IPCResult RecvTrackCookiesLoad(nsTArray<CookieStruct>&& aCookiesList,
+                                               const OriginAttributes &aAttrs) override;
+
+  CookiesMap mCookiesMap;
   nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
+  nsCOMPtr<nsIEffectiveTLDService> mTLDService;
   uint8_t mCookieBehavior;
   bool mThirdPartySession;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_CookieServiceChild_h__
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/net/NeckoParent.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "nsCookieService.h"
 #include "nsIChannel.h"
+#include "nsIEffectiveTLDService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsNetCID.h"
 #include "nsPrintfCString.h"
 
 using namespace mozilla::ipc;
 using mozilla::BasePrincipal;
 using mozilla::OriginAttributes;
@@ -73,16 +74,78 @@ CookieServiceParent::CookieServiceParent
   NS_ASSERTION(mCookieService, "couldn't get nsICookieService");
 }
 
 CookieServiceParent::~CookieServiceParent()
 {
 }
 
 void
+CookieServiceParent::TrackCookieLoad(nsIChannel *aChannel)
+{
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  mozilla::OriginAttributes attrs;
+  if (loadInfo) {
+    attrs = loadInfo->GetOriginAttributes();
+  }
+
+  // Send matching cookies to Child.
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
+  thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+  bool isForeign = true;
+  thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
+  nsTArray<nsCookie*> foundCookieList;
+  mCookieService->GetCookiesForURI(uri, isForeign, false,
+                                   attrs, foundCookieList);
+  nsTArray<CookieStruct> matchingCookiesList;
+  SerialializeCookieList(foundCookieList, matchingCookiesList, uri);
+  Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
+}
+
+void
+CookieServiceParent::SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
+                                            nsTArray<CookieStruct>    &aCookiesList,
+                                            nsIURI                    *aHostURI)
+{
+  for (uint32_t i = 0; i < aFoundCookieList.Length(); i++) {
+    nsCookie *cookie = aFoundCookieList.ElementAt(i);
+    CookieStruct* cookieStruct = aCookiesList.AppendElement();
+    cookieStruct->name() = cookie->Name();
+    cookieStruct->value() = cookie->Value();
+    cookieStruct->host() = cookie->Host();
+    cookieStruct->path() = cookie->Path();
+    cookieStruct->expiry() = cookie->Expiry();
+    cookieStruct->lastAccessed() = cookie->LastAccessed();
+    cookieStruct->creationTime() = cookie->CreationTime();
+    cookieStruct->isSession() = cookie->IsSession();
+    cookieStruct->isSecure() = cookie->IsSecure();
+  }
+}
+
+mozilla::ipc::IPCResult
+CookieServiceParent::RecvPrepareCookieList(const URIParams        &aHost,
+                                           const bool             &aIsForeign,
+                                           const OriginAttributes &aAttrs)
+{
+  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
+
+  // Send matching cookies to Child.
+  nsTArray<nsCookie*> foundCookieList;
+  mCookieService->GetCookiesForURI(hostURI, aIsForeign, false,
+                                   aAttrs, foundCookieList);
+  nsTArray<CookieStruct> matchingCookiesList;
+  SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI);
+  Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs);
+  return IPC_OK();
+}
+
+void
 CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
--- a/netwerk/cookie/CookieServiceParent.h
+++ b/netwerk/cookie/CookieServiceParent.h
@@ -3,42 +3,54 @@
  * 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_CookieServiceParent_h
 #define mozilla_net_CookieServiceParent_h
 
 #include "mozilla/net/PCookieServiceParent.h"
 
+class nsCookie;
 class nsCookieService;
 namespace mozilla { class OriginAttributes; }
 
 namespace mozilla {
 namespace net {
 
 class CookieServiceParent : public PCookieServiceParent
 {
 public:
   CookieServiceParent();
   virtual ~CookieServiceParent();
 
+  void TrackCookieLoad(nsIChannel *aChannel);
+
 protected:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual mozilla::ipc::IPCResult RecvGetCookieString(const URIParams& aHost,
                                                       const bool& aIsForeign,
                                                       const OriginAttributes& aAttrs,
                                                       nsCString* aResult) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCookieString(const URIParams& aHost,
                                                       const bool& aIsForeign,
                                                       const nsCString& aCookieString,
                                                       const nsCString& aServerTime,
                                                       const OriginAttributes& aAttrs,
                                                       const bool& aFromHttp) override;
+  virtual
+  mozilla::ipc::IPCResult RecvPrepareCookieList(const URIParams &aHost,
+                                                const bool &aIsForeign,
+                                                const OriginAttributes &aAttrs) override;
+
+  void
+  SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
+                         nsTArray<CookieStruct> &aCookiesList,
+                         nsIURI *aHostURI);
 
   RefPtr<nsCookieService> mCookieService;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_CookieServiceParent_h
--- a/netwerk/cookie/PCookieService.ipdl
+++ b/netwerk/cookie/PCookieService.ipdl
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
 
 /* 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 protocol PNecko;
 include URIParams;
+include NeckoChannelParams;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 
 namespace mozilla {
 namespace net {
 
 /**
  * PCookieService
@@ -95,14 +96,22 @@ parent:
    */
   nested(inside_cpow) async SetCookieString(URIParams host,
                                             bool isForeign,
                                             nsCString cookieString,
                                             nsCString serverTime,
                                             OriginAttributes attrs,
                                             bool aFromHttp);
 
+  async PrepareCookieList(URIParams host,
+                          bool isForeign,
+                          OriginAttributes attrs);
+
   async __delete__();
+
+child:
+  async TrackCookiesLoad(CookieStruct[] cookiesList,
+                         OriginAttributes attrs);
 };
 
 }
 }
 
--- a/netwerk/cookie/moz.build
+++ b/netwerk/cookie/moz.build
@@ -18,16 +18,17 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'necko_cookie'
 
 if CONFIG['NECKO_COOKIES']:
     EXPORTS.mozilla.net = [
         'CookieServiceChild.h',
         'CookieServiceParent.h',
+        'nsCookieKey.h',
     ]
     UNIFIED_SOURCES += [
         'CookieServiceChild.cpp',
         'CookieServiceParent.cpp',
         'nsCookie.cpp',
     ]
     # nsCookieService.cpp can't be unified because of symbol conflicts
     SOURCES += [
--- a/netwerk/cookie/nsCookie.h
+++ b/netwerk/cookie/nsCookie.h
@@ -141,9 +141,33 @@ class nsCookie : public nsICookie2
     int64_t      mLastAccessed;
     int64_t      mCreationTime;
     bool mIsSession;
     bool mIsSecure;
     bool mIsHttpOnly;
     mozilla::OriginAttributes mOriginAttributes;
 };
 
+// Comparator class for sorting cookies before sending to a server.
+class CompareCookiesForSending
+{
+public:
+  bool Equals(const nsCookie* aCookie1, const nsCookie* aCookie2) const
+  {
+    return aCookie1->CreationTime() == aCookie2->CreationTime() &&
+           aCookie2->Path().Length() == aCookie1->Path().Length();
+  }
+
+  bool LessThan(const nsCookie* aCookie1, const nsCookie* aCookie2) const
+  {
+    // compare by cookie path length in accordance with RFC2109
+    int32_t result = aCookie2->Path().Length() - aCookie1->Path().Length();
+    if (result != 0)
+      return result < 0;
+
+    // when path lengths match, older cookies should be listed first.  this is
+    // required for backwards compatibility since some websites erroneously
+    // depend on receiving cookies in the order in which they were sent to the
+    // browser!  see bug 236772.
+    return aCookie1->CreationTime() < aCookie2->CreationTime();
+  }
+};
 #endif // nsCookie_h__
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1040,17 +1040,17 @@ nsCookieService::TryInitDB(bool aRecreat
           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
 
           if (!hasResult)
             break;
 
           int64_t id = select->AsInt64(SCHEMA2_IDX_ID);
           select->GetUTF8String(SCHEMA2_IDX_HOST, host);
 
-          rv = GetBaseDomainFromHost(host, baseDomain);
+          rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
 
           mozStorageStatementScoper scoper(update);
 
           rv = update->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
                                             baseDomain);
           NS_ASSERT_SUCCESS(rv);
           rv = update->BindInt64ByName(NS_LITERAL_CSTRING("id"),
@@ -2102,17 +2102,17 @@ nsCookieService::SetCookieStringInternal
 
   // get the base domain for the host URI.
   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
   // file:// URI's (i.e. with an empty host) are allowed, but any other
   // scheme must have a non-empty host. A trailing dot in the host
   // is acceptable.
   bool requireHostMatch;
   nsAutoCString baseDomain;
-  nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
+  nsresult rv = GetBaseDomain(mTLDService, aHostURI, baseDomain, requireHostMatch);
   if (NS_FAILED(rv)) {
     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
                       "couldn't get base domain from URI");
     return;
   }
 
   nsCookieKey key(baseDomain, aOriginAttrs);
 
@@ -2515,17 +2515,17 @@ nsCookieService::AddNative(const nsACStr
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // get the base domain for the host URI.
   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   int64_t currentTimeInUsec = PR_Now();
   nsCookieKey key = nsCookieKey(baseDomain, *aOriginAttributes);
 
   RefPtr<nsCookie> cookie =
     nsCookie::Create(aName, aValue, host, aPath,
                      aExpiry,
@@ -2558,17 +2558,17 @@ nsCookieService::Remove(const nsACString
   mDBState = (aAttrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsListIter matchIter;
   RefPtr<nsCookie> cookie;
   if (FindCookie(nsCookieKey(baseDomain, aAttrs),
                  host,
                  PromiseFlatCString(aName),
                  PromiseFlatCString(aPath),
@@ -3118,17 +3118,17 @@ nsCookieService::ImportCookies(nsIFile *
     // check for bad legacy cookies (domain not starting with a dot, or containing a port),
     // and discard
     if ((isDomain && !host.IsEmpty() && host.First() != '.') ||
         host.Contains(':')) {
       continue;
     }
 
     // compute the baseDomain from the host
-    rv = GetBaseDomainFromHost(host, baseDomain);
+    rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
     if (NS_FAILED(rv))
       continue;
 
     // pre-existing cookies have inIsolatedMozBrowser=false set by default
     // constructor of OriginAttributes().
     nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
 
     // Create a new nsCookie and assign the data. We don't know the cookie
@@ -3186,52 +3186,31 @@ nsCookieService::ImportCookies(nsIFile *
 /******************************************************************************
  * nsCookieService impl:
  * private GetCookie/SetCookie helpers
  ******************************************************************************/
 
 // helper function for GetCookieList
 static inline bool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
 
-// Comparator class for sorting cookies before sending to a server.
-class CompareCookiesForSending
+bool
+nsCookieService::DomainMatches(nsCookie* aCookie,
+                               const nsACString& aHost)
 {
-public:
-  bool Equals(const nsCookie* aCookie1, const nsCookie* aCookie2) const
-  {
-    return aCookie1->CreationTime() == aCookie2->CreationTime() &&
-           aCookie2->Path().Length() == aCookie1->Path().Length();
-  }
-
-  bool LessThan(const nsCookie* aCookie1, const nsCookie* aCookie2) const
-  {
-    // compare by cookie path length in accordance with RFC2109
-    int32_t result = aCookie2->Path().Length() - aCookie1->Path().Length();
-    if (result != 0)
-      return result < 0;
-
-    // when path lengths match, older cookies should be listed first.  this is
-    // required for backwards compatibility since some websites erroneously
-    // depend on receiving cookies in the order in which they were sent to the
-    // browser!  see bug 236772.
-    return aCookie1->CreationTime() < aCookie2->CreationTime();
-  }
-};
-
-static bool
-DomainMatches(nsCookie* aCookie, const nsACString& aHost) {
   // first, check for an exact host or domain cookie match, e.g. "google.com"
   // or ".google.com"; second a subdomain match, e.g.
   // host = "mail.google.com", cookie domain = ".google.com".
   return aCookie->RawHost() == aHost ||
       (aCookie->IsDomain() && StringEndsWith(aHost, aCookie->Host()));
 }
 
-static bool
-PathMatches(nsCookie* aCookie, const nsACString& aPath) {
+bool
+nsCookieService::PathMatches(nsCookie* aCookie,
+                             const nsACString& aPath)
+{
   // calculate cookie path length, excluding trailing '/'
   uint32_t cookiePathLen = aCookie->Path().Length();
   if (cookiePathLen > 0 && aCookie->Path().Last() == '/')
     --cookiePathLen;
 
   // if the given path is shorter than the cookie path, it doesn't match
   // if the given path doesn't start with the cookie path, it doesn't match.
   if (!StringBeginsWith(aPath, Substring(aCookie->Path(), 0, cookiePathLen)))
@@ -3253,21 +3232,21 @@ PathMatches(nsCookie* aCookie, const nsA
   }
 
   // either the paths match exactly, or the cookie path is a prefix of
   // the given path.
   return true;
 }
 
 void
-nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
-                                         bool aIsForeign,
-                                         bool aHttpBound,
-                                         const OriginAttributes& aOriginAttrs,
-                                         nsCString &aCookieString)
+nsCookieService::GetCookiesForURI(nsIURI *aHostURI,
+                                  bool aIsForeign,
+                                  bool aHttpBound,
+                                  const OriginAttributes& aOriginAttrs,
+                                  nsTArray<nsCookie*>& aCookieList)
 {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return;
   }
 
@@ -3276,17 +3255,17 @@ nsCookieService::GetCookieStringInternal
 
   // get the base domain, host, and path from the URI.
   // e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
   // file:// URI's (i.e. with an empty host) are allowed, but any other
   // scheme must have a non-empty host. A trailing dot in the host
   // is acceptable.
   bool requireHostMatch;
   nsAutoCString baseDomain, hostFromURI, pathFromURI;
-  nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
+  nsresult rv = GetBaseDomain(mTLDService, aHostURI, baseDomain, requireHostMatch);
   if (NS_SUCCEEDED(rv))
     rv = aHostURI->GetAsciiHost(hostFromURI);
   if (NS_SUCCEEDED(rv))
     rv = aHostURI->GetPathQueryRef(pathFromURI);
   if (NS_FAILED(rv)) {
     COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nullptr, "invalid host/path from URI");
     return;
   }
@@ -3312,17 +3291,16 @@ nsCookieService::GetCookieStringInternal
   // if it isn't, then we can't send a secure cookie over the connection.
   // if SchemeIs fails, assume an insecure connection, to be on the safe side
   bool isSecure;
   if (NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) {
     isSecure = false;
   }
 
   nsCookie *cookie;
-  AutoTArray<nsCookie*, 8> foundCookieList;
   int64_t currentTimeInUsec = PR_Now();
   int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
   bool stale = false;
 
   nsCookieKey key(baseDomain, aOriginAttrs);
   EnsureReadDomain(key);
 
   // perform the hash lookup
@@ -3353,39 +3331,39 @@ nsCookieService::GetCookieStringInternal
       continue;
 
     // check if the cookie has expired
     if (cookie->Expiry() <= currentTime) {
       continue;
     }
 
     // all checks passed - add to list and check if lastAccessed stamp needs updating
-    foundCookieList.AppendElement(cookie);
+    aCookieList.AppendElement(cookie);
     if (cookie->IsStale()) {
       stale = true;
     }
   }
 
-  int32_t count = foundCookieList.Length();
+  int32_t count = aCookieList.Length();
   if (count == 0)
     return;
 
   // update lastAccessed timestamps. we only do this if the timestamp is stale
   // by a certain amount, to avoid thrashing the db during pageload.
   if (stale) {
     // Create an array of parameters to bind to our update statement. Batching
     // is OK here since we're updating cookies with no interleaved operations.
     nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
     mozIStorageAsyncStatement* stmt = mDBState->stmtUpdate;
     if (mDBState->dbConn) {
       stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
     }
 
     for (int32_t i = 0; i < count; ++i) {
-      cookie = foundCookieList.ElementAt(i);
+      cookie = aCookieList.ElementAt(i);
 
       if (cookie->IsStale()) {
         UpdateCookieInList(cookie, currentTimeInUsec, paramsArray);
       }
     }
     // Update the database now if necessary.
     if (paramsArray) {
       uint32_t length;
@@ -3399,19 +3377,31 @@ nsCookieService::GetCookieStringInternal
         NS_ASSERT_SUCCESS(rv);
       }
     }
   }
 
   // return cookies in order of path length; longest to shortest.
   // this is required per RFC2109.  if cookies match in length,
   // then sort by creation time (see bug 236772).
-  foundCookieList.Sort(CompareCookiesForSending());
-
-  for (int32_t i = 0; i < count; ++i) {
+  aCookieList.Sort(CompareCookiesForSending());
+}
+
+void
+nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
+                                         bool aIsForeign,
+                                         bool aHttpBound,
+                                         const OriginAttributes& aOriginAttrs,
+                                         nsCString &aCookieString)
+{
+  AutoTArray<nsCookie*, 8> foundCookieList;
+  GetCookiesForURI(aHostURI, aIsForeign, aHttpBound, aOriginAttrs, foundCookieList);
+
+  nsCookie* cookie;
+  for (uint32_t i = 0; i < foundCookieList.Length(); ++i) {
     cookie = foundCookieList.ElementAt(i);
 
     // check if we have anything to write
     if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
       // if we've already added a cookie to the return list, append a "; " so
       // that subsequent cookies are delimited in the final list.
       if (!aCookieString.IsEmpty()) {
         aCookieString.AppendLiteral("; ");
@@ -4027,23 +4017,24 @@ nsCookieService::ParseAttributes(nsDepen
 
 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
 // dot may be present. If aHostURI is an IP address, an alias such as
 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
 // be the exact host, and aRequireHostMatch will be true to indicate that
 // substring matches should not be performed.
 nsresult
-nsCookieService::GetBaseDomain(nsIURI    *aHostURI,
+nsCookieService::GetBaseDomain(nsIEffectiveTLDService *aTLDService,
+                               nsIURI    *aHostURI,
                                nsCString &aBaseDomain,
                                bool      &aRequireHostMatch)
 {
   // get the base domain. this will fail if the host contains a leading dot,
   // more than one trailing dot, or is otherwise malformed.
-  nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
+  nsresult rv = aTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
   aRequireHostMatch = rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
                       rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
   if (aRequireHostMatch) {
     // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
     // such as 'co.uk', or the empty string. use the host as a key in such
     // cases.
     rv = aHostURI->GetAsciiHost(aBaseDomain);
   }
@@ -4060,36 +4051,37 @@ nsCookieService::GetBaseDomain(nsIURI   
     if (!isFileURI)
       return NS_ERROR_INVALID_ARG;
   }
 
   return NS_OK;
 }
 
 // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
-// "bbc.co.uk". This is done differently than GetBaseDomain(): it is assumed
+// "bbc.co.uk". This is done differently than GetBaseDomain(mTLDService, ): it is assumed
 // that aHost is already normalized, and it may contain a leading dot
 // (indicating that it represents a domain). A trailing dot may be present.
 // If aHost is an IP address, an alias such as 'localhost', an eTLD such as
 // 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
 // leading dot will be treated as an error.
 nsresult
-nsCookieService::GetBaseDomainFromHost(const nsACString &aHost,
+nsCookieService::GetBaseDomainFromHost(nsIEffectiveTLDService *aTLDService,
+                                       const nsACString &aHost,
                                        nsCString        &aBaseDomain)
 {
   // aHost must not be the string '.'.
   if (aHost.Length() == 1 && aHost.Last() == '.')
     return NS_ERROR_INVALID_ARG;
 
   // aHost may contain a leading dot; if so, strip it now.
   bool domain = !aHost.IsEmpty() && aHost.First() == '.';
 
   // get the base domain. this will fail if the host contains a leading dot,
   // more than one trailing dot, or is otherwise malformed.
-  nsresult rv = mTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0, aBaseDomain);
+  nsresult rv = aTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0, aBaseDomain);
   if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
       rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
     // aHost is either an IP address, an alias such as 'localhost', an eTLD
     // such as 'co.uk', or the empty string. use the host as a key in such
     // cases; however, we reject any such hosts with a leading dot, since it
     // doesn't make sense for them to be domain cookies.
     if (domain)
       return NS_ERROR_INVALID_ARG;
@@ -4280,17 +4272,17 @@ nsCookieService::CheckDomain(nsCookieAtt
   }
 
   // no domain specified, use hostFromURI
   aCookieAttributes.host = hostFromURI;
   return true;
 }
 
 nsCString
-GetPathFromURI(nsIURI* aHostURI)
+nsCookieService::GetPathFromURI(nsIURI* aHostURI)
 {
   // strip down everything after the last slash to get the path,
   // ignoring slashes in the query string part.
   // if we can QI to nsIURL, that'll take care of the query string portion.
   // otherwise, it's not an nsIURL and can't have a query string, so just find the last slash.
   nsAutoCString path;
   nsCOMPtr<nsIURL> hostURL = do_QueryInterface(aHostURI);
   if (hostURL) {
@@ -4652,17 +4644,17 @@ nsCookieService::CookieExistsNative(nsIC
   nsresult rv = aCookie->GetHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aCookie->GetName(name);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aCookie->GetPath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsListIter iter;
   *aFoundCookie = FindCookie(nsCookieKey(baseDomain, *aOriginAttributes),
                              host, name, path, iter);
   return NS_OK;
 }
 
@@ -4674,17 +4666,17 @@ nsCookieService::FindStaleCookie(nsCooki
                                  nsIURI* aSource,
                                  const mozilla::Maybe<bool> &aIsSecure,
                                  nsListIter &aIter)
 {
   aIter.entry = nullptr;
   bool requireHostMatch = true;
   nsAutoCString baseDomain, sourceHost, sourcePath;
   if (aSource) {
-    GetBaseDomain(aSource, baseDomain, requireHostMatch);
+    GetBaseDomain(mTLDService, aSource, baseDomain, requireHostMatch);
     aSource->GetAsciiHost(sourceHost);
     sourcePath = GetPathFromURI(aSource);
   }
 
   const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
 
   int64_t oldestNonMatchingCookieTime = 0;
   nsListIter oldestNonMatchingCookie;
@@ -4787,17 +4779,17 @@ nsCookieService::CountCookiesFromHost(co
   }
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
   EnsureReadDomain(key);
 
   // Return a count of all cookies, including expired.
   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
   *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
@@ -4821,17 +4813,17 @@ nsCookieService::GetCookiesFromHost(cons
   }
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   OriginAttributes attrs;
   rv = InitializeOriginAttributes(&attrs,
                                   aOriginAttributes,
                                   aCx,
                                   aArgc,
                                   u"nsICookieManager2.getCookiesFromHost()",
@@ -4867,17 +4859,17 @@ nsCookieService::GetCookiesWithOriginAtt
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return GetCookiesWithOriginAttributes(pattern, baseDomain, aEnumerator);
 }
 
 nsresult
 nsCookieService::GetCookiesWithOriginAttributes(
     const mozilla::OriginAttributesPattern& aPattern,
@@ -4926,17 +4918,17 @@ nsCookieService::RemoveCookiesWithOrigin
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
-  rv = GetBaseDomainFromHost(host, baseDomain);
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return RemoveCookiesWithOriginAttributes(pattern, baseDomain);
 }
 
 nsresult
 nsCookieService::RemoveCookiesWithOriginAttributes(
     const mozilla::OriginAttributesPattern& aPattern,
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -8,16 +8,17 @@
 
 #include "nsICookieService.h"
 #include "nsICookieManager.h"
 #include "nsICookieManager2.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 
 #include "nsCookie.h"
+#include "nsCookieKey.h"
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "nsHashKeys.h"
 #include "nsIMemoryReporter.h"
 #include "nsTHashtable.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageAsyncStatement.h"
 #include "mozIStoragePendingStatement.h"
@@ -26,20 +27,18 @@
 #include "mozIStorageCompletionCallback.h"
 #include "mozIStorageStatementCallback.h"
 #include "mozIStorageFunction.h"
 #include "nsIVariant.h"
 #include "nsIFile.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Maybe.h"
-#include "nsCookieKey.h"
 
 using mozilla::OriginAttributes;
-using mozilla::nsCookieKey;
 
 class nsICookiePermission;
 class nsIEffectiveTLDService;
 class nsIIDNService;
 class nsIPrefBranch;
 class nsIObserverService;
 class nsIURI;
 class nsIChannel;
@@ -48,20 +47,22 @@ class mozIStorageService;
 class mozIThirdPartyUtil;
 class ReadCookieDBListener;
 
 struct nsCookieAttributes;
 struct nsListIter;
 
 namespace mozilla {
 namespace net {
+class nsCookieKey;
 class CookieServiceParent;
 } // namespace net
 } // namespace mozilla
 
+using mozilla::net::nsCookieKey;
 // Inherit from nsCookieKey so this can be stored in nsTHashTable
 // TODO: why aren't we using nsClassHashTable<nsCookieKey, ArrayType>?
 class nsCookieEntry : public nsCookieKey
 {
   public:
     // Hash methods
     typedef nsTArray< RefPtr<nsCookie> > ArrayType;
     typedef ArrayType::index_type IndexType;
@@ -205,16 +206,22 @@ class nsCookieService final : public nsI
 
   /**
    * Start watching the observer service for messages indicating that an app has
    * been uninstalled.  When an app is uninstalled, we get the cookie service
    * (thus instantiating it, if necessary) and clear all the cookies for that
    * app.
    */
   static void AppClearDataObserverInit();
+  static nsCString GetPathFromURI(nsIURI *aHostURI);
+  static nsresult GetBaseDomain(nsIEffectiveTLDService *aTLDService, nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
+  static nsresult GetBaseDomainFromHost(nsIEffectiveTLDService *aTLDService, const nsACString &aHost, nsCString &aBaseDomain);
+  static bool DomainMatches(nsCookie* aCookie, const nsACString& aHost);
+  static bool PathMatches(nsCookie* aCookie, const nsACString& aPath);
+  void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList);
 
   protected:
     virtual ~nsCookieService();
 
     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
     void                          InitDBStates();
     OpenDBResult                  TryInitDB(bool aDeleteExistingDB);
     nsresult                      CreateTableWorker(const char* aName);
@@ -230,18 +237,16 @@ class nsCookieService final : public nsI
     void                          RebuildCorruptDB(DBState* aDBState);
     OpenDBResult                  Read();
     template<class T> nsCookie*   GetCookieFromRow(T &aRow, const OriginAttributes& aOriginAttributes);
     void                          AsyncReadComplete();
     void                          CancelAsyncRead(bool aPurgeReadSet);
     void                          EnsureReadDomain(const nsCookieKey &aKey);
     void                          EnsureReadComplete();
     nsresult                      NormalizeHost(nsCString &aHost);
-    nsresult                      GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
-    nsresult                      GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
     nsresult                      GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
     void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsCString &aCookie);
     nsresult                      SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
     void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const OriginAttributes &aOriginAttrs, nsIChannel* aChannel);
     bool                          SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel);
     void                          AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp);
     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr);
     void                          AddCookieToList(const nsCookieKey& aKey, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true);
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -194,16 +194,29 @@ struct HttpChannelDiverterArgs
 };
 
 union ChannelDiverterArgs
 {
   HttpChannelDiverterArgs;
   PFTPChannel;
 };
 
+struct CookieStruct
+{
+  nsCString name;
+  nsCString value;
+  nsCString host;
+  nsCString path;
+  int64_t   expiry;
+  int64_t   lastAccessed;
+  int64_t   creationTime;
+  bool      isSession;
+  bool      isSecure;
+};
+
 //-----------------------------------------------------------------------------
 // RTSP IPDL structs
 //-----------------------------------------------------------------------------
 
 struct RtspChannelConnectArgs
 {
   URIParams uri;
   uint32_t channelId;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 
 #include "AltDataOutputStreamChild.h"
+#include "CookieServiceChild.h"
 #include "HttpBackgroundChannelChild.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsPrimitives.h"
 #include "nsChannelClassifier.h"
 #include "nsContentPolicyUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsStringStream.h"
 #include "nsHttpChannel.h"
@@ -192,16 +193,22 @@ HttpChannelChild::HttpChannelChild()
 #if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
   static bool sSecurityPrefChecked = false;
   if (!sSecurityPrefChecked) {
     Preferences::AddBoolVarCache(&gIPCSecurityDisabled,
                                  "network.disable.ipc.security");
     sSecurityPrefChecked = true;
   }
 #endif
+
+  // Ensure that the cookie service is initialized before the first
+  // IPC HTTP channel is created.
+  // We require that the parent cookie service actor exists while
+  // processing HTTP responses.
+  CookieServiceChild::GetSingleton();
 }
 
 HttpChannelChild::~HttpChannelChild()
 {
   LOG(("Destroying HttpChannelChild @%p\n", this));
 
   ReleaseMainThreadOnlyReferences();
 }
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -118,16 +118,17 @@ EXTRA_JS_MODULES += [
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/netwerk/base',
+    '/netwerk/cookie',
 ]
 
 EXTRA_COMPONENTS += [
     'UAOverridesBootstrapper.js',
     'UAOverridesBootstrapper.manifest',
     'WellKnownOpportunisticUtils.js',
     'WellKnownOpportunisticUtils.manifest',
 ]