Bug 457153. Introduce an nsILoadContext interface so that consumers can get load context information from a channel/loadgroup without having to depend on getInterface of docshell stuff. r=dwitte for cookie part, r=jst for rest, sr=jst, a=beltzner for CLOSED TREE
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 24 Nov 2008 13:32:04 -0500
changeset 21877 d584db13034e68e8642418410b10cd490a08932f
parent 21876 66aec2d9ab08f4d8bad26fdd6c4cb3c689fa8046
child 21878 371b210baaa4d26004c6140703a2e8fa1bc2ea15
push idunknown
push userunknown
push dateunknown
reviewersdwitte, jst, jst, beltzner
bugs457153
milestone1.9.1b2pre
Bug 457153. Introduce an nsILoadContext interface so that consumers can get load context information from a channel/loadgroup without having to depend on getInterface of docshell stuff. r=dwitte for cookie part, r=jst for rest, sr=jst, a=beltzner for CLOSED TREE
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
docshell/base/Makefile.in
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsILoadContext.idl
extensions/cookie/nsCookiePermission.cpp
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -157,21 +157,18 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsCycleCollector.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsICategoryManager.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsIContentViewer.h"
 #include "nsIXMLContentSink.h"
-#include "nsIChannelEventSink.h"
 #include "nsContentErrors.h"
 #include "nsIXULDocument.h"
-#include "nsIProgressEventSink.h"
-#include "nsISecurityEventSink.h"
 #include "nsIPrompt.h"
 #include "nsIPropertyBag2.h"
 
 #include "nsFrameLoader.h"
 
 #include "mozAutoDocUpdate.h"
 
 
@@ -1132,40 +1129,68 @@ nsExternalResourceMap::PendingLoad::Star
 
   channel->SetNotificationCallbacks(req);
   return channel->AsyncOpen(this, nsnull);
 }
 
 NS_IMPL_ISUPPORTS1(nsExternalResourceMap::LoadgroupCallbacks,
                    nsIInterfaceRequestor)
 
+#define IMPL_SHIM(_i) \
+  NS_IMPL_ISUPPORTS1(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
+
+IMPL_SHIM(nsILoadContext)
+IMPL_SHIM(nsIProgressEventSink);
+IMPL_SHIM(nsIChannelEventSink);
+IMPL_SHIM(nsISecurityEventSink);
+IMPL_SHIM(nsIApplicationCacheContainer);
+
+#undef IMPL_SHIM
+
+#define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
+
+#define TRY_SHIM(_i)                                                       \
+  PR_BEGIN_MACRO                                                           \
+    if (IID_IS(_i)) {                                                      \
+      nsCOMPtr<_i> real = do_GetInterface(mCallbacks);                     \
+      if (!real) {                                                         \
+        return NS_NOINTERFACE;                                             \
+      }                                                                    \
+      nsCOMPtr<_i> shim = new _i##Shim(this, real);                        \
+      if (!shim) {                                                         \
+        return NS_ERROR_OUT_OF_MEMORY;                                     \
+      }                                                                    \
+      *aSink = shim.forget().get();                                        \
+      return NS_OK;                                                        \
+    }                                                                      \
+  PR_END_MACRO
+
 NS_IMETHODIMP
 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
                                                         void **aSink)
 {
-#define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
   if (mCallbacks &&
-      (IID_IS(nsIProgressEventSink) ||
-       IID_IS(nsIChannelEventSink) ||
-       IID_IS(nsISecurityEventSink) ||
-       IID_IS(nsIPrompt) ||
-       IID_IS(nsIAuthPrompt) ||
-       IID_IS(nsIAuthPrompt2) ||
-       IID_IS(nsIApplicationCacheContainer) ||
-       // XXXbz evil hack for cookies for now
-       IID_IS(nsIDOMWindow) ||
-       IID_IS(nsIDocShellTreeItem))) {
+      (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2))) {
     return mCallbacks->GetInterface(aIID, aSink);
   }
-#undef IID_IS
 
   *aSink = nsnull;
+
+  TRY_SHIM(nsILoadContext);
+  TRY_SHIM(nsIProgressEventSink);
+  TRY_SHIM(nsIChannelEventSink);
+  TRY_SHIM(nsISecurityEventSink);
+  TRY_SHIM(nsIApplicationCacheContainer);
+    
   return NS_NOINTERFACE;
 }
 
+#undef TRY_SHIM
+#undef IID_IS
+
 nsExternalResourceMap::ExternalResource::~ExternalResource()
 {
   if (mViewer) {
     mViewer->Close(nsnull);
     mViewer->Destroy();
   }
 }
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -105,16 +105,20 @@
 #include "pldhash.h"
 #include "nsAttrAndChildArray.h"
 #include "nsDOMAttributeMap.h"
 #include "nsPresShellIterator.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIDocumentViewer.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsILoadContext.h"
+#include "nsIProgressEventSink.h"
+#include "nsISecurityEventSink.h"
+#include "nsIChannelEventSink.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsIEventListenerManager;
@@ -491,17 +495,52 @@ protected:
   {
   public:
     LoadgroupCallbacks(nsIInterfaceRequestor* aOtherCallbacks)
       : mCallbacks(aOtherCallbacks)
     {}
     NS_DECL_ISUPPORTS
     NS_DECL_NSIINTERFACEREQUESTOR
   private:
+    // The only reason it's safe to hold a strong ref here without leaking is
+    // that the notificationCallbacks on a loadgroup aren't the docshell itself
+    // but a shim that holds a weak reference to the docshell.
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+
+    // Use shims for interfaces that docshell implements directly so that we
+    // don't hand out references to the docshell.  The shims should all allow
+    // getInterface back on us, but other than that each one should only
+    // implement one interface.
+    
+    // XXXbz I wish we could just derive the _allcaps thing from _i
+#define DECL_SHIM(_i, _allcaps)                                              \
+    class _i##Shim : public nsIInterfaceRequestor,                           \
+                     public _i                                               \
+    {                                                                        \
+    public:                                                                  \
+      _i##Shim(nsIInterfaceRequestor* aIfreq, _i* aRealPtr)                  \
+        : mIfReq(aIfreq), mRealPtr(aRealPtr)                                 \
+      {                                                                      \
+        NS_ASSERTION(mIfReq, "Expected non-null here");                      \
+        NS_ASSERTION(mRealPtr, "Expected non-null here");                    \
+      }                                                                      \
+      NS_DECL_ISUPPORTS                                                      \
+      NS_FORWARD_NSIINTERFACEREQUESTOR(mIfReq->);                            \
+      NS_FORWARD_##_allcaps(mRealPtr->);                                     \
+    private:                                                                 \
+      nsCOMPtr<nsIInterfaceRequestor> mIfReq;                                \
+      nsCOMPtr<_i> mRealPtr;                                                 \
+    };
+
+    DECL_SHIM(nsILoadContext, NSILOADCONTEXT)
+    DECL_SHIM(nsIProgressEventSink, NSIPROGRESSEVENTSINK)
+    DECL_SHIM(nsIChannelEventSink, NSICHANNELEVENTSINK)
+    DECL_SHIM(nsISecurityEventSink, NSISECURITYEVENTSINK)
+    DECL_SHIM(nsIApplicationCacheContainer, NSIAPPLICATIONCACHECONTAINER)
+#undef DECL_SHIM
   };
   
   /**
    * Add an ExternalResource for aURI.  aViewer and aLoadGroup might be null
    * when this is called if the URI didn't result in an XML document.  This
    * function makes sure to remove the pending load for aURI, if any, from our
    * hashtable, and to notify its observers, if any.
    */
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -111,17 +111,18 @@ XPIDLSRCS	= \
 		nsIContentViewer.idl		\
 		nsIContentViewerEdit.idl	\
 		nsIContentViewerFile.idl	\
 		nsIURIFixup.idl			\
 		nsIEditorDocShell.idl		\
 		nsIWebPageDescriptor.idl	\
 		nsIURIClassifier.idl		\
 		nsIChannelClassifier.idl	\
-		nsIDownloadHistory.idl \
+		nsIDownloadHistory.idl          \
+		nsILoadContext.idl              \
 		$(NULL)
 
 EXPORTS		= nsDocShellLoadTypes.h
 
 CPPSRCS		= \
 		nsDocShell.cpp		\
 		nsWebShell.cpp		\
 		nsDocShellLoadInfo.cpp		\
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -431,16 +431,17 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
     NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
     NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
     NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
     NS_INTERFACE_MAP_ENTRY(nsIObserver)
+    NS_INTERFACE_MAP_ENTRY(nsILoadContext)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 ///*****************************************************************************
 // nsDocShell::nsIInterfaceRequestor
 //*****************************************************************************   
 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
 {
     NS_PRECONDITION(aSink, "null out param");
@@ -9529,16 +9530,62 @@ nsDocShell::Observe(nsISupports *aSubjec
             mUseErrorPages = tmpbool;
 
     } else {
         rv = NS_ERROR_UNEXPECTED;
     }
     return rv;
 }
 
+//*****************************************************************************
+// nsDocShell::nsILoadContext
+//*****************************************************************************
+NS_IMETHODIMP
+nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
+{
+    return CallGetInterface(this, aWindow);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
+{
+    nsresult rv;
+    nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this), &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return win->GetTop(aWindow);
+}
+
+NS_IMETHODIMP
+nsDocShell::IsAppOfType(PRUint32 aAppType, PRBool *aIsOfType)
+{
+    nsCOMPtr<nsIDocShell> shell = this;
+    while (shell) {
+        PRUint32 type;
+        shell->GetAppType(&type);
+        if (type == aAppType) {
+            *aIsOfType = PR_TRUE;
+            return NS_OK;
+        }
+        nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
+        nsCOMPtr<nsIDocShellTreeItem> parent;
+        item->GetParent(getter_AddRefs(parent));
+        shell = do_QueryInterface(parent);
+    }
+
+    *aIsOfType = PR_FALSE;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsContent(PRBool *aIsContent)
+{
+    *aIsContent = (mItemType == typeContent);
+    return NS_OK;
+}
+
 /* static */
 nsresult
 nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
 {
     // Note: about:blank URIs do NOT inherit the security context from the
     // current document, which is what this function tests for...
     return NS_URIChainHasFlags(aURI,
                                nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -102,16 +102,17 @@
 #include "nsDocShellTransferableHooks.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIObserver.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIURIClassifier.h"
 #include "nsIChannelClassifier.h"
+#include "nsILoadContext.h"
 
 class nsIScrollableView;
 class nsDocShell;
 
 /* load commands were moved to nsIDocShell.h */
 /* load types were moved to nsDocShellLoadTypes.h */
 
 /* internally used ViewMode types */
@@ -180,17 +181,18 @@ class nsDocShell : public nsDocLoader,
                    public nsIDocCharset, 
                    public nsIContentViewerContainer,
                    public nsIScriptGlobalObjectOwner,
                    public nsIRefreshURI,
                    public nsIWebProgressListener,
                    public nsIEditorDocShell,
                    public nsIWebPageDescriptor,
                    public nsIAuthPromptProvider,
-                   public nsIObserver
+                   public nsIObserver,
+                   public nsILoadContext
 {
 friend class nsDSURIContentListener;
 
 public:
     // Object Management
     nsDocShell();
 
     virtual nsresult Init();
@@ -209,16 +211,17 @@ public:
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NSIWEBPROGRESSLISTENER
     NS_DECL_NSIREFRESHURI
     NS_DECL_NSICONTENTVIEWERCONTAINER
     NS_DECL_NSIEDITORDOCSHELL
     NS_DECL_NSIWEBPAGEDESCRIPTOR
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSIOBSERVER
+    NS_DECL_NSILOADCONTEXT
 
     NS_IMETHOD Stop() {
         // Need this here because otherwise nsIWebNavigation::Stop
         // overrides the docloader's Stop()
         return nsDocLoader::Stop();
     }
 
     // Need to implement (and forward) nsISecurityEventSink, because
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsILoadContext.idl
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: ft=cpp tw=78 sw=2 et ts=2 sts=2 cin
+ * ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Boris Zbarsky <bzbarsky@mit.edu> (original author)
+ *
+ * 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 nsIDOMWindow;
+
+/**
+ * An nsILoadContext represents the context of a load.  This interface
+ * can be queried for various information about where the load is
+ * happening.
+ */
+[scriptable, uuid(314d8a54-1caf-4721-94d7-f6c82d9b82ed)]
+interface nsILoadContext : nsISupports
+{
+  /**
+   * associatedWindow is the window with which the load is associated, if any.
+   * Note that the load may be triggered by a document which is different from
+   * the document in associatedWindow, and in fact the source of the load need
+   * not be same-origin with the document in associatedWindow.  This attribute
+   * may be null if there is no associated window.
+   */
+  readonly attribute nsIDOMWindow associatedWindow;
+
+  /**
+   * topWindow is the top window which is of same type as associatedWindow.
+   * This is equivalent to associatedWindow.top, but is provided here as a
+   * convenience.  All the same caveats as associatedWindow of apply, of
+   * course.  This attribute may be null if there is no associated window.
+   */
+  readonly attribute nsIDOMWindow topWindow;
+
+  /**
+   * Check whether the load is happening in a particular type of application.
+   *
+   * @param an application type.  For now, the constants to be passed here are
+   *        the nsIDocShell APP_TYPE_* constants.
+   *
+   * @return whether there is some ancestor of the associatedWindow that is of
+   *         the given app type.
+   */
+  boolean isAppOfType(in unsigned long appType);
+
+  /**
+   * True if the load context is content (as opposed to chrome).  This is
+   * determined based on the type of window the load is performed in, NOT based
+   * on any URIs that might be around.
+   */
+  readonly attribute boolean isContent;
+};
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -1,9 +1,10 @@
-// vim:ts=2:sw=2:et:
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:ts=2:sw=2:et: */
 /* ***** 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/
  *
@@ -44,25 +45,25 @@
 #include "nsICookiePromptService.h"
 #include "nsICookieManager2.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefBranch2.h"
 #include "nsIDocShell.h"
-#include "nsIDocShellTreeItem.h"
 #include "nsIWebNavigation.h"
-#include "nsINode.h"
 #include "nsIChannel.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocument.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsCRT.h"
+#include "nsILoadContext.h"
+#include "nsIScriptObjectPrincipal.h"
 
 /****************************************************************
  ************************ nsCookiePermission ********************
  ****************************************************************/
 
 // values for mCookiesLifetimePolicy
 // 0 == accept normally
 // 1 == ask before accepting
@@ -86,16 +87,17 @@ static const char kCookiesPrefsMigrated[
 static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
 static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
 static const char kCookiesAskPermission[] = "network.cookie.warnAboutCookies";
 
 static const char kPermissionType[] = "cookie";
 
 #ifdef MOZ_MAIL_NEWS
 // returns PR_TRUE if URI appears to be the URI of a mailnews protocol
+// XXXbz this should be a protocol flag, not a scheme list, dammit!
 static PRBool
 IsFromMailNews(nsIURI *aURI)
 {
   static const char *kMailNewsProtocols[] =
       { "imap", "news", "snews", "mailbox", nsnull };
   PRBool result;
   for (const char **p = kMailNewsProtocols; *p; ++p) {
     if (NS_SUCCEEDED(aURI->SchemeIs(*p, &result)) && result)
@@ -203,45 +205,31 @@ NS_IMETHODIMP
 nsCookiePermission::CanAccess(nsIURI         *aURI,
                               nsIChannel     *aChannel,
                               nsCookieAccess *aResult)
 {
 #ifdef MOZ_MAIL_NEWS
   // disable cookies in mailnews if user's prefs say so
   if (mCookiesDisabledForMailNews) {
     //
-    // try to examine the "app type" of the docshell owning this request.  if
-    // we find a docshell in the heirarchy of type APP_TYPE_MAIL, then assume
-    // this URI is being loaded from within mailnews.
-    //
-    // XXX this is a pretty ugly hack at the moment since cookies really
-    // shouldn't have to talk to the docshell directly.  ultimately, we want
-    // to talk to some more generic interface, which the docshell would also
-    // implement.  but, the basic mechanism here of leveraging the channel's
-    // (or loadgroup's) notification callbacks attribute seems ideal as it
-    // avoids the problem of having to modify all places in the code which
-    // kick off network requests.
-    //
-    PRUint32 appType = nsIDocShell::APP_TYPE_UNKNOWN;
+    // try to examine the "app type" of the window owning this request.  if it
+    // or some ancestor is of type APP_TYPE_MAIL, then assume this URI is being
+    // loaded from within mailnews.
+    PRBool isMail = PR_FALSE;
     if (aChannel) {
-      nsCOMPtr<nsIDocShellTreeItem> item, parent;
-      NS_QueryNotificationCallbacks(aChannel, parent);
-      if (parent) {
-        do {
-            item = parent;
-            nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(item);
-            if (docshell)
-              docshell->GetAppType(&appType);
-        } while (appType != nsIDocShell::APP_TYPE_MAIL &&
-                 NS_SUCCEEDED(item->GetParent(getter_AddRefs(parent))) &&
-                 parent);
+      nsCOMPtr<nsILoadContext> ctx;
+      NS_QueryNotificationCallbacks(aChannel, ctx);
+      if (ctx) {
+        PRBool temp;
+        isMail =
+          NS_FAILED(ctx->IsAppOfType(nsIDocShell::APP_TYPE_MAIL, &temp)) ||
+          temp;
       }
     }
-    if ((appType == nsIDocShell::APP_TYPE_MAIL) ||
-        IsFromMailNews(aURI)) {
+    if (isMail || IsFromMailNews(aURI)) {
       *aResult = ACCESS_DENY;
       return NS_OK;
     }
   }
 #endif // MOZ_MAIL_NEWS
   
   // finally, check with permission manager...
   nsresult rv = mPermMgr->TestPermission(aURI, kPermissionType, (PRUint32 *) aResult);
@@ -345,18 +333,23 @@ nsCookiePermission::CanSetCookie(nsIURI 
       // worth the memory.
       nsresult rv;
       nsCOMPtr<nsICookiePromptService> cookiePromptService =
           do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv);
       if (NS_FAILED(rv)) return rv;
 
       // try to get a nsIDOMWindow from the channel...
       nsCOMPtr<nsIDOMWindow> parent;
-      if (aChannel)
-        NS_QueryNotificationCallbacks(aChannel, parent);
+      if (aChannel) {
+        nsCOMPtr<nsILoadContext> ctx;
+        NS_QueryNotificationCallbacks(aChannel, ctx);
+        if (ctx) {
+          ctx->GetAssociatedWindow(getter_AddRefs(parent));
+        }
+      }
 
       // get some useful information to present to the user:
       // whether a previous cookie already exists, and how many cookies this host
       // has set
       PRBool foundCookie = PR_FALSE;
       PRUint32 countFromHost;
       nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
       if (NS_SUCCEEDED(rv)) {
@@ -421,91 +414,84 @@ nsCookiePermission::CanSetCookie(nsIURI 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsCookiePermission::GetOriginatingURI(nsIChannel  *aChannel,
                                       nsIURI     **aURI)
 {
   /* to find the originating URI, we use the loadgroup of the channel to obtain
-   * the docshell owning the load, and from there, we find the root content
-   * docshell and its URI. there are several possible cases:
+   * the window owning the load, and from there, we find the top same-type
+   * window and its URI. there are several possible cases:
    *
    * 1) no channel. this will occur for plugins using the nsICookieStorage
    *    interface, since they have none to provide. other consumers should
    *    have a channel.
    *
-   * 2) a channel, but no docshell. this can occur when the consumer kicking
+   * 2) a channel, but no window. this can occur when the consumer kicking
    *    off the load doesn't provide one to the channel, and should be limited
-   *    to loads of certain types of resources (e.g. favicons).
-   *
-   * 3) a non-content docshell. this occurs for loads kicked off from chrome,
-   *    where no content docshell exists (favicons can also fall into this
-   *    category).
+   *    to loads of certain types of resources.
    *
-   * 4) a content docshell equal to the root content docshell, with channel
-   *    loadflags LOAD_DOCUMENT_URI. 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 currentURI hasn't yet been set,
-   *    and will be bogus. we return the channel URI in this case. note that
-   *    we could also allow non-content docshells here, but that goes against
-   *    the philosophy of having an audit trail back to a URI the user typed
-   *    or clicked on.
+   * 3) a window equal to the top window of same type, with the channel its
+   *    document channel. 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. we return the channel URI in this case.
    *
-   * 5) a root content docshell. this covers most cases for an ordinary page
-   *    load from the location bar, and will catch nested frames within
-   *    a page, image loads, etc. we return the URI of the docshell's principal
+   * 4) Anything else. this covers most cases for an ordinary page load from
+   *    the location bar, and will catch nested frames within a page, image
+   *    loads, etc. we return the URI of the root window's document's principal
    *    in this case.
-   *
    */
 
   *aURI = nsnull;
 
   // case 1)
   if (!aChannel)
     return NS_ERROR_NULL_POINTER;
 
-  // find the docshell and its root
-  nsCOMPtr<nsIDocShellTreeItem> docshell, root;
-  NS_QueryNotificationCallbacks(aChannel, docshell);
-  if (docshell)
-    docshell->GetSameTypeRootTreeItem(getter_AddRefs(root));
+  // find the associated window and its top window
+  nsCOMPtr<nsILoadContext> ctx;
+  NS_QueryNotificationCallbacks(aChannel, ctx);
+  nsCOMPtr<nsIDOMWindow> topWin, ourWin;
+  if (ctx) {
+    ctx->GetTopWindow(getter_AddRefs(topWin));
+    ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
+  }
 
-  PRInt32 type;
-  if (root)
-    root->GetItemType(&type);
-
-  // cases 2) and 3)
-  if (!root || type != nsIDocShellTreeItem::typeContent)
+  // case 2)
+  if (!topWin)
     return NS_ERROR_INVALID_ARG;
 
-  // case 4)
-  if (docshell == root) {
+  // case 3)
+  if (ourWin == topWin) {
+    // Check whether this is the document channel for this window (representing
+    // a load of a new page).  This is a bit of a nasty hack, but we will
+    // hopefully flag these channels better later.
     nsLoadFlags flags;
     aChannel->GetLoadFlags(&flags);
 
     if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
-      // get the channel URI - the docshell's will be bogus
+      // get the channel URI - the window's will be bogus
       aChannel->GetURI(aURI);
       if (!*aURI)
         return NS_ERROR_NULL_POINTER;
 
       return NS_OK;
     }
   }
 
-  // case 5) - get the originating URI from the docshell's principal
-  nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
-  if (webnav) {
-    nsCOMPtr<nsIDOMDocument> doc;
-    webnav->GetDocument(getter_AddRefs(doc));
-    nsCOMPtr<nsINode> node = do_QueryInterface(doc);
-    if (node)
-      node->NodePrincipal()->GetURI(aURI);
-  }
+  // case 4) - get the originating URI from the top window's principal
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(topWin);
+  NS_ENSURE_TRUE(scriptObjPrin, NS_ERROR_UNEXPECTED);
+
+  nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
+  NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
+  
+  prin->GetURI(aURI);
 
   if (!*aURI)
     return NS_ERROR_NULL_POINTER;
 
   // all done!
   return NS_OK;
 }