diff --git a/js/src/xpconnect/loader/Makefile.in b/js/src/xpconnect/loader/Makefile.in index 4f20301d66ce..def3717d2dec 100644 --- a/js/src/xpconnect/loader/Makefile.in +++ b/js/src/xpconnect/loader/Makefile.in @@ -47,7 +47,7 @@ FORCE_STATIC_LIB = 1 LIBXUL_LIBRARY = 1 LOCAL_INCLUDES += -I$(srcdir)/../src -CPPSRCS = mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp +CPPSRCS = mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp mozJSLoaderUtils.cpp EXTRA_JS_MODULES = XPCOMUtils.jsm ISO8601DateUtils.jsm diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index 8956a3612c25..143bc9f5e1cd 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -58,6 +58,7 @@ #include "nsIServiceManager.h" #include "nsISupports.h" #include "mozJSComponentLoader.h" +#include "mozJSLoaderUtils.h" #include "nsIJSRuntimeService.h" #include "nsIJSContextStack.h" #include "nsIXPConnect.h" @@ -386,108 +387,6 @@ ReportOnCaller(JSCLContextHelper &helper, return NS_OK; } -static nsresult -ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream, - JSObject **scriptObj) -{ - *scriptObj = nsnull; - - PRUint32 size; - nsresult rv = stream->Read32(&size); - NS_ENSURE_SUCCESS(rv, rv); - - char *data; - rv = stream->ReadBytes(size, &data); - NS_ENSURE_SUCCESS(rv, rv); - - JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE); - NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY); - - xdr->userdata = stream; - JS_XDRMemSetData(xdr, data, size); - - if (!JS_XDRScriptObject(xdr, scriptObj)) { - rv = NS_ERROR_FAILURE; - } - - // Update data in case ::JS_XDRScript called back into C++ code to - // read an XPCOM object. - // - // In that case, the serialization process must have flushed a run - // of counted bytes containing JS data at the point where the XPCOM - // object starts, after which an encoding C++ callback from the JS - // XDR code must have written the XPCOM object directly into the - // nsIObjectOutputStream. - // - // The deserialization process will XDR-decode counted bytes up to - // but not including the XPCOM object, then call back into C++ to - // read the object, then read more counted bytes and hand them off - // to the JSXDRState, so more JS data can be decoded. - // - // This interleaving of JS XDR data and XPCOM object data may occur - // several times beneath the call to ::JS_XDRScript, above. At the - // end of the day, we need to free (via nsMemory) the data owned by - // the JSXDRState. So we steal it back, nulling xdr's buffer so it - // doesn't get passed to ::JS_free by ::JS_XDRDestroy. - - uint32 length; - data = static_cast(JS_XDRMemGetData(xdr, &length)); - if (data) { - JS_XDRMemSetData(xdr, nsnull, 0); - } - JS_XDRDestroy(xdr); - - // If data is null now, it must have been freed while deserializing an - // XPCOM object (e.g., a principal) beneath ::JS_XDRScript. - if (data) { - nsMemory::Free(data); - } - - return rv; -} - -static nsresult -WriteScriptToStream(JSContext *cx, JSObject *scriptObj, - nsIObjectOutputStream *stream) -{ - JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); - NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY); - - xdr->userdata = stream; - nsresult rv = NS_OK; - - if (JS_XDRScriptObject(xdr, &scriptObj)) { - // Get the encoded JSXDRState data and write it. The JSXDRState owns - // this buffer memory and will free it beneath ::JS_XDRDestroy. - // - // If an XPCOM object needs to be written in the midst of the JS XDR - // encoding process, the C++ code called back from the JS engine (e.g., - // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data - // from the JSXDRState to aStream, then write the object, then return - // to JS XDR code with xdr reset so new JS data is encoded at the front - // of the xdr's data buffer. - // - // However many XPCOM objects are interleaved with JS XDR data in the - // stream, when control returns here from ::JS_XDRScript, we'll have - // one last buffer of data to write to aStream. - - uint32 size; - const char* data = reinterpret_cast - (JS_XDRMemGetData(xdr, &size)); - NS_ASSERTION(data, "no decoded JSXDRState data!"); - - rv = stream->Write32(size); - if (NS_SUCCEEDED(rv)) { - rv = stream->WriteBytes(data, size); - } - } else { - rv = NS_ERROR_FAILURE; // likely to be a principals serialization error - } - - JS_XDRDestroy(xdr); - return rv; -} - mozJSComponentLoader::mozJSComponentLoader() : mRuntime(nsnull), mContext(nsnull), @@ -849,66 +748,6 @@ class JSPrincipalsHolder JSPrincipals *mPrincipals; }; -/* static */ -nsresult -mozJSComponentLoader::ReadScript(StartupCache* cache, nsIURI *uri, - JSContext *cx, JSObject **scriptObj) -{ - nsresult rv; - - nsCAutoString spec(kJSCachePrefix); - rv = NS_PathifyURI(uri, spec); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoArrayPtr buf; - PRUint32 len; - rv = cache->GetBuffer(spec.get(), getter_Transfers(buf), - &len); - if (NS_FAILED(rv)) { - return rv; // don't warn since NOT_AVAILABLE is an ok error - } - - LOG(("Found %s in startupcache\n", spec.get())); - nsCOMPtr ois; - rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois)); - NS_ENSURE_SUCCESS(rv, rv); - buf.forget(); - - return ReadScriptFromStream(cx, ois, scriptObj); -} - -nsresult -mozJSComponentLoader::WriteScript(StartupCache* cache, JSObject *scriptObj, - nsIFile *component, nsIURI *uri, JSContext *cx) -{ - nsresult rv; - - nsCAutoString spec(kJSCachePrefix); - rv = NS_PathifyURI(uri, spec); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Writing %s to startupcache\n", spec.get())); - nsCOMPtr oos; - nsCOMPtr storageStream; - rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos), - getter_AddRefs(storageStream), - true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteScriptToStream(cx, scriptObj, oos); - oos->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoArrayPtr buf; - PRUint32 len; - rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf), - &len); - NS_ENSURE_SUCCESS(rv, rv); - - rv = cache->PutBuffer(spec.get(), buf, len); - return rv; -} - nsresult mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile, nsIURI *aURI, @@ -1015,8 +854,12 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile, PRBool writeToCache = PR_FALSE; StartupCache* cache = StartupCache::GetSingleton(); + nsCAutoString cachePath(kJSCachePrefix); + rv = NS_PathifyURI(aURI, cachePath); + NS_ENSURE_SUCCESS(rv, rv); + if (cache) { - rv = ReadScript(cache, aURI, cx, &scriptObj); + rv = ReadCachedScript(cache, cachePath, cx, &scriptObj); if (NS_SUCCEEDED(rv)) { LOG(("Successfully loaded %s from startupcache\n", nativePath.get())); } else { @@ -1170,7 +1013,7 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile, if (writeToCache) { // We successfully compiled the script, so cache it. - rv = WriteScript(cache, scriptObj, aComponentFile, aURI, cx); + rv = WriteCachedScript(cache, cachePath, cx, scriptObj); // Don't treat failure to write as fatal, since we might be working // with a read-only cache. diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.h b/js/src/xpconnect/loader/mozJSComponentLoader.h index 77d4ae5f485a..5162a9749d44 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.h +++ b/js/src/xpconnect/loader/mozJSComponentLoader.h @@ -106,11 +106,6 @@ class mozJSComponentLoader : public mozilla::ModuleLoader, char **location, jsval *exception); - nsresult ReadScript(StartupCache *cache, nsIURI *uri, - JSContext *cx, JSObject **scriptObj); - nsresult WriteScript(StartupCache *cache, JSObject *scriptObj, - nsIFile *component, nsIURI *uri, JSContext *cx); - nsCOMPtr mCompMgr; nsCOMPtr mRuntimeService; nsCOMPtr mContextStack; diff --git a/js/src/xpconnect/loader/mozJSLoaderUtils.cpp b/js/src/xpconnect/loader/mozJSLoaderUtils.cpp new file mode 100644 index 000000000000..edd545b6548b --- /dev/null +++ b/js/src/xpconnect/loader/mozJSLoaderUtils.cpp @@ -0,0 +1,196 @@ +/* ***** 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 + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010-2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Michael Wu + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 ***** */ + +#if !defined(XPCONNECT_STANDALONE) + +#include "nsAutoPtr.h" +#include "nsScriptLoader.h" + +#include "jsapi.h" +#include "jsxdrapi.h" + +#include "mozilla/scache/StartupCache.h" +#include "mozilla/scache/StartupCacheUtils.h" + +using namespace mozilla::scache; + +static nsresult +ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream, + JSObject **scriptObj) +{ + *scriptObj = nsnull; + + PRUint32 size; + nsresult rv = stream->Read32(&size); + NS_ENSURE_SUCCESS(rv, rv); + + char *data; + rv = stream->ReadBytes(size, &data); + NS_ENSURE_SUCCESS(rv, rv); + + JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY); + + xdr->userdata = stream; + JS_XDRMemSetData(xdr, data, size); + + if (!JS_XDRScriptObject(xdr, scriptObj)) { + rv = NS_ERROR_FAILURE; + } + + // Update data in case ::JS_XDRScript called back into C++ code to + // read an XPCOM object. + // + // In that case, the serialization process must have flushed a run + // of counted bytes containing JS data at the point where the XPCOM + // object starts, after which an encoding C++ callback from the JS + // XDR code must have written the XPCOM object directly into the + // nsIObjectOutputStream. + // + // The deserialization process will XDR-decode counted bytes up to + // but not including the XPCOM object, then call back into C++ to + // read the object, then read more counted bytes and hand them off + // to the JSXDRState, so more JS data can be decoded. + // + // This interleaving of JS XDR data and XPCOM object data may occur + // several times beneath the call to ::JS_XDRScript, above. At the + // end of the day, we need to free (via nsMemory) the data owned by + // the JSXDRState. So we steal it back, nulling xdr's buffer so it + // doesn't get passed to ::JS_free by ::JS_XDRDestroy. + + uint32 length; + data = static_cast(JS_XDRMemGetData(xdr, &length)); + JS_XDRMemSetData(xdr, nsnull, 0); + JS_XDRDestroy(xdr); + + // If data is null now, it must have been freed while deserializing an + // XPCOM object (e.g., a principal) beneath ::JS_XDRScript. + nsMemory::Free(data); + + return rv; +} + +static nsresult +WriteScriptToStream(JSContext *cx, JSObject *scriptObj, + nsIObjectOutputStream *stream) +{ + JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); + NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY); + + xdr->userdata = stream; + nsresult rv = NS_OK; + + if (JS_XDRScriptObject(xdr, &scriptObj)) { + // Get the encoded JSXDRState data and write it. The JSXDRState owns + // this buffer memory and will free it beneath ::JS_XDRDestroy. + // + // If an XPCOM object needs to be written in the midst of the JS XDR + // encoding process, the C++ code called back from the JS engine (e.g., + // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data + // from the JSXDRState to aStream, then write the object, then return + // to JS XDR code with xdr reset so new JS data is encoded at the front + // of the xdr's data buffer. + // + // However many XPCOM objects are interleaved with JS XDR data in the + // stream, when control returns here from ::JS_XDRScript, we'll have + // one last buffer of data to write to aStream. + + uint32 size; + const char* data = reinterpret_cast + (JS_XDRMemGetData(xdr, &size)); + NS_ASSERTION(data, "no decoded JSXDRState data!"); + + rv = stream->Write32(size); + if (NS_SUCCEEDED(rv)) { + rv = stream->WriteBytes(data, size); + } + } else { + rv = NS_ERROR_FAILURE; // likely to be a principals serialization error + } + + JS_XDRDestroy(xdr); + return rv; +} + +nsresult +ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSObject **scriptObj) +{ + nsresult rv; + + nsAutoArrayPtr buf; + PRUint32 len; + rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf), + &len); + if (NS_FAILED(rv)) { + return rv; // don't warn since NOT_AVAILABLE is an ok error + } + + nsCOMPtr ois; + rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois)); + NS_ENSURE_SUCCESS(rv, rv); + buf.forget(); + + return ReadScriptFromStream(cx, ois, scriptObj); +} + +nsresult +WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSObject *scriptObj) +{ + nsresult rv; + + nsCOMPtr oos; + nsCOMPtr storageStream; + rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos), + getter_AddRefs(storageStream), + true); + NS_ENSURE_SUCCESS(rv, rv); + + rv = WriteScriptToStream(cx, scriptObj, oos); + oos->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoArrayPtr buf; + PRUint32 len; + rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf), + &len); + NS_ENSURE_SUCCESS(rv, rv); + + rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len); + return rv; +} + +#endif /* XPCONNECT_STANDALONE */ diff --git a/js/src/xpconnect/loader/mozJSLoaderUtils.h b/js/src/xpconnect/loader/mozJSLoaderUtils.h new file mode 100644 index 000000000000..5081f267925c --- /dev/null +++ b/js/src/xpconnect/loader/mozJSLoaderUtils.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** 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. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Michael Wu + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 mozJSLoaderUtils_h +#define mozJSLoaderUtils_h + +#include "nsString.h" +#include "jsapi.h" + +class nsIURI; +namespace mozilla { +namespace scache { +class StartupCache; +} +} + +nsresult +ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri, + JSContext *cx, JSObject **scriptObj); + +nsresult +WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri, + JSContext *cx, JSObject *scriptObj); +#endif /* mozJSLoaderUtils_h */ diff --git a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp index 63e070ba229f..0f9dd6468103 100644 --- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -41,6 +41,7 @@ * ***** END LICENSE BLOCK ***** */ #include "mozJSSubScriptLoader.h" +#include "mozJSLoaderUtils.h" #include "nsIServiceManager.h" #include "nsIXPConnect.h" @@ -58,10 +59,15 @@ #include "nsScriptLoader.h" #include "jsapi.h" +#include "jscntxt.h" #include "jsdbgapi.h" #include "jsfriendapi.h" #include "mozilla/FunctionTimer.h" +#include "mozilla/scache/StartupCache.h" +#include "mozilla/scache/StartupCacheUtils.h" + +using namespace mozilla::scache; /* load() error msgs, XXX localize? */ #define LOAD_ERROR_NOSERVICE "Error creating IO Service." @@ -91,6 +97,89 @@ mozJSSubScriptLoader::~mozJSSubScriptLoader() NS_IMPL_THREADSAFE_ISUPPORTS1(mozJSSubScriptLoader, mozIJSSubScriptLoader) +static nsresult +ReportError(JSContext *cx, const char *msg) +{ + JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg))); + return NS_OK; +} + +nsresult +mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj, + jschar *charset, const char *uriStr, + nsIIOService *serv, JSObject **scriptObjp) +{ + nsCOMPtr chan; + nsCOMPtr instream; + JSPrincipals *jsPrincipals; + JSErrorReporter er; + + nsresult rv; + // Instead of calling NS_OpenURI, we create the channel ourselves and call + // SetContentType, to avoid expensive MIME type lookups (bug 632490). + rv = NS_NewChannel(getter_AddRefs(chan), uri, serv, + nsnull, nsnull, nsIRequest::LOAD_NORMAL); + if (NS_SUCCEEDED(rv)) { + chan->SetContentType(NS_LITERAL_CSTRING("application/javascript")); + rv = chan->Open(getter_AddRefs(instream)); + } + + if (NS_FAILED(rv)) { + return ReportError(cx, LOAD_ERROR_NOSTREAM); + } + + PRInt32 len = -1; + + rv = chan->GetContentLength(&len); + if (NS_FAILED(rv) || len == -1) { + return ReportError(cx, LOAD_ERROR_NOCONTENT); + } + + nsCString buf; + rv = NS_ReadInputStreamToString(instream, buf, len); + if (NS_FAILED(rv)) + return rv; + + /* we can't hold onto jsPrincipals as a module var because the + * JSPRINCIPALS_DROP macro takes a JSContext, which we won't have in the + * destructor */ + rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals); + if (NS_FAILED(rv) || !jsPrincipals) { + return ReportError(cx, LOAD_ERROR_NOPRINCIPALS); + } + + /* set our own error reporter so we can report any bad things as catchable + * exceptions, including the source/line number */ + er = JS_SetErrorReporter(cx, mozJSLoaderErrorReporter); + + if (charset) { + nsString script; + rv = nsScriptLoader::ConvertToUTF16( + nsnull, reinterpret_cast(buf.get()), len, + nsDependentString(reinterpret_cast(charset)), nsnull, script); + + if (NS_FAILED(rv)) { + JSPRINCIPALS_DROP(cx, jsPrincipals); + return ReportError(cx, LOAD_ERROR_BADCHARSET); + } + + *scriptObjp = + JS_CompileUCScriptForPrincipals(cx, target_obj, jsPrincipals, + reinterpret_cast(script.get()), + script.Length(), uriStr, 1); + } else { + *scriptObjp = JS_CompileScriptForPrincipals(cx, target_obj, jsPrincipals, buf.get(), + len, uriStr, 1); + } + + JSPRINCIPALS_DROP(cx, jsPrincipals); + + /* repent for our evil deeds */ + JS_SetErrorReporter(cx, er); + + return NS_OK; +} + NS_IMETHODIMP /* args and return value are delt with using XPConnect and JSAPI */ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL /* [, JSObject *target_obj] */) @@ -227,17 +316,6 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL /* load up the url. From here on, failures are reflected as ``custom'' * js exceptions */ - PRInt32 len = -1; - PRUint32 readcount = 0; // Total amount of data read - PRUint32 lastReadCount = 0; // Amount of data read in last Read() call - nsAutoArrayPtr buf; - - JSString *errmsg; - JSErrorReporter er; - JSPrincipals *jsPrincipals; - - nsCOMPtr chan; - nsCOMPtr instream; nsCOMPtr uri; nsCAutoString uriStr; nsCAutoString scheme; @@ -261,32 +339,27 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL return NS_ERROR_FAILURE; } + StartupCache* cache = StartupCache::GetSingleton(); nsCOMPtr serv = do_GetService(NS_IOSERVICE_CONTRACTID); - if (!serv) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSERVICE); - goto return_exception; + if (!serv) { + return ReportError(cx, LOAD_ERROR_NOSERVICE); } // Make sure to explicitly create the URI, since we'll need the // canonicalized spec. rv = NS_NewURI(getter_AddRefs(uri), urlbytes.ptr(), nsnull, serv); if (NS_FAILED(rv)) { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOURI); - goto return_exception; + return ReportError(cx, LOAD_ERROR_NOURI); } rv = uri->GetSpec(uriStr); if (NS_FAILED(rv)) { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSPEC); - goto return_exception; + return ReportError(cx, LOAD_ERROR_NOSPEC); } rv = uri->GetScheme(scheme); - if (NS_FAILED(rv)) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSCHEME); - goto return_exception; + if (NS_FAILED(rv)) { + return ReportError(cx, LOAD_ERROR_NOSCHEME); } if (!scheme.EqualsLiteral("chrome")) @@ -294,10 +367,8 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL // This might be a URI to a local file, though! nsCOMPtr innerURI = NS_GetInnermostURI(uri); nsCOMPtr fileURL = do_QueryInterface(innerURI); - if (!fileURL) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_URI_NOT_LOCAL); - goto return_exception; + if (!fileURL) { + return ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL); } // For file URIs prepend the filename with the filename of the @@ -309,104 +380,38 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL uriStr = tmp; } - // Instead of calling NS_OpenURI, we create the channel ourselves and call - // SetContentType, to avoid expensive MIME type lookups (bug 632490). - rv = NS_NewChannel(getter_AddRefs(chan), uri, serv, - nsnull, nsnull, nsIRequest::LOAD_NORMAL); - if (NS_SUCCEEDED(rv)) - { - chan->SetContentType(NS_LITERAL_CSTRING("application/javascript")); - rv = chan->Open(getter_AddRefs(instream)); + bool writeScript = false; + JSObject *scriptObj = nsnull; + JSVersion version = cx->findVersion(); + nsCAutoString cachePath; + cachePath.AppendPrintf("jssubloader/%d", version); + NS_PathifyURI(uri, cachePath); + + if (cache) + rv = ReadCachedScript(cache, cachePath, cx, &scriptObj); + if (!scriptObj) { + rv = ReadScript(uri, cx, target_obj, charset, (char *)uriStr.get(), serv, &scriptObj); + writeScript = true; } - if (NS_FAILED(rv)) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSTREAM); - goto return_exception; - } - - rv = chan->GetContentLength (&len); - if (NS_FAILED(rv) || len == -1) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOCONTENT); - goto return_exception; - } + if (NS_FAILED(rv) || !scriptObj) + return rv; - buf = new char[len + 1]; - if (!buf) - return NS_ERROR_OUT_OF_MEMORY; - buf[len] = '\0'; - - do { - rv = instream->Read (buf + readcount, len - readcount, &lastReadCount); - if (NS_FAILED(rv)) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_BADREAD); - goto return_exception; - } - readcount += lastReadCount; - } while (lastReadCount && readcount != PRUint32(len)); - - if (static_cast(len) != readcount) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_READUNDERFLOW); - goto return_exception; - } + ok = false; + if (scriptObj) + ok = JS_ExecuteScriptVersion(cx, target_obj, scriptObj, rval, version); - /* we can't hold onto jsPrincipals as a module var because the - * JSPRINCIPALS_DROP macro takes a JSContext, which we won't have in the - * destructor */ - rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals); - if (NS_FAILED(rv) || !jsPrincipals) - { - errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOPRINCIPALS); - goto return_exception; - } - - /* set our own error reporter so we can report any bad things as catchable - * exceptions, including the source/line number */ - er = JS_SetErrorReporter (cx, mozJSLoaderErrorReporter); - - if (charset) - { - nsString script; - rv = nsScriptLoader::ConvertToUTF16( - nsnull, reinterpret_cast(buf.get()), len, - nsDependentString(reinterpret_cast(charset)), nsnull, script); - - if (NS_FAILED(rv)) - { - JSPRINCIPALS_DROP(cx, jsPrincipals); - errmsg = JS_NewStringCopyZ(cx, LOAD_ERROR_BADCHARSET); - goto return_exception; - } - ok = JS_EvaluateUCScriptForPrincipals(cx, target_obj, jsPrincipals, - reinterpret_cast(script.get()), - script.Length(), uriStr.get(), 1, rval); - } - else - { - ok = JS_EvaluateScriptForPrincipals(cx, target_obj, jsPrincipals, - buf, len, uriStr.get(), 1, rval); - } - - JSPRINCIPALS_DROP(cx, jsPrincipals); - - if (ok) - { + if (ok) { JSAutoEnterCompartment rac; - if (!rac.enter(cx, result_obj) || !JS_WrapValue(cx, rval)) return NS_ERROR_UNEXPECTED; } - /* repent for our evil deeds */ - JS_SetErrorReporter (cx, er); + if (cache && ok && writeScript) { + WriteCachedScript(cache, cachePath, cx, scriptObj); + } cc->SetReturnValueWasSet (ok); return NS_OK; - - return_exception: - JS_SetPendingException (cx, STRING_TO_JSVAL(errmsg)); - return NS_OK; } + diff --git a/js/src/xpconnect/loader/mozJSSubScriptLoader.h b/js/src/xpconnect/loader/mozJSSubScriptLoader.h index c4ded258673e..2083d933eb12 100644 --- a/js/src/xpconnect/loader/mozJSSubScriptLoader.h +++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.h @@ -38,6 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "jsapi.h" #include "nsCOMPtr.h" #include "mozIJSSubScriptLoader.h" #include "nsIScriptSecurityManager.h" @@ -50,6 +51,8 @@ {0x8e, 0x08, 0x82, 0xfa, 0x0a, 0x33, 0x9b, 0x00} \ } +class nsIIOService; + class mozJSSubScriptLoader : public mozIJSSubScriptLoader { public: @@ -61,6 +64,9 @@ public: NS_DECL_MOZIJSSUBSCRIPTLOADER private: + nsresult ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj, + jschar *charset, const char *uriStr, + nsIIOService *serv, JSObject **scriptObjp); + nsCOMPtr mSystemPrincipal; - };