Bug 595305 - Factor cookie third-party URI code into separate API. r=bent, a=betaN+
authorDan Witte <dwitte@mozilla.com>
Tue, 19 Oct 2010 09:37:03 -0700
changeset 56094 56815e37d436bad9a337bd9726c5d6754cfeea69
parent 56093 9926aff395c1ba654059c0f5281a87eee9b8009a
child 56095 d35efa6966ac87245fe35442460a19f5f786e1a7
push idunknown
push userunknown
push dateunknown
reviewersbent, betaN
bugs595305
milestone2.0b8pre
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 595305 - Factor cookie third-party URI code into separate API. r=bent, a=betaN+
content/base/public/nsContentCID.h
content/base/src/Makefile.in
content/base/src/ThirdPartyUtil.cpp
content/base/src/ThirdPartyUtil.h
layout/build/nsLayoutModule.cpp
netwerk/base/public/Makefile.in
netwerk/base/public/mozIThirdPartyUtil.idl
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceChild.h
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/CookieServiceParent.h
netwerk/cookie/PCookieService.ipdl
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
netwerk/cookie/nsICookiePermission.idl
--- a/content/base/public/nsContentCID.h
+++ b/content/base/public/nsContentCID.h
@@ -323,9 +323,13 @@
   { 0x9a, 0x8a, 0x86, 0xaf, 0xdb, 0x9f, 0xbb, 0xb6 } }
 #define NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID \
   "@mozilla.org/childprocessmessagemanager;1"
   
 // {f96f5ec9-755b-447e-b1f3-717d1a84bb41}
 #define NS_PLUGINDOCUMENT_CID \
 { 0xf96f5ec9, 0x755b, 0x447e, { 0xb1, 0xf3, 0x71, 0x7d, 0x1a, 0x84, 0xbb, 0x41 } }
 
+// {08c6cc8b-cfb0-421d-b1f7-683ff2989681}
+#define THIRDPARTYUTIL_CID \
+ {0x08c6cc8b, 0xcfb0, 0x421d, {0xb1, 0xf7, 0x68, 0x3f, 0xf2, 0x98, 0x96, 0x81}}
+
 #endif /* nsContentCID_h__ */
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -143,16 +143,17 @@ CPPSRCS		= \
 		nsXHTMLContentSerializer.cpp \
 		nsXMLContentSerializer.cpp \
 		nsXMLHttpRequest.cpp \
 		nsXMLNameSpaceMap.cpp \
 		Link.cpp \
 		nsFileDataProtocolHandler.cpp \
 		nsFrameMessageManager.cpp \
 		nsInProcessTabChildGlobal.cpp \
+		ThirdPartyUtil.cpp \
 		$(NULL)
 
 GQI_SRCS = contentbase.gqi
 
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
new file mode 100644
--- /dev/null
+++ b/content/base/src/ThirdPartyUtil.cpp
@@ -0,0 +1,312 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is third party utility code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Witte (dwitte@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ThirdPartyUtil.h"
+#include "nsNetUtil.h"
+#include "nsIServiceManager.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIDOMWindow.h"
+#include "nsILoadContext.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsThreadUtils.h"
+
+NS_IMPL_ISUPPORTS1(ThirdPartyUtil, mozIThirdPartyUtil)
+
+nsresult
+ThirdPartyUtil::Init()
+{
+  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
+
+  nsresult rv;
+  mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
+  return rv;
+}
+
+// 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 (and will be stripped). 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. The result of this function should only
+// be used in exact string comparisons, since substring comparisons will not
+// be valid for the special cases elided above.
+nsresult
+ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI,
+                              nsCString& aBaseDomain)
+{
+  // 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);
+  if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
+      rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+    // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
+    // such as 'co.uk', or the empty string. Uses the normalized host in such
+    // cases.
+    rv = aHostURI->GetAsciiHost(aBaseDomain);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // aHostURI (and thus aBaseDomain) may contain a trailing dot; if so, trim it.
+  if (!aBaseDomain.IsEmpty() && aBaseDomain.Last() == '.')
+    aBaseDomain.Truncate(aBaseDomain.Length() - 1);
+
+  // Reject any URIs without a host that aren't file:// URIs. This makes it the
+  // only way we can get a base domain consisting of the empty string, which
+  // means we can safely perform foreign tests on such URIs where "not foreign"
+  // means "the involved URIs are all file://".
+  if (aBaseDomain.IsEmpty()) {
+    PRBool isFileURI = PR_FALSE;
+    aHostURI->SchemeIs("file", &isFileURI);
+    NS_ENSURE_TRUE(isFileURI, NS_ERROR_INVALID_ARG);
+  }
+
+  return NS_OK;
+}
+
+// Determine if aFirstDomain is a different base domain to aSecondURI; or, if
+// the concept of base domain does not apply, determine if the two hosts are not
+// string-identical.
+nsresult
+ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
+                                     nsIURI* aSecondURI,
+                                     PRBool* aResult)
+{
+  NS_ASSERTION(aSecondURI, "null URI!");
+
+  // Get the base domain for aSecondURI.
+  nsCString secondDomain;
+  nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
+  if (NS_FAILED(rv))
+    return rv;
+
+  // Check strict equality.
+  *aResult = aFirstDomain != secondDomain;
+  return NS_OK;
+}
+
+// Get the URI associated with a window.
+already_AddRefed<nsIURI>
+ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin)
+{
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin);
+  NS_ENSURE_TRUE(scriptObjPrin, NULL);
+
+  nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
+  NS_ENSURE_TRUE(prin, NULL);
+
+  nsCOMPtr<nsIURI> result;
+  prin->GetURI(getter_AddRefs(result));
+  return result.forget();
+}
+
+// Determine if aFirstURI is third party with respect to aSecondURI. See docs
+// for mozIThirdPartyUtil.
+NS_IMETHODIMP
+ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI,
+                                nsIURI* aSecondURI,
+                                PRBool* aResult)
+{
+  NS_ENSURE_ARG(aFirstURI);
+  NS_ENSURE_ARG(aSecondURI);
+  NS_ASSERTION(aResult, "null outparam pointer");
+
+  nsCString firstHost;
+  nsresult rv = GetBaseDomain(aFirstURI, firstHost);
+  if (NS_FAILED(rv))
+    return rv;
+
+  return IsThirdPartyInternal(firstHost, aSecondURI, aResult);
+}
+
+// Determine if any URI of the window hierarchy of aWindow is foreign with
+// respect to aSecondURI. See docs for mozIThirdPartyUtil.
+NS_IMETHODIMP
+ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow,
+                                   nsIURI* aURI,
+                                   PRBool* aResult)
+{
+  NS_ENSURE_ARG(aWindow);
+  NS_ASSERTION(aResult, "null outparam pointer");
+
+  PRBool result;
+
+  // Get the URI of the window, and its base domain.
+  nsCOMPtr<nsIURI> currentURI = GetURIFromWindow(aWindow);
+  NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG);
+
+  nsCString bottomDomain;
+  nsresult rv = GetBaseDomain(currentURI, bottomDomain);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (aURI) {
+    // Determine whether aURI is foreign with respect to currentURI.
+    rv = IsThirdPartyInternal(bottomDomain, aURI, &result);
+    if (NS_FAILED(rv))
+      return rv;
+
+    if (result) {
+      *aResult = true;
+      return NS_OK;
+    }
+  }
+
+  nsCOMPtr<nsIDOMWindow> current = aWindow, parent;
+  nsCOMPtr<nsIURI> parentURI;
+  do {
+    rv = current->GetParent(getter_AddRefs(parent));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (SameCOMIdentity(parent, current)) {
+      // We're at the topmost content window. We already know the answer.
+      *aResult = false;
+      return NS_OK;
+    }
+
+    parentURI = GetURIFromWindow(parent);
+    NS_ENSURE_TRUE(parentURI, NS_ERROR_INVALID_ARG);
+
+    rv = IsThirdPartyInternal(bottomDomain, parentURI, &result);
+    if (NS_FAILED(rv))
+      return rv;
+
+    if (result) {
+      *aResult = true;
+      return NS_OK;
+    }
+
+    current = parent;
+    currentURI = parentURI;
+  } while (1);
+
+  NS_NOTREACHED("should've returned");
+  return NS_ERROR_UNEXPECTED;
+}
+
+// Determine if the URI associated with aChannel or any URI of the window
+// hierarchy associated with the channel is foreign with respect to aSecondURI.
+// See docs for mozIThirdPartyUtil.
+NS_IMETHODIMP 
+ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
+                                    nsIURI* aURI,
+                                    PRBool* aResult)
+{
+  NS_ENSURE_ARG(aChannel);
+  NS_ASSERTION(aResult, "null outparam pointer");
+
+  nsresult rv;
+  PRBool doForce = false;
+  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+    do_QueryInterface(aChannel);
+  if (httpChannelInternal) {
+    rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // If aURI was not supplied, and we're forcing, then we're by definition
+    // not foreign. If aURI was supplied, we still want to check whether it's
+    // foreign with respect to the channel URI. (The forcing only applies to
+    // whatever window hierarchy exists above the channel.)
+    if (doForce && !aURI) {
+      *aResult = false;
+      return NS_OK;
+    }
+  }
+
+  // Obtain the URI from the channel, and its base domain.
+  nsCOMPtr<nsIURI> channelURI;
+  aChannel->GetURI(getter_AddRefs(channelURI));
+  NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
+
+  nsCString channelDomain;
+  rv = GetBaseDomain(channelURI, channelDomain);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (aURI) {
+    // Determine whether aURI is foreign with respect to channelURI.
+    PRBool result;
+    rv = IsThirdPartyInternal(channelDomain, aURI, &result);
+    if (NS_FAILED(rv))
+     return rv;
+
+    // If it's foreign, or we're forcing, we're done.
+    if (result || doForce) {
+      *aResult = result;
+      return NS_OK;
+    }
+  }
+
+  // Find the associated window and its parent window.
+  nsCOMPtr<nsILoadContext> ctx;
+  NS_QueryNotificationCallbacks(aChannel, ctx);
+  if (!ctx) return NS_ERROR_INVALID_ARG;
+
+  // If there is no window, the consumer kicking off the load didn't provide one
+  // to the channel. This is limited to loads of certain types of resources. If
+  // those loads require cookies, the forceAllowThirdPartyCookie property should
+  // be set on the channel.
+  nsCOMPtr<nsIDOMWindow> ourWin, parentWin;
+  ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
+  if (!ourWin) return NS_ERROR_INVALID_ARG;
+
+  ourWin->GetParent(getter_AddRefs(parentWin));
+  NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG);
+
+  if (SameCOMIdentity(ourWin, parentWin)) {
+    // Check whether this is the document channel for this window (representing
+    // a load of a new page). This covers the case of a freshly kicked-off load
+    // (e.g. the user typing something in the location bar, or clicking on a
+    // bookmark), where the window's URI hasn't yet been set, and will be bogus.
+    // This is a bit of a nasty hack, but we will hopefully flag these channels
+    // better later.
+    nsLoadFlags flags;
+    rv = aChannel->GetLoadFlags(&flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
+      // We only need to compare aURI to the channel URI -- the window's will be
+      // bogus. We already know the answer.
+      *aResult = false;
+      return NS_OK;
+    }
+  }
+
+  // Check the window hierarchy. This covers most cases for an ordinary page
+  // load from the location bar.
+  return IsThirdPartyWindow(ourWin, channelURI, aResult);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/src/ThirdPartyUtil.h
@@ -0,0 +1,68 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is third party utility code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Witte (dwitte@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef ThirdPartyUtil_h__
+#define ThirdPartyUtil_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsIEffectiveTLDService.h"
+
+class nsIURI;
+class nsIChannel;
+class nsIDOMWindow;
+
+class ThirdPartyUtil : public mozIThirdPartyUtil
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZITHIRDPARTYUTIL
+
+  nsresult Init();
+
+private:
+  nsresult GetBaseDomain(nsIURI* aHostURI, nsCString& aBaseDomain);
+  nsresult IsThirdPartyInternal(const nsCString& aFirstDomain,
+    nsIURI* aSecondURI, PRBool* aResult);
+  static already_AddRefed<nsIURI> GetURIFromWindow(nsIDOMWindow* aWin);
+
+  nsCOMPtr<nsIEffectiveTLDService> mTLDService;
+};
+
+#endif
+
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -92,16 +92,17 @@
 #include "nsBox.h"
 #include "nsIFrameTraversal.h"
 #include "nsLayoutCID.h"
 #include "nsILanguageAtomService.h"
 #include "nsStyleSheetService.h"
 #include "nsXULPopupManager.h"
 #include "nsFocusManager.h"
 #include "nsIContentUtils.h"
+#include "ThirdPartyUtil.h"
 #include "mozilla/Services.h"
 
 #include "nsIEventListenerService.h"
 #include "nsIFrameMessageManager.h"
 
 // Transformiix stuff
 #include "nsXPathEvaluator.h"
 #include "txMozillaXSLTProcessor.h"
@@ -326,16 +327,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannel
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
                                          IndexedDatabaseManager::GetOrCreateInstance)
 #if defined(XP_UNIX)    || \
     defined(_WINDOWS)   || \
     defined(machintosh) || \
     defined(android)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAccelerometerSystem)
 #endif
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ThirdPartyUtil, Init)
 
 //-----------------------------------------------------------------------------
 
 // Per bug 209804, it is necessary to observe the "xpcom-shutdown" event and
 // perform shutdown of the layout modules at that time instead of waiting for
 // our module destructor to run.  If we do not do this, then we risk holding
 // references to objects in other component libraries that have already been
 // shutdown (and possibly unloaded if 60709 is ever fixed).
@@ -878,16 +880,17 @@ NS_DEFINE_NAMED_CID(NS_GLOBALMESSAGEMANA
 NS_DEFINE_NAMED_CID(NS_PARENTPROCESSMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_CHILDPROCESSMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NSCHANNELPOLICY_CID);
 NS_DEFINE_NAMED_CID(NS_SCRIPTSECURITYMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_PRINCIPAL_CID);
 NS_DEFINE_NAMED_CID(NS_SYSTEMPRINCIPAL_CID);
 NS_DEFINE_NAMED_CID(NS_NULLPRINCIPAL_CID);
 NS_DEFINE_NAMED_CID(NS_SECURITYNAMESET_CID);
+NS_DEFINE_NAMED_CID(THIRDPARTYUTIL_CID);
 
 #if defined(XP_UNIX)    || \
     defined(_WINDOWS)   || \
     defined(machintosh) || \
     defined(android)
 NS_DEFINE_NAMED_CID(NS_ACCELEROMETER_CID);
 #endif
 
@@ -1034,16 +1037,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_NULLPRINCIPAL_CID, false, NULL, nsNullPrincipalConstructor },
   { &kNS_SECURITYNAMESET_CID, false, NULL, nsSecurityNameSetConstructor },
 #if defined(XP_UNIX)    || \
     defined(_WINDOWS)   || \
     defined(machintosh) || \
     defined(android)
   { &kNS_ACCELEROMETER_CID, false, NULL, nsAccelerometerSystemConstructor },
 #endif
+  { &kTHIRDPARTYUTIL_CID, false, NULL, ThirdPartyUtilConstructor },
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
   { "@mozilla.org/layout/xul-boxobject-listbox;1", &kNS_LISTBOXOBJECT_CID },
@@ -1179,16 +1183,17 @@ static const mozilla::Module::ContractID
   { NS_NULLPRINCIPAL_CONTRACTID, &kNS_NULLPRINCIPAL_CID },
   { NS_SECURITYNAMESET_CONTRACTID, &kNS_SECURITYNAMESET_CID },
 #if defined(XP_UNIX)    || \
     defined(_WINDOWS)   || \
     defined(machintosh) || \
     defined(android)
   { NS_ACCELEROMETER_CONTRACTID, &kNS_ACCELEROMETER_CID },
 #endif
+  { THIRDPARTYUTIL_CONTRACTID, &kTHIRDPARTYUTIL_CID },
   { NULL }
 };
 
 static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
   XPCONNECT_CATEGORIES
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Image", NS_HTMLIMGELEMENT_CONTRACTID },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY, "Image", "HTMLImageElement" },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Option", NS_HTMLOPTIONELEMENT_CONTRACTID },
--- a/netwerk/base/public/Makefile.in
+++ b/netwerk/base/public/Makefile.in
@@ -136,16 +136,17 @@ XPIDLSRCS	= \
 		nsIChannelEventSink.idl \
 		nsINetUtil.idl \
 		nsIProxiedChannel.idl \
 		nsIRandomGenerator.idl \
 		nsIStrictTransportSecurityService.idl \
 		nsIURIWithPrincipal.idl \
 		nsIURIClassifier.idl \
 		nsIRedirectResultListener.idl \
+		mozIThirdPartyUtil.idl \
 		$(NULL)
 
 ifdef MOZ_TOOLKIT_SEARCH
 XPIDLSRCS	+= nsIBrowserSearchService.idl
 endif
 
 EXPORTS		= \
 		netCore.h \
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/mozIThirdPartyUtil.idl
@@ -0,0 +1,167 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is third party utility code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Witte (dwitte@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIDOMWindow;
+interface nsIChannel;
+
+/**
+ * Utility functions for determining whether a given URI, channel, or window
+ * hierarchy is third party with respect to a known URI.
+ */
+[scriptable, uuid(55385caa-1b94-4376-a34c-b47c51ef0837)]
+interface mozIThirdPartyUtil : nsISupports
+{
+  /**
+   * isThirdPartyURI
+   *
+   * Determine whether two URIs are third party with respect to each other.
+   * This is determined by computing the base domain for both URIs. If they can
+   * be determined, and the base domains match, the request is defined as first
+   * party. If it cannot be determined because one or both URIs do not have a
+   * base domain (for instance, in the case of IP addresses, host aliases such
+   * as 'localhost', or a file:// URI), an exact string comparison on host is
+   * performed.
+   *
+   * For example, the URI "http://mail.google.com/" is not third party with
+   * respect to "http://images.google.com/", but "http://mail.yahoo.com/" and
+   * "http://192.168.1.1/" are.
+   *
+   * @return true if aFirstURI is third party with respect to aSecondURI.
+   *
+   * @throws if either URI is null, has a malformed host, or has an empty host
+   *         and is not a file:// URI.
+   */
+  boolean isThirdPartyURI(in nsIURI aFirstURI, in nsIURI aSecondURI);
+
+  /**
+   * isThirdPartyWindow
+   *
+   * Determine whether the given window hierarchy is third party. This is done
+   * as follows:
+   *
+   * 1) Obtain the URI of the principal associated with 'aWindow'. Call this the
+   *    'bottom URI'.
+   * 2) If 'aURI' is provided, determine if it is third party with respect to
+   *    the bottom URI. If so, return.
+   * 3) Find the same-type parent window, if there is one, and its URI.
+   *    Determine whether it is third party with respect to the bottom URI. If
+   *    so, return.
+   *
+   * Therefore, each level in the window hierarchy is tested. (This means that
+   * nested iframes with different base domains, even though the bottommost and
+   * topmost URIs might be equal, will be considered third party.)
+   *
+   * @param aWindow
+   *        The bottommost window in the hierarchy.
+   * @param aURI
+   *        A URI to test against. If null, the URI of the principal
+   *        associated with 'aWindow' will be used.
+   *
+   * For example, if 'aURI' is "http://mail.google.com/", 'aWindow' has a URI
+   * of "http://google.com/", and its parent is the topmost content window with
+   * a URI of "http://mozilla.com", the result will be true.
+   *
+   * @return true if 'aURI' is third party with respect to any of the URIs
+   *         associated with aWindow and its same-type parents.
+   *
+   * @throws if aWindow is null; the same-type parent of any window in the
+   *         hierarchy cannot be determined; or the URI associated with any
+   *         window in the hierarchy is null, has a malformed host, or has an
+   *         empty host and is not a file:// URI.
+   *
+   * @see isThirdPartyURI
+   */
+  boolean isThirdPartyWindow(in nsIDOMWindow aWindow, [optional] in nsIURI aURI);
+
+  /**
+   * isThirdPartyChannel
+   *
+   * Determine whether the given channel and its content window hierarchy is
+   * third party. This is done as follows:
+   *
+   * 1) If 'aChannel' is an nsIHttpChannel and has the
+   *    'forceAllowThirdPartyCookie' property set, then:
+   *    a) If 'aURI' is null, return false.
+   *    b) Otherwise, find the URI of the channel, determine whether it is
+   *       foreign with respect to 'aURI', and return.
+   * 2) Find the URI of the channel and determine whether it is third party with
+   *    respect to the URI of the channel. If so, return.
+   * 3) Obtain the bottommost nsIDOMWindow, and its same-type parent if it
+   *    exists, from the channel's notification callbacks. Then:
+   *    a) If the parent is the same as the bottommost window, and the channel
+   *       has the LOAD_DOCUMENT_URI flag set, return false. This represents the
+   *       case where a toplevel load is occurring and the window's URI has not
+   *       yet been updated. (We have already checked that 'aURI' is not foreign
+   *       with respect to the channel URI.)
+   *    b) Otherwise, return the result of isThirdPartyWindow with arguments
+   *       of the channel's bottommost window and the channel URI, respectively.
+   *
+   * Therefore, both the channel's URI and each level in the window hierarchy
+   * associated with the channel is tested.
+   *
+   * @param aChannel
+   *        The channel associated with the load.
+   * @param aURI
+   *        A URI to test against. If null, the URI of the channel will be used.
+   *
+   * For example, if 'aURI' is "http://mail.google.com/", 'aChannel' has a URI
+   * of "http://google.com/", and its parent is the topmost content window with
+   * a URI of "http://mozilla.com", the result will be true.
+   *
+   * @return true if aURI is third party with respect to the channel URI or any
+   *         of the URIs associated with the same-type window hierarchy of the
+   *         channel.
+   *
+   * @throws if 'aChannel' is null; the channel has no notification callbacks or
+   *         an associated window; or isThirdPartyWindow throws.
+   *
+   * @see isThirdPartyWindow
+   */
+  boolean isThirdPartyChannel(in nsIChannel aChannel, [optional] in nsIURI aURI);
+};
+
+%{ C++
+/**
+ * The mozIThirdPartyUtil implementation is an XPCOM service registered
+ * under the ContractID:
+ */
+#define THIRDPARTYUTIL_CONTRACTID "@mozilla.org/thirdpartyutil;1"
+%}
+
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -34,114 +34,165 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "nsIURI.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch2.h"
 
 namespace mozilla {
 namespace net {
 
+// Behavior pref constants
+static const PRInt32 BEHAVIOR_ACCEPT = 0;
+static const PRInt32 BEHAVIOR_REJECTFOREIGN = 1;
+static const PRInt32 BEHAVIOR_REJECT = 2;
+
+// Pref string constants
+static const char kPrefCookieBehavior[] = "network.cookie.cookieBehavior";
+static const char kPrefThirdPartySession[] =
+  "network.cookie.thirdparty.sessionOnly";
+
 static CookieServiceChild *gCookieService;
 
 CookieServiceChild*
 CookieServiceChild::GetSingleton()
 {
   if (!gCookieService)
     gCookieService = new CookieServiceChild();
 
   NS_ADDREF(gCookieService);
   return gCookieService;
 }
 
-NS_IMPL_ISUPPORTS1(CookieServiceChild, nsICookieService)
+NS_IMPL_ISUPPORTS2(CookieServiceChild, nsICookieService, nsIObserver)
 
 CookieServiceChild::CookieServiceChild()
+  : mCookieBehavior(BEHAVIOR_ACCEPT)
+  , mThirdPartySession(false)
 {
   NS_ASSERTION(IsNeckoChild(), "not a child process");
 
   // This corresponds to Release() in DeallocPCookieService.
   NS_ADDREF_THIS();
 
   // Create a child PCookieService actor.
   NeckoChild::InitNeckoChild();
   gNeckoChild->SendPCookieServiceConstructor(this);
 
-  mPermissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
-  if (!mPermissionService)
-    NS_WARNING("couldn't get nsICookiePermission in child");
+  // Init our prefs and observer.
+  nsCOMPtr<nsIPrefBranch2> prefBranch =
+    do_GetService(NS_PREFSERVICE_CONTRACTID);
+  NS_WARN_IF_FALSE(prefBranch, "no prefservice");
+  if (prefBranch) {
+    prefBranch->AddObserver(kPrefCookieBehavior, this, PR_TRUE);
+    prefBranch->AddObserver(kPrefThirdPartySession, this, PR_TRUE);
+    PrefChanged(prefBranch);
+  }
 }
 
 CookieServiceChild::~CookieServiceChild()
 {
   gCookieService = nsnull;
 }
 
+void
+CookieServiceChild::PrefChanged(nsIPrefBranch *aPrefBranch)
+{
+  PRInt32 val;
+  if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieBehavior, &val)))
+    mCookieBehavior =
+      val >= BEHAVIOR_ACCEPT && val <= BEHAVIOR_REJECT ? val : BEHAVIOR_ACCEPT;
+
+  PRBool boolval;
+  if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefThirdPartySession, &boolval)))
+    mThirdPartySession = !!boolval;
+
+  if (!mThirdPartyUtil && RequireThirdPartyCheck()) {
+    mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+    NS_ASSERTION(mThirdPartyUtil, "require ThirdPartyUtil service");
+  }
+}
+
+bool
+CookieServiceChild::RequireThirdPartyCheck()
+{
+  return mCookieBehavior == BEHAVIOR_REJECTFOREIGN || mThirdPartySession;
+}
+
 nsresult
 CookieServiceChild::GetCookieStringInternal(nsIURI *aHostURI,
                                             nsIChannel *aChannel,
                                             char **aCookieString,
                                             bool aFromHttp)
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG_POINTER(aCookieString);
 
   *aCookieString = NULL;
 
-  // Determine the originating URI. Failure is acceptable.
-  nsCOMPtr<nsIURI> originatingURI;
-  if (!mPermissionService) {
-    NS_WARNING("nsICookiePermission unavailable! Cookie may be rejected");
-    mPermissionService->GetOriginatingURI(aChannel,
-                                          getter_AddRefs(originatingURI));
-  }
+  // Determine whether the request is foreign. Failure is acceptable.
+  PRBool isForeign = true;
+  if (RequireThirdPartyCheck())
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   // Synchronously call the parent.
   nsCAutoString result;
-  SendGetCookieString(IPC::URI(aHostURI), IPC::URI(originatingURI),
-                      aFromHttp, &result);
+  SendGetCookieString(IPC::URI(aHostURI), !!isForeign, aFromHttp, &result);
   if (!result.IsEmpty())
     *aCookieString = ToNewCString(result);
 
   return NS_OK;
 }
 
 nsresult
 CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI,
                                             nsIChannel *aChannel,
                                             const char *aCookieString,
                                             const char *aServerTime,
                                             bool aFromHttp)
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG_POINTER(aCookieString);
 
-  // Determine the originating URI. Failure is acceptable.
-  nsCOMPtr<nsIURI> originatingURI;
-  if (!mPermissionService) {
-    NS_WARNING("nsICookiePermission unavailable! Cookie may be rejected");
-    mPermissionService->GetOriginatingURI(aChannel,
-                                          getter_AddRefs(originatingURI));
-  }
+  // Determine whether the request is foreign. Failure is acceptable.
+  PRBool isForeign = true;
+  if (RequireThirdPartyCheck())
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsDependentCString cookieString(aCookieString);
   nsDependentCString serverTime;
   if (aServerTime)
     serverTime.Rebind(aServerTime);
 
   // Synchronously call the parent.
-  SendSetCookieString(IPC::URI(aHostURI), IPC::URI(originatingURI),
+  SendSetCookieString(IPC::URI(aHostURI), !!isForeign,
                       cookieString, serverTime, aFromHttp);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+CookieServiceChild::Observe(nsISupports     *aSubject,
+                            const char      *aTopic,
+                            const PRUnichar *aData)
+{
+  NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
+               "not a pref change topic!");
+
+  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
+  if (prefBranch)
+    PrefChanged(prefBranch);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 CookieServiceChild::GetCookieString(nsIURI *aHostURI,
                                     nsIChannel *aChannel,
                                     char **aCookieString)
 {
   return GetCookieStringInternal(aHostURI, aChannel, aCookieString, false);
 }
 
 NS_IMETHODIMP
--- a/netwerk/cookie/CookieServiceChild.h
+++ b/netwerk/cookie/CookieServiceChild.h
@@ -36,27 +36,31 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_net_CookieServiceChild_h__
 #define mozilla_net_CookieServiceChild_h__
 
 #include "mozilla/net/PCookieServiceChild.h"
 #include "nsICookieService.h"
-#include "nsICookiePermission.h"
+#include "nsIObserver.h"
+#include "nsIPrefBranch.h"
+#include "mozIThirdPartyUtil.h"
 
 namespace mozilla {
 namespace net {
 
 class CookieServiceChild : public PCookieServiceChild
                          , public nsICookieService
+                         , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICOOKIESERVICE
+  NS_DECL_NSIOBSERVER
 
   CookieServiceChild();
   virtual ~CookieServiceChild();
 
   static CookieServiceChild* GetSingleton();
 
 protected:
   void SerializeURIs(nsIURI *aHostURI,
@@ -72,16 +76,22 @@ protected:
                                    bool aFromHttp);
 
   nsresult SetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    const char *aCookieString,
                                    const char *aServerTime,
                                    bool aFromHttp);
 
-  nsCOMPtr<nsICookiePermission> mPermissionService;
+  void PrefChanged(nsIPrefBranch *aPrefBranch);
+
+  bool RequireThirdPartyCheck();
+
+  nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
+  PRUint8 mCookieBehavior;
+  bool mThirdPartySession;
 };
 
 }
 }
 
 #endif // mozilla_net_CookieServiceChild_h__
 
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -56,53 +56,51 @@ CookieServiceParent::CookieServiceParent
 }
 
 CookieServiceParent::~CookieServiceParent()
 {
 }
 
 bool
 CookieServiceParent::RecvGetCookieString(const IPC::URI& aHost,
-                                         const IPC::URI& aOriginating,
+                                         const bool& aIsForeign,
                                          const bool& aFromHttp,
                                          nsCString* aResult)
 {
   if (!mCookieService)
     return true;
 
-  // Deserialize URIs. Having a host URI is mandatory and should always be
+  // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI(aHost);
-  nsCOMPtr<nsIURI> originatingURI(aOriginating);
   if (!hostURI)
     return false;
 
-  mCookieService->GetCookieStringInternal(hostURI, originatingURI,
+  mCookieService->GetCookieStringInternal(hostURI, aIsForeign,
                                           aFromHttp, *aResult);
   return true;
 }
 
 bool
 CookieServiceParent::RecvSetCookieString(const IPC::URI& aHost,
-                                         const IPC::URI& aOriginating,
+                                         const bool& aIsForeign,
                                          const nsCString& aCookieString,
                                          const nsCString& aServerTime,
                                          const bool& aFromHttp)
 {
   if (!mCookieService)
     return true;
 
-  // Deserialize URIs. Having a host URI is mandatory and should always be
+  // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI(aHost);
-  nsCOMPtr<nsIURI> originatingURI(aOriginating);
   if (!hostURI)
     return false;
 
-  mCookieService->SetCookieStringInternal(hostURI, originatingURI,
+  mCookieService->SetCookieStringInternal(hostURI, aIsForeign,
                                           aCookieString, aServerTime,
                                           aFromHttp);
   return true;
 }
 
 }
 }
 
--- a/netwerk/cookie/CookieServiceParent.h
+++ b/netwerk/cookie/CookieServiceParent.h
@@ -50,22 +50,22 @@ namespace net {
 class CookieServiceParent : public PCookieServiceParent
 {
 public:
   CookieServiceParent();
   virtual ~CookieServiceParent();
 
 protected:
   virtual bool RecvGetCookieString(const IPC::URI& aHost,
-                                   const IPC::URI& aOriginating,
+                                   const bool& aIsForeign,
                                    const bool& aFromHttp,
                                    nsCString* aResult);
 
   virtual bool RecvSetCookieString(const IPC::URI& aHost,
-                                   const IPC::URI& aOriginating,
+                                   const bool& aIsForeign,
                                    const nsCString& aCookieString,
                                    const nsCString& aServerTime,
                                    const bool& aFromHttp);
 
   nsRefPtr<nsCookieService> mCookieService;
 };
 
 }
--- a/netwerk/cookie/PCookieService.ipdl
+++ b/netwerk/cookie/PCookieService.ipdl
@@ -68,70 +68,66 @@ parent:
 
   /*
    * Get the complete cookie string associated with the URI. This is a sync
    * call in order to avoid race conditions -- for instance, an HTTP response
    * on the parent and script access on the child.
    *
    * @param host
    *        Same as the 'aURI' argument to nsICookieService.getCookieString.
-   * @param originating
-   *        The originating URI associated with the request. This is used
-   *        to determine whether the request is first or third party, for
-   *        purposes of allowing access to cookies. This should be obtained
-   *        from nsICookiePermission.getOriginatingURI. This parameter may
-   *        be null; in this case, the request is assumed to be third party
-   *        and may be rejected depending on user preferences. In
-   *        nsICookieService.getCookieString, this argument is determined
-   *        from the aChannel argument.
+   * @param isForeign
+   *        True if the the request is third party, for purposes of allowing
+   *        access to cookies. This should be obtained from
+   *        mozIThirdPartyUtil.isThirdPartyChannel. Third party requests may be
+   *        rejected depending on user preferences; if those checks are
+   *        disabled, this parameter is ignored.
    * @param fromHttp
    *        Whether the result is for an HTTP request header. This should be
    *        true for nsICookieService.getCookieStringFromHttp calls, false
    *        otherwise.
    *
    * @see nsICookieService.getCookieString
    * @see nsICookieService.getCookieStringFromHttp
-   * @see nsICookiePermission.getOriginatingURI
+   * @see mozIThirdPartyUtil.isThirdPartyChannel
    *
    * @return the resulting cookie string.
    */
   sync GetCookieString(URI host,
-                       URI originating,
+                       bool isForeign,
                        bool fromHttp)
        returns (nsCString result);
 
   /*
    * Set a cookie string.
    *
    * @param host
    *        Same as the 'aURI' argument to nsICookieService.setCookieString.
-   * @param originating
-   *        The originating URI associated with the request. This is used
-   *        to determine whether the request is first or third party, for
-   *        purposes of allowing access to cookies. This should be obtained
-   *        from nsICookiePermission.getOriginatingURI. This parameter may
-   *        be null; in this case, the request is assumed to be third party
-   *        and may be rejected depending on user preferences.
+   * @param isForeign
+   *        True if the the request is third party, for purposes of allowing
+   *        access to cookies. This should be obtained from
+   *        mozIThirdPartyUtil.isThirdPartyChannel. Third party requests may be
+   *        rejected depending on user preferences; if those checks are
+   *        disabled, this parameter is ignored.
    * @param cookieString
    *        Same as the 'aCookie' argument to nsICookieService.setCookieString.
    * @param serverTime
    *        Same as the 'aServerTime' argument to
    *        nsICookieService.setCookieStringFromHttp. If the string is empty or
    *        null (e.g. for non-HTTP requests), the current local time is used.
    * @param fromHttp
    *        Whether the result is for an HTTP request header. This should be
    *        true for nsICookieService.setCookieStringFromHttp calls, false
    *        otherwise.
    *
    * @see nsICookieService.setCookieString
    * @see nsICookieService.setCookieStringFromHttp
-   * @see nsICookiePermission.getOriginatingURI
+   * @see mozIThirdPartyUtil.isThirdPartyChannel
    */
   SetCookieString(URI host,
-                  URI originating,
+                  bool isForeign,
                   nsCString cookieString,
                   nsCString serverTime,
                   bool fromHttp);
 
   __delete__();
 };
 
 }
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -61,16 +61,17 @@
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsILineInputStream.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
+#include "mozIThirdPartyUtil.h"
 
 #include "nsTArray.h"
 #include "nsCOMArray.h"
 #include "nsIMutableArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsEnumeratorUtils.h"
 #include "nsAutoPtr.h"
 #include "nsReadableUtils.h"
@@ -1091,21 +1092,23 @@ nsresult
 nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
                                        nsIChannel *aChannel,
                                        bool aHttpBound,
                                        char** aCookie)
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookie);
 
-  nsCOMPtr<nsIURI> originatingURI;
-  GetOriginatingURI(aChannel, getter_AddRefs(originatingURI));
+  // Determine whether the request is foreign. Failure is acceptable.
+  PRBool isForeign = true;
+  if (RequireThirdPartyCheck())
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsCAutoString result;
-  GetCookieStringInternal(aHostURI, originatingURI, aHttpBound, result);
+  GetCookieStringInternal(aHostURI, isForeign, aHttpBound, result);
   *aCookie = result.IsEmpty() ? nsnull : ToNewCString(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCookieService::SetCookieString(nsIURI     *aHostURI,
                                  nsIPrompt  *aPrompt,
                                  const char *aCookieHeader,
@@ -1131,29 +1134,31 @@ nsCookieService::SetCookieStringCommon(n
                                        const char *aCookieHeader,
                                        const char *aServerTime,
                                        nsIChannel *aChannel,
                                        bool aFromHttp) 
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookieHeader);
 
-  nsCOMPtr<nsIURI> originatingURI;
-  GetOriginatingURI(aChannel, getter_AddRefs(originatingURI));
+  // Determine whether the request is foreign. Failure is acceptable.
+  PRBool isForeign = true;
+  if (RequireThirdPartyCheck())
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsDependentCString cookieString(aCookieHeader);
   nsDependentCString serverTime(aServerTime ? aServerTime : "");
-  SetCookieStringInternal(aHostURI, originatingURI, cookieString,
+  SetCookieStringInternal(aHostURI, isForeign, cookieString,
                           serverTime, aFromHttp);
   return NS_OK;
 }
 
 void
 nsCookieService::SetCookieStringInternal(nsIURI          *aHostURI,
-                                         nsIURI          *aOriginatingURI,
+                                         bool             aIsForeign,
                                          const nsCString &aCookieHeader,
                                          const nsCString &aServerTime,
                                          PRBool           aFromHttp) 
 {
   NS_ASSERTION(aHostURI, "null host!");
 
   // get the base domain for the host URI.
   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
@@ -1165,17 +1170,17 @@ nsCookieService::SetCookieStringInternal
   nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
   if (NS_FAILED(rv)) {
     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, 
                       "couldn't get base domain from URI");
     return;
   }
 
   // check default prefs
-  CookieStatus cookieStatus = CheckPrefs(aHostURI, aOriginatingURI, baseDomain,
+  CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
                                          requireHostMatch, aCookieHeader.get());
   // fire a notification if cookie was rejected (but not if there was an error)
   switch (cookieStatus) {
   case STATUS_REJECTED:
     NotifyRejected(aHostURI);
   case STATUS_REJECTED_WITH_ERROR:
     return;
   default:
@@ -1246,16 +1251,22 @@ nsCookieService::PrefChanged(nsIPrefBran
     mMaxCookiesPerHost = (PRUint16) LIMIT(val, 1, 0xFFFF, kMaxCookiesPerHost);
 
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val)))
     mCookiePurgeAge = LIMIT(val, 0, PR_INT32_MAX, PR_INT32_MAX) * PR_USEC_PER_SEC;
 
   PRBool boolval;
   if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefThirdPartySession, &boolval)))
     mThirdPartySession = boolval;
+
+  // Lazily instantiate the third party service if necessary.
+  if (!mThirdPartyUtil && RequireThirdPartyCheck()) {
+    mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+    NS_ABORT_IF_FALSE(mThirdPartyUtil, "require ThirdPartyUtil service");
+  }
 }
 
 /******************************************************************************
  * nsICookieManager impl:
  * nsICookieManager
  ******************************************************************************/
 
 NS_IMETHODIMP
@@ -1879,17 +1890,17 @@ public:
     // browser!  see bug 236772.
     // note: CreationID is unique, so two id's can never be equal.
     return aCookie1->CreationID() < aCookie2->CreationID();
   }
 };
 
 void
 nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
-                                         nsIURI *aOriginatingURI,
+                                         bool aIsForeign,
                                          PRBool aHttpBound,
                                          nsCString &aCookieString)
 {
   NS_ASSERTION(aHostURI, "null host!");
 
   // 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
@@ -1906,17 +1917,17 @@ nsCookieService::GetCookieStringInternal
   if (!hostFromURI.IsEmpty() && hostFromURI.Last() == '.')
     hostFromURI.Truncate(hostFromURI.Length() - 1);
   if (NS_FAILED(rv)) {
     COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "invalid host/path from URI");
     return;
   }
 
   // check default prefs
-  CookieStatus cookieStatus = CheckPrefs(aHostURI, aOriginatingURI, baseDomain,
+  CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
                                          requireHostMatch, nsnull);
   // for GetCookie(), we don't fire rejection notifications.
   switch (cookieStatus) {
   case STATUS_REJECTED:
   case STATUS_REJECTED_WITH_ERROR:
     return;
   default:
     break;
@@ -2588,62 +2599,26 @@ static inline PRBool IsSubdomainOf(const
 {
   if (a == b)
     return PR_TRUE;
   if (a.Length() > b.Length())
     return a[a.Length() - b.Length() - 1] == '.' && StringEndsWith(a, b);
   return PR_FALSE;
 }
 
-PRBool
-nsCookieService::IsForeign(const nsCString &aBaseDomain,
-                           PRBool           aRequireHostMatch,
-                           nsIURI          *aFirstURI)
+bool
+nsCookieService::RequireThirdPartyCheck()
 {
-  nsCAutoString firstHost;
-  if (NS_FAILED(aFirstURI->GetAsciiHost(firstHost))) {
-    // assume foreign
-    return PR_TRUE;
-  }
-
-  // trim any trailing dot
-  if (!firstHost.IsEmpty() && firstHost.Last() == '.')
-    firstHost.Truncate(firstHost.Length() - 1);
-
-  // check whether the host is either an IP address, an alias such as
-  // 'localhost', an eTLD such as 'co.uk', or the empty string. in these
-  // cases, require an exact string match for the domain. note that the base
-  // domain parameter will be equivalent to the host in this case.
-  if (aRequireHostMatch)
-    return !firstHost.Equals(aBaseDomain);
-
-  // ensure the originating domain is also derived from the host's base domain.
-  return !IsSubdomainOf(firstHost, aBaseDomain);
-}
-
-void
-nsCookieService::GetOriginatingURI(nsIChannel *aChannel,
-                                   nsIURI **aURI)
-{
-  // Determine the originating URI. We only need to do this if we're
-  // rejecting or altering the lifetime of third-party cookies.
-  if (mCookieBehavior != BEHAVIOR_REJECTFOREIGN && !mThirdPartySession)
-    return;
-
-  if (!mPermissionService) {
-    NS_WARNING("nsICookiePermission unavailable! Cookie may be rejected");
-    return;
-  }
-
-  mPermissionService->GetOriginatingURI(aChannel, aURI);
+  // 'true' iff we need to perform a third party test.
+  return mCookieBehavior == BEHAVIOR_REJECTFOREIGN || mThirdPartySession;
 }
 
 CookieStatus
 nsCookieService::CheckPrefs(nsIURI          *aHostURI,
-                            nsIURI          *aOriginatingURI,
+                            bool             aIsForeign,
                             const nsCString &aBaseDomain,
                             PRBool           aRequireHostMatch,
                             const char      *aCookieHeader)
 {
   nsresult rv;
 
   // don't let ftp sites get/set cookies (could be a security issue)
   PRBool ftp;
@@ -2674,26 +2649,23 @@ nsCookieService::CheckPrefs(nsIURI      
   }
 
   // check default prefs
   if (mCookieBehavior == BEHAVIOR_REJECT) {
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled");
     return STATUS_REJECTED;
   }
 
-  if (mCookieBehavior == BEHAVIOR_REJECTFOREIGN || mThirdPartySession) {
+  if (RequireThirdPartyCheck() && aIsForeign) {
     // check if cookie is foreign
-    if (!aOriginatingURI ||
-        IsForeign(aBaseDomain, aRequireHostMatch, aOriginatingURI)) {
-      if (mCookieBehavior == BEHAVIOR_ACCEPT && mThirdPartySession)
-        return STATUS_ACCEPT_SESSION;
-
-      COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
-      return STATUS_REJECTED;
-    }
+    if (mCookieBehavior == BEHAVIOR_ACCEPT && mThirdPartySession)
+      return STATUS_ACCEPT_SESSION;
+
+    COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
+    return STATUS_REJECTED;
   }
 
   // if nothing has complained, accept cookie
   return STATUS_ACCEPTED;
 }
 
 // processes domain attribute, and returns PR_TRUE if host has permission to set for this domain.
 PRBool
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -63,16 +63,17 @@ class nsIEffectiveTLDService;
 class nsIIDNService;
 class nsIPrefBranch;
 class nsIObserverService;
 class nsIURI;
 class nsIChannel;
 class mozIStorageService;
 class mozIStorageStatementCallback;
 class mozIStorageCompletionCallback;
+class mozIThirdPartyUtil;
 class ReadCookieDBListener;
 
 struct nsCookieAttributes;
 struct nsListIter;
 struct nsEnumerationData;
 
 namespace mozilla {
 namespace net {
@@ -224,43 +225,43 @@ class nsCookieService : public nsICookie
     void                          CancelAsyncRead(PRBool aPurgeReadSet);
     mozIStorageConnection*        GetSyncDBConn();
     void                          EnsureReadDomain(const nsCString &aBaseDomain);
     void                          EnsureReadComplete();
     nsresult                      NormalizeHost(nsCString &aHost);
     nsresult                      GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
     nsresult                      GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
     nsresult                      GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
-    void                          GetCookieStringInternal(nsIURI *aHostURI, nsIURI *aOriginatingURI, PRBool aHttpBound, nsCString &aCookie);
+    void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, PRBool aHttpBound, nsCString &aCookie);
     nsresult                      SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
-    void                          SetCookieStringInternal(nsIURI *aHostURI, nsIURI *aOriginatingURI, const nsCString &aCookieHeader, const nsCString &aServerTime, PRBool aFromHttp);
+    void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, const nsCString &aCookieHeader, const nsCString &aServerTime, PRBool aFromHttp);
     PRBool                        SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, PRBool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
     void                          AddInternal(const nsCString& aBaseDomain, nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL);
     void                          AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
     void                          UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
     static PRBool                 GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound);
     static PRBool                 ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
-    PRBool                        IsForeign(const nsCString &aBaseDomain, PRBool aRequireHostMatch, nsIURI *aFirstURI);
-    void                          GetOriginatingURI(nsIChannel *aChannel, nsIURI **aURI);
-    CookieStatus                  CheckPrefs(nsIURI *aHostURI, nsIURI *aOriginatingURI, const nsCString &aBaseDomain, PRBool aRequireHostMatch, const char *aCookieHeader);
+    bool                          RequireThirdPartyCheck();
+    CookieStatus                  CheckPrefs(nsIURI *aHostURI, bool aIsForeign, const nsCString &aBaseDomain, PRBool aRequireHostMatch, const char *aCookieHeader);
     PRBool                        CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, PRBool aRequireHostMatch);
     static PRBool                 CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
     static PRBool                 GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime);
     void                          RemoveAllFromMemory();
     void                          PurgeCookies(PRInt64 aCurrentTimeInUsec);
     PRBool                        FindCookie(const nsCString& aBaseDomain, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter, PRInt64 aCurrentTime);
     PRUint32                      CountCookiesFromHostInternal(const nsCString &aBaseDomain, nsEnumerationData &aData);
     void                          NotifyRejected(nsIURI *aHostURI);
     void                          NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
 
   protected:
     // cached members.
     nsCOMPtr<nsIObserverService>     mObserverService;
     nsCOMPtr<nsICookiePermission>    mPermissionService;
+    nsCOMPtr<mozIThirdPartyUtil>     mThirdPartyUtil;
     nsCOMPtr<nsIEffectiveTLDService> mTLDService;
     nsCOMPtr<nsIIDNService>          mIDNService;
     nsCOMPtr<mozIStorageService>     mStorageService;
 
     // we have two separate DB states: one for normal browsing and one for
     // private browsing, switching between them as appropriate. this state
     // encapsulates both the in-memory table and the on-disk DB.
     // note that the private states' dbConn should always be null - we never
--- a/netwerk/cookie/nsICookiePermission.idl
+++ b/netwerk/cookie/nsICookiePermission.idl
@@ -137,16 +137,18 @@ interface nsICookiePermission : nsISuppo
    * find the root content docshell, and the URI associated with its principal.
    * if the root content docshell or its principal's URI cannot be obtained,
    * this method will throw.
    *
    * @param aChannel
    *        the channel for the load trying to get or set cookies
    *
    * @return the originating URI.
+   *
+   * @status DEPRECATED -- use mozIThirdPartyUtil instead.
    */
   nsIURI getOriginatingURI(in nsIChannel aChannel);
 };
 
 %{ C++
 /**
  * The nsICookiePermission implementation is an XPCOM service registered
  * under the ContractID: