зеркало из https://github.com/mozilla/pjs.git
1) Now evaluting the script during channel creation rather than read() to
avoid a deadlock between the UI/JS thread and the FileTransport thread. 2) Isolating the proxy of the eval to a single interface 3) Change makefiles for windows and unix. Brendan asked me to look at this. I am not sure if there is a bug number. reviewer=brendan@meer.net
This commit is contained in:
Родитель
e15d631923
Коммит
2ab5823b4f
|
@ -22,10 +22,14 @@ VPATH = @srcdir@
|
|||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = dom
|
||||
MODULE = jsurl
|
||||
LIBRARY_NAME = jsurl
|
||||
IS_COMPONENT = 1
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsIEvaluateStringProxy.idl \
|
||||
$(NULL)
|
||||
|
||||
REQUIRES = netwerk xpcom
|
||||
|
||||
CPPSRCS = nsJSProtocolHandler.cpp
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# Reserved.
|
||||
|
||||
DEPTH=..\..\..
|
||||
MODULE=dom
|
||||
MODULE=jsurl
|
||||
include <$(DEPTH)/config/config.mak>
|
||||
|
||||
IS_COMPONENT=1
|
||||
|
@ -53,6 +53,10 @@ CPPSRCS = \
|
|||
nsJSProtocolHandler.cpp \
|
||||
$(NULL)
|
||||
|
||||
XPIDLSRCS = \
|
||||
.\nsIEvaluateStringProxy.idl \
|
||||
$(NULL)
|
||||
|
||||
|
||||
REQUIRES=netwerk
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPrincipal;
|
||||
interface nsIScriptContext;
|
||||
|
||||
[uuid(67411da0-7c2e-11d3-8c67-00609792278c)]
|
||||
interface nsIEvaluateStringProxy: nsISupports
|
||||
{
|
||||
|
||||
void evaluateString( in nsIScriptContext scriptContext,
|
||||
in string script,
|
||||
in voidStar aObj,
|
||||
in nsIPrincipal principal,
|
||||
in string aURL,
|
||||
in long aLineNo,
|
||||
out string aRetValue,
|
||||
out boolean aIsUndefined);
|
||||
};
|
||||
|
|
@ -15,9 +15,8 @@
|
|||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifdef NECKO
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "jsapi.h"
|
||||
#include "prmem.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIComponentManager.h"
|
||||
|
@ -25,16 +24,17 @@
|
|||
#include "nsIServiceManager.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIStringStream.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIEventSinkGetter.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptContextOwner.h"
|
||||
#include "nsJSProtocolHandler.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsProxyObjectManager.h"
|
||||
|
||||
#include "nsIEvaluateStringProxy.h"
|
||||
|
||||
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
|
||||
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
||||
|
@ -43,176 +43,55 @@ static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
|||
static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
|
||||
static NS_DEFINE_CID(kJSProtocolHandlerCID, NS_JSPROTOCOLHANDLER_CID);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class nsJSInputStream : public nsIInputStream
|
||||
/***************************************************************************/
|
||||
/* nsEvaluateStringProxy */
|
||||
/* This private class will allow us to evaluate js on another thread */
|
||||
/***************************************************************************/
|
||||
class nsEvaluateStringProxy : public nsIEvaluateStringProxy
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsJSInputStream()
|
||||
: mVerb(nsnull), mURI(nsnull), mEventSinkGetter(nsnull),
|
||||
mResult(nsnull), mLength(0), mReadCursor(0), mPrincipal(nsnull) {
|
||||
NS_INIT_REFCNT();
|
||||
}
|
||||
|
||||
virtual ~nsJSInputStream() {
|
||||
if (mVerb)
|
||||
nsCRT::free(mVerb);
|
||||
NS_IF_RELEASE(mURI);
|
||||
NS_IF_RELEASE(mEventSinkGetter);
|
||||
if (mResult)
|
||||
nsCRT::free(mResult);
|
||||
NS_IF_RELEASE(mPrincipal);
|
||||
}
|
||||
|
||||
nsresult Init(const char* verb, nsIURI* uri,
|
||||
nsIEventSinkGetter* eventSinkGetter) {
|
||||
mVerb = nsCRT::strdup(verb); // XXX do we need this?
|
||||
if (!mVerb)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mURI = uri;
|
||||
NS_ADDREF(mURI);
|
||||
mEventSinkGetter = eventSinkGetter;
|
||||
NS_IF_ADDREF(mEventSinkGetter);
|
||||
|
||||
// Get principal of currently executing code, save for execution
|
||||
nsresult result;
|
||||
NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
|
||||
NS_SCRIPTSECURITYMANAGER_PROGID, &result);
|
||||
if (NS_FAILED(result))
|
||||
return NS_ERROR_FAILURE;
|
||||
PRBool hasPrincipal;
|
||||
if (NS_FAILED(securityManager->HasSubjectPrincipal(&hasPrincipal)))
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!hasPrincipal) {
|
||||
// Must be loaded as a result of a user action on a HTML element
|
||||
// with a javascript: HREF attribute
|
||||
// TODO: need to find URI of enclosing page
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_FAILED(securityManager->GetSubjectPrincipal(&mPrincipal)))
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Close() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Available(PRUint32 *_retval) {
|
||||
if (!mResult) {
|
||||
nsresult rv = Eval();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
NS_ASSERTION(mResult, "Eval didn't set mResult");
|
||||
}
|
||||
PRUint32 rest = mLength - mReadCursor;
|
||||
if (!rest)
|
||||
return NS_ERROR_FAILURE;
|
||||
*_retval = rest;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Read(char * buf, PRUint32 count, PRUint32 *_retval) {
|
||||
nsresult rv;
|
||||
PRUint32 rest;
|
||||
rv = Available(&rest);
|
||||
if (rv != NS_OK)
|
||||
return rv;
|
||||
PRUint32 amt = PR_MIN(count, rest);
|
||||
nsCRT::memcpy(buf, &mResult[mReadCursor], amt);
|
||||
mReadCursor += amt;
|
||||
*_retval = amt;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Eval() {
|
||||
nsresult rv;
|
||||
|
||||
if (!mEventSinkGetter)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
//TODO Change this to GetSpec and then skip past the javascript:
|
||||
// Get the expression:
|
||||
char* jsExpr;
|
||||
rv = mURI->GetPath(&jsExpr);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// The event sink must be a script context owner or we fail.
|
||||
nsIScriptContextOwner* owner;
|
||||
rv = mEventSinkGetter->GetEventSink(mVerb,
|
||||
NS_GET_IID(nsIScriptContextOwner),
|
||||
(nsISupports**)&owner);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
if (!owner)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// So far so good: get the script context from its owner.
|
||||
nsIScriptContext* scriptContext;
|
||||
rv = owner->GetScriptContext(&scriptContext);
|
||||
NS_RELEASE(owner);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Finally, we have everything needed to evaluate the expression.
|
||||
nsAutoString ret;
|
||||
PRBool isUndefined;
|
||||
rv = scriptContext->EvaluateString(nsString(jsExpr), nsnull,
|
||||
mPrincipal, nsnull, 0, ret,
|
||||
&isUndefined);
|
||||
nsCRT::free(jsExpr);
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
} else {
|
||||
// Find out if it can be converted into a string
|
||||
if (isUndefined) {
|
||||
mResult = nsCRT::strdup("");
|
||||
} else {
|
||||
#if 0 // plaintext is apparently busted
|
||||
if (ret[0] != PRUnichar('<'))
|
||||
ret.Insert("<plaintext>\n", 0);
|
||||
#endif
|
||||
mLength = ret.Length();
|
||||
PRUint32 resultSize = mLength + 1;
|
||||
mResult = (char *)PR_Malloc(resultSize);
|
||||
ret.ToCString(mResult, resultSize);
|
||||
}
|
||||
}
|
||||
NS_RELEASE(scriptContext);
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected:
|
||||
char* mVerb;
|
||||
nsIURI* mURI;
|
||||
nsIEventSinkGetter* mEventSinkGetter;
|
||||
char* mResult;
|
||||
PRUint32 mLength;
|
||||
PRUint32 mReadCursor;
|
||||
nsIPrincipal* mPrincipal;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVALUATESTRINGPROXY
|
||||
|
||||
nsEvaluateStringProxy();
|
||||
virtual ~nsEvaluateStringProxy();
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsJSInputStream, NS_GET_IID(nsIInputStream));
|
||||
|
||||
nsresult
|
||||
NS_NewJSInputStream(const char* verb, nsIURI* uri,
|
||||
nsIEventSinkGetter* eventSinkGetter,
|
||||
nsIInputStream* *result)
|
||||
nsEvaluateStringProxy::nsEvaluateStringProxy()
|
||||
{
|
||||
nsJSInputStream* js = new nsJSInputStream();
|
||||
if (!js)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(js);
|
||||
NS_INIT_REFCNT();
|
||||
NS_ADDREF_THIS();
|
||||
}
|
||||
|
||||
nsEvaluateStringProxy::~nsEvaluateStringProxy()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsEvaluateStringProxy, nsIEvaluateStringProxy)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEvaluateStringProxy::EvaluateString(nsIScriptContext *scriptContext,
|
||||
const char *script,
|
||||
void * aObj,
|
||||
nsIPrincipal *principal,
|
||||
const char *aURL,
|
||||
PRInt32 aLineNo,
|
||||
char **aRetValue,
|
||||
PRBool *aIsUndefined)
|
||||
{
|
||||
nsString string;
|
||||
nsresult rv;
|
||||
|
||||
rv = scriptContext->EvaluateString( nsString(script),
|
||||
aObj,
|
||||
principal,
|
||||
aURL,
|
||||
aLineNo,
|
||||
string,
|
||||
aIsUndefined);
|
||||
*aRetValue = string.ToNewCString();
|
||||
|
||||
nsresult rv = js->Init(verb, uri, eventSinkGetter);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(js);
|
||||
return rv;
|
||||
}
|
||||
*result = js;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -323,28 +202,165 @@ nsJSProtocolHandler::NewChannel(const char* verb, nsIURI* uri,
|
|||
nsIEventSinkGetter* eventSinkGetter,
|
||||
nsIChannel* *result)
|
||||
{
|
||||
// Strategy: Let's use an "input stream channel" here with a special
|
||||
// implementation of nsIInputStream that evaluates the JS expression.
|
||||
// That seems simpler than implementing a new channel class.
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
NS_ENSURE_ARG_POINTER(eventSinkGetter);
|
||||
|
||||
nsresult rv;
|
||||
nsIChannel* channel;
|
||||
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
|
||||
|
||||
// The event sink must be a script context owner or we fail.
|
||||
nsIScriptContextOwner* owner;
|
||||
rv = eventSinkGetter->GetEventSink(verb,
|
||||
NS_GET_IID(nsIScriptContextOwner),
|
||||
(nsISupports**)&owner);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!owner)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// So far so good: get the script context from its owner.
|
||||
nsCOMPtr<nsIScriptContext> scriptContext;
|
||||
rv = owner->GetScriptContext(getter_AddRefs(scriptContext));
|
||||
NS_RELEASE(owner);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIInputStream* in;
|
||||
rv = NS_NewJSInputStream(verb, uri, eventSinkGetter, &in);
|
||||
// Get principal of currently executing code, save for execution
|
||||
|
||||
NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
|
||||
NS_SCRIPTSECURITYMANAGER_PROGID, &rv);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRBool hasPrincipal;
|
||||
if (NS_FAILED(securityManager->HasSubjectPrincipal(&hasPrincipal)))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (!hasPrincipal)
|
||||
{
|
||||
// Must be loaded as a result of a user action on a HTML element
|
||||
// with a javascript: HREF attribute
|
||||
// TODO: need to find URI of enclosing page
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
|
||||
if (NS_FAILED(securityManager->GetSubjectPrincipal(getter_AddRefs(principal))))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
//TODO Change this to GetSpec and then skip past the javascript:
|
||||
// Get the expression:
|
||||
|
||||
char* str;
|
||||
rv = uri->GetPath(&str);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsString jsExpr(str);
|
||||
|
||||
nsCRT::free(str);
|
||||
|
||||
// Finally, we have everything needed to evaluate the expression.
|
||||
// We do this by proxying back to the main thread.
|
||||
|
||||
PRBool isUndefined;
|
||||
|
||||
NS_WITH_SERVICE(nsIProxyObjectManager, proxyObjectManager,
|
||||
nsIProxyObjectManager::GetCID(), &rv);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIEvaluateStringProxy* evalProxy = nsnull;
|
||||
nsEvaluateStringProxy* eval = new nsEvaluateStringProxy();
|
||||
|
||||
if (eval == nsnull)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
rv = proxyObjectManager->GetProxyObject(nsnull,
|
||||
nsCOMTypeInfo<nsIEvaluateStringProxy>::GetIID(),
|
||||
NS_STATIC_CAST(nsISupports*, eval),
|
||||
PROXY_SYNC | PROXY_ALWAYS,
|
||||
(void**) &evalProxy);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
NS_RELEASE(eval);
|
||||
return rv;
|
||||
}
|
||||
|
||||
char* retString;
|
||||
char* tempString = jsExpr.ToNewCString();
|
||||
if (!tempString)
|
||||
{
|
||||
NS_RELEASE(eval);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
rv = evalProxy->EvaluateString(scriptContext, tempString, nsnull, principal, nsnull, 0, &retString, &isUndefined);
|
||||
|
||||
Recycle(tempString);
|
||||
|
||||
NS_RELEASE(evalProxy);
|
||||
NS_RELEASE(eval);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (isUndefined)
|
||||
{
|
||||
strcpy( retString, "" );
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
{
|
||||
// This is from the old code which need to be hack on a bit more:
|
||||
|
||||
#if 0 // plaintext is apparently busted
|
||||
if (ret[0] != PRUnichar('<'))
|
||||
ret.Insert("<plaintext>\n", 0);
|
||||
#endif
|
||||
mLength = ret.Length();
|
||||
PRUint32 resultSize = mLength + 1;
|
||||
mResult = (char *)PR_Malloc(resultSize);
|
||||
ret.ToCString(mResult, resultSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
if (retString)
|
||||
Recycle(retString);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> s;
|
||||
rv = NS_NewStringInputStream(getter_AddRefs(s), retString);
|
||||
Recycle(retString);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> in = do_QueryInterface(s, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsIChannel* channel;
|
||||
|
||||
rv = serv->NewInputStreamChannel(uri, "text/html",
|
||||
-1, // XXX need contentLength -- implies that the evaluation should happen here, not in Read
|
||||
in, aGroup, &channel);
|
||||
NS_RELEASE(in);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
*result = channel;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -385,6 +401,16 @@ NSRegisterSelf(nsISupports* aServMgr, const char* aPath)
|
|||
"JavaScript Protocol Handler",
|
||||
NS_NETWORK_PROTOCOL_PROGID_PREFIX "javascript",
|
||||
aPath, PR_TRUE, PR_TRUE);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = compMgr->RegisterComponent(kJSProtocolHandlerCID,
|
||||
"JavaScript Protocol Handler",
|
||||
NS_NETWORK_PROTOCOL_PROGID_PREFIX "javascript",
|
||||
aPath, PR_TRUE, PR_TRUE);
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -402,4 +428,3 @@ NSUnregisterSelf(nsISupports* aServMgr, const char* aPath)
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#endif // NECKO
|
||||
|
|
Загрузка…
Ссылка в новой задаче