Make it possible to give DOMParsers the right principals from C++. Bug 332840,

r=sicking, sr=jst
This commit is contained in:
bzbarsky%mit.edu 2006-09-09 04:54:03 +00:00
parent 3b2bf8ecb8
commit 01946a8fe9
7 changed files with 293 additions and 62 deletions

View File

@ -40,6 +40,7 @@
interface nsIInputStream;
interface nsIDOMDocument;
interface nsIURI;
interface nsIPrincipal;
/**
* The nsIDOMParser interface is a non-SAX interface that can be used
@ -49,10 +50,9 @@ interface nsIURI;
* parsing with the XMLHttpRequest interface, which can be used for
* asynchronous (callback-based) loading.
*/
[scriptable, uuid(4f45513e-55e5-411c-a844-e899057026c1)]
interface nsIDOMParser : nsISupports {
[scriptable, uuid(5db52912-cdee-46d2-9166-01436c3f9e73)]
interface nsIDOMParser : nsISupports
{
/**
* The string passed in is parsed into a DOM document.
*
@ -97,9 +97,41 @@ interface nsIDOMParser : nsISupports {
in string contentType);
/**
* Set/Get the baseURI, may be needed when called from native code.
* Initialize the principal and document and base URIs that the parser should
* use for documents it creates. If this is not called, then a null
* principal and its URI will be used. When creating a DOMParser via the JS
* constructor, this will be called automatically. This method may only be
* called once. If this method fails, all following parse attempts will
* fail.
*
* @param principal The principal to use for documents we create.
* If this is null, a codebase principal will be created
* based on documentURI; in that case the documentURI must
* be non-null.
* @param documentURI The documentURI to use for the documents we create.
* If null, the principal's URI will be used;
* in that case, the principal must be non-null and its
* URI must be non-null.
* @param baseURI The baseURI to use for the documents we create.
* If null, the documentURI will be used.
*/
[noscript] attribute nsIURI baseURI;
[noscript] void init(in nsIPrincipal principal,
in nsIURI documentURI,
in nsIURI baseURI);
};
/**
* The nsIDOMParserJS interface provides a scriptable way of calling init().
* Do NOT use this interface from languages other than JavaScript.
*/
[scriptable, uuid(dca92fe9-ae7a-44b7-80aa-d151216698ac)]
interface nsIDOMParserJS : nsISupports
{
/**
* Just like nsIDOMParser.init, but callable from JS. It'll pick up the args
* from XPConnect.
*/
void init();
};
%{ C++

View File

@ -57,6 +57,7 @@
#include "nsNetCID.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "nsDOMError.h"
// nsIDOMEventListener
nsresult
@ -103,7 +104,8 @@ nsDOMParser::Error(nsIDOMEvent* aEvent)
}
nsDOMParser::nsDOMParser()
: mLoopingForSyncLoad(PR_FALSE)
: mLoopingForSyncLoad(PR_FALSE),
mAttemptedInit(PR_FALSE)
{
}
@ -118,8 +120,10 @@ nsDOMParser::~nsDOMParser()
NS_INTERFACE_MAP_BEGIN(nsDOMParser)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMParser)
NS_INTERFACE_MAP_ENTRY(nsIDOMParser)
NS_INTERFACE_MAP_ENTRY(nsIDOMParserJS)
NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DOMParser)
NS_INTERFACE_MAP_END
@ -187,8 +191,23 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
(nsCRT::strcmp(contentType, "application/xhtml+xml") != 0))
return NS_ERROR_NOT_IMPLEMENTED;
// Put the nsCOMPtr out here so we hold a ref to the stream as needed
nsresult rv;
if (!mPrincipal) {
NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
AttemptedInitMarker marker(&mAttemptedInit);
nsCOMPtr<nsIPrincipal> prin =
do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = Init(prin, nsnull, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ASSERTION(mPrincipal, "Must have principal by now");
NS_ASSERTION(mDocumentURI, "Must have document URI by now");
// Put the nsCOMPtr out here so we hold a ref to the stream as needed
nsCOMPtr<nsIInputStream> bufferedStream;
if (!NS_InputStreamIsBuffered(stream)) {
rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream,
@ -198,40 +217,9 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
stream = bufferedStream;
}
nsCOMPtr<nsIPrincipal> principal;
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
if (secMan) {
secMan->GetSubjectPrincipal(getter_AddRefs(principal));
}
// Try to find a base URI for the document we're creating.
nsCOMPtr<nsIURI> baseURI;
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(nsContentUtils::GetDocumentFromContext());
if (doc) {
baseURI = doc->GetBaseURI();
}
if (!baseURI) {
// No URI from script environment (we are running from command line, for example).
// Create a dummy one.
// XXX Is this safe? Could we get the URI from stream or something?
if (!mBaseURI) {
rv = NS_NewURI(getter_AddRefs(baseURI),
"about:blank" );
if (NS_FAILED(rv)) return rv;
} else {
baseURI = mBaseURI;
}
}
// XXXbz Is this really right? Why are we setting the documentURI to
// baseURI? But note that's what the StartDocumentLoad() below would do
// if we let it reset. In any case, this is odd, since the caller can
// set baseURI to anything it feels like, pretty much.
nsCOMPtr<nsIDOMDocument> domDocument;
rv = nsContentUtils::CreateDocument(EmptyString(), EmptyString(), nsnull,
baseURI, baseURI, principal,
mDocumentURI, mBaseURI, mPrincipal,
getter_AddRefs(domDocument));
NS_ENSURE_SUCCESS(rv, rv);
@ -251,14 +239,11 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
// Create a fake channel
nsCOMPtr<nsIChannel> parserChannel;
NS_NewInputStreamChannel(getter_AddRefs(parserChannel), baseURI, nsnull,
NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nsnull,
nsDependentCString(contentType), nsnull);
NS_ENSURE_STATE(parserChannel);
// Hold a reference to it in this method
if (principal) {
parserChannel->SetOwner(principal);
}
parserChannel->SetOwner(mPrincipal);
if (charset) {
parserChannel->SetContentCharset(nsDependentCString(charset));
@ -273,16 +258,18 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
// Have to pass PR_FALSE for reset here, else the reset will remove
// our event listener. Should that listener addition move to later
// than this call?
// than this call? Then we wouldn't need to mess around with
// SetPrincipal, etc, probably!
rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
nsnull, nsnull,
getter_AddRefs(listener),
PR_FALSE);
if (principal) {
// Make sure to give this document the right principal
document->SetPrincipal(principal);
}
// Make sure to give this document the right principal
document->SetPrincipal(mPrincipal);
// And the right base URI
document->SetBaseURI(mBaseURI);
if (NS_FAILED(rv) || !listener) {
return NS_ERROR_FAILURE;
@ -326,20 +313,200 @@ nsDOMParser::ParseFromStream(nsIInputStream *stream,
return NS_OK;
}
NS_IMETHODIMP
nsDOMParser::GetBaseURI(nsIURI **aBaseURI)
NS_IMETHODIMP
nsDOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI,
nsIURI* baseURI)
{
NS_ENSURE_ARG_POINTER(aBaseURI);
NS_ENSURE_STATE(!mAttemptedInit);
mAttemptedInit = PR_TRUE;
NS_ENSURE_ARG(principal || documentURI);
NS_IF_ADDREF(*aBaseURI = mBaseURI);
mDocumentURI = documentURI;
if (!mDocumentURI) {
principal->GetURI(getter_AddRefs(mDocumentURI));
if (!mDocumentURI) {
return NS_ERROR_INVALID_ARG;
}
}
mPrincipal = principal;
if (!mPrincipal) {
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_NOT_AVAILABLE);
nsresult rv =
secMan->GetCodebasePrincipal(mDocumentURI, getter_AddRefs(mPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
}
mBaseURI = baseURI;
// Note: if mBaseURI is null, fine. Leave it like that; that will use the
// documentURI as the base. Otherwise for null principals we'll get
// nsDocument::SetBaseURI giving errors.
NS_POSTCONDITION(mPrincipal, "Must have principal");
NS_POSTCONDITION(mDocumentURI, "Must have document URI");
return NS_OK;
}
static nsQueryInterface
JSvalToInterface(JSContext* cx, jsval val, nsIXPConnect* xpc, PRBool* wasNull)
{
if (val == JSVAL_NULL) {
*wasNull = PR_TRUE;
return nsQueryInterface(nsnull);
}
*wasNull = PR_FALSE;
if (JSVAL_IS_OBJECT(val)) {
JSObject* arg = JSVAL_TO_OBJECT(val);
nsCOMPtr<nsIXPConnectWrappedNative> native;
xpc->GetWrappedNativeOfJSObject(cx, arg, getter_AddRefs(native));
// do_QueryWrappedNative is not null-safe
if (native) {
return do_QueryWrappedNative(native);
}
}
return nsQueryInterface(nsnull);
}
static nsresult
GetInitArgs(JSContext *cx, PRUint32 argc, jsval *argv,
nsIPrincipal** aPrincipal, nsIURI** aDocumentURI,
nsIURI** aBaseURI)
{
// Only proceed if the caller has UniversalXPConnect.
PRBool haveUniversalXPConnect;
nsresult rv = nsContentUtils::GetSecurityManager()->
IsCapabilityEnabled("UniversalXPConnect", &haveUniversalXPConnect);
NS_ENSURE_SUCCESS(rv, rv);
if (!haveUniversalXPConnect) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsIXPConnect* xpc = nsContentUtils::XPConnect();
// First arg is our principal. If someone passes something that's
// not a principal and not null, die to prevent privilege escalation.
PRBool wasNull;
nsCOMPtr<nsIPrincipal> prin = JSvalToInterface(cx, argv[0], xpc, &wasNull);
if (!prin && !wasNull) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIURI> documentURI;
nsCOMPtr<nsIURI> baseURI;
if (argc > 1) {
// Grab our document URI too. Again, if it's something unexpected bail
// out.
documentURI = JSvalToInterface(cx, argv[1], xpc, &wasNull);
if (!documentURI && !wasNull) {
return NS_ERROR_INVALID_ARG;
}
if (argc > 2) {
// Grab our base URI as well
baseURI = JSvalToInterface(cx, argv[2], xpc, &wasNull);
if (!baseURI && !wasNull) {
return NS_ERROR_INVALID_ARG;
}
}
}
NS_IF_ADDREF(*aPrincipal = prin);
NS_IF_ADDREF(*aDocumentURI = documentURI);
NS_IF_ADDREF(*aBaseURI = baseURI);
return NS_OK;
}
NS_IMETHODIMP
nsDOMParser::SetBaseURI(nsIURI *aBaseURI)
NS_IMETHODIMP
nsDOMParser::Initialize(JSContext *cx, JSObject* obj,
PRUint32 argc, jsval *argv)
{
mBaseURI = aBaseURI;
AttemptedInitMarker marker(&mAttemptedInit);
nsCOMPtr<nsIPrincipal> prin;
nsCOMPtr<nsIURI> documentURI;
nsCOMPtr<nsIURI> baseURI;
if (argc > 0) {
nsresult rv = GetInitArgs(cx, argc, argv, getter_AddRefs(prin),
getter_AddRefs(documentURI),
getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
} else {
// No arguments; use the subject principal
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
return NS_OK;
secMan->GetSubjectPrincipal(getter_AddRefs(prin));
// We're called from JS; there better be a subject principal, really.
NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
}
NS_ASSERTION(prin, "Must have principal by now");
if (!documentURI) {
// No explicit documentURI; grab document and base URIs off the window our
// constructor was called on. Error out if anything untoward happens.
// Note that this is a behavior change as far as I can tell -- we're now
// using the base URI and document URI of the window off of which the
// DOMParser is created, not the window in which parse*() is called.
// Does that matter?
// Also note that |cx| matches what GetDocumentFromContext() would return,
// while GetDocumentFromCaller() gives us the window that the DOMParser()
// call was made on.
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(nsContentUtils::GetDocumentFromCaller());
if (!doc) {
return NS_ERROR_UNEXPECTED;
}
baseURI = doc->GetBaseURI();
documentURI = doc->GetDocumentURI();
}
return Init(prin, documentURI, baseURI);
}
NS_IMETHODIMP
nsDOMParser::Init()
{
AttemptedInitMarker marker(&mAttemptedInit);
nsCOMPtr<nsIXPCNativeCallContext> ncc;
nsresult rv = nsContentUtils::XPConnect()->
GetCurrentNativeCallContext(getter_AddRefs(ncc));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(ncc, NS_ERROR_UNEXPECTED);
JSContext *cx = nsnull;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);
PRUint32 argc;
jsval *argv = nsnull;
ncc->GetArgc(&argc);
ncc->GetArgvPtr(&argv);
if (argc != 3) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIPrincipal> prin;
nsCOMPtr<nsIURI> documentURI;
nsCOMPtr<nsIURI> baseURI;
rv = GetInitArgs(cx, argc, argv, getter_AddRefs(prin),
getter_AddRefs(documentURI), getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
return Init(prin, documentURI, baseURI);
}

View File

@ -43,9 +43,12 @@
#include "nsIURI.h"
#include "nsIDOMLoadListener.h"
#include "nsWeakReference.h"
#include "nsIJSNativeInitializer.h"
class nsDOMParser : public nsIDOMParser,
public nsIDOMParserJS,
public nsIDOMLoadListener,
public nsIJSNativeInitializer,
public nsSupportsWeakReference
{
public:
@ -57,6 +60,9 @@ public:
// nsIDOMParser
NS_DECL_NSIDOMPARSER
// nsIDOMParserJS
NS_DECL_NSIDOMPARSERJS
// nsIDOMEventListener
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
@ -67,9 +73,31 @@ public:
NS_IMETHOD Abort(nsIDOMEvent* aEvent);
NS_IMETHOD Error(nsIDOMEvent* aEvent);
// nsIJSNativeInitializer
NS_IMETHOD Initialize(JSContext *cx, JSObject *obj,
PRUint32 argc, jsval *argv);
private:
class AttemptedInitMarker {
public:
AttemptedInitMarker(PRPackedBool* aAttemptedInit) :
mAttemptedInit(aAttemptedInit)
{}
~AttemptedInitMarker() {
*mAttemptedInit = PR_TRUE;
}
private:
PRPackedBool* mAttemptedInit;
};
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIURI> mDocumentURI;
nsCOMPtr<nsIURI> mBaseURI;
PRBool mLoopingForSyncLoad;
PRPackedBool mLoopingForSyncLoad;
PRPackedBool mAttemptedInit;
};
#endif

View File

@ -3050,6 +3050,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMParser, nsIDOMParser)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMParser)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMParserJS)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLSerializer, nsIDOMSerializer)

View File

@ -500,7 +500,9 @@ nsXFormsSubmissionElement::LoadReplaceInstance(nsIChannel *channel)
nsCOMPtr<nsIURI> uri;
nsresult rv = channel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
rv = parser->SetBaseURI(uri);
// XXXbz is this the right principal?
rv = parser->Init(nsnull, uri, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMDocument> newDoc;

View File

@ -200,7 +200,7 @@ int main (int argc, char* argv[])
&rv );
if (NS_SUCCEEDED( rv )) {
pDOMParser->SetBaseURI(pURI);
pDOMParser->Init(nsnull, pURI, nsnull);
rv = pDOMParser->ParseFromStream( pInputStream,
"UTF-8",

View File

@ -572,7 +572,8 @@ nsLivemarkLoadListener::TryParseAsSimpleRSS ()
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMDocument> xmldoc;
parser->SetBaseURI(mLivemark->feedURI);
// XXXbz is this the right principal?
parser->Init(nsnull, mLivemark->feedURI, nsnull);
rv = parser->ParseFromBuffer ((const PRUint8*) mBody.get(), mBody.Length(), "text/xml", getter_AddRefs(xmldoc));
if (NS_FAILED(rv)) return rv;