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

This commit is contained in:
Boris Zbarsky 2008-11-24 13:32:04 -05:00
parent cc9f2ff870
commit 92c0e38ba1
7 changed files with 278 additions and 91 deletions

View File

@ -162,11 +162,8 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
#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"
@ -1137,30 +1134,58 @@ nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
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) {

View File

@ -110,6 +110,10 @@
#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)
@ -496,7 +500,42 @@ protected:
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
};
/**

View File

@ -116,7 +116,8 @@ XPIDLSRCS = \
nsIWebPageDescriptor.idl \
nsIURIClassifier.idl \
nsIChannelClassifier.idl \
nsIDownloadHistory.idl \
nsIDownloadHistory.idl \
nsILoadContext.idl \
$(NULL)
EXPORTS = nsDocShellLoadTypes.h

View File

@ -436,6 +436,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
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)
///*****************************************************************************
@ -9534,6 +9535,52 @@ nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
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)

View File

@ -107,6 +107,7 @@
#include "nsPIDOMEventTarget.h"
#include "nsIURIClassifier.h"
#include "nsIChannelClassifier.h"
#include "nsILoadContext.h"
class nsIScrollableView;
class nsDocShell;
@ -185,7 +186,8 @@ class nsDocShell : public nsDocLoader,
public nsIEditorDocShell,
public nsIWebPageDescriptor,
public nsIAuthPromptProvider,
public nsIObserver
public nsIObserver,
public nsILoadContext
{
friend class nsDSURIContentListener;
@ -214,6 +216,7 @@ public:
NS_DECL_NSIWEBPAGEDESCRIPTOR
NS_DECL_NSIAUTHPROMPTPROVIDER
NS_DECL_NSIOBSERVER
NS_DECL_NSILOADCONTEXT
NS_IMETHOD Stop() {
// Need this here because otherwise nsIWebNavigation::Stop

View File

@ -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;
};

View File

@ -1,4 +1,5 @@
// 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
*
@ -49,15 +50,15 @@
#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 ********************
@ -91,6 +92,7 @@ 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)
{
@ -208,35 +210,21 @@ nsCookiePermission::CanAccess(nsIURI *aURI,
// 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;
}
@ -350,8 +338,13 @@ nsCookiePermission::CanSetCookie(nsIURI *aURI,
// 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
@ -426,35 +419,27 @@ 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).
* to loads of certain types of resources.
*
* 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).
* 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.
*
* 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.
*
* 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;
@ -463,27 +448,29 @@ nsCookiePermission::GetOriginatingURI(nsIChannel *aChannel,
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;
@ -492,15 +479,14 @@ nsCookiePermission::GetOriginatingURI(nsIChannel *aChannel,
}
}
// 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;