diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 2356a7eda4a0..85f98a7b5ba8 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -76,12 +76,21 @@ public: mLoading(true), mIsInline(true), mHasSourceMapURL(false), + mScriptTextBuf(nullptr), + mScriptTextLength(0), mJSVersion(aVersion), mLineNo(1), mCORSMode(aCORSMode) { } + ~nsScriptLoadRequest() + { + if (mScriptTextBuf) { + js_free(mScriptTextBuf); + } + } + NS_DECL_THREADSAFE_ISUPPORTS void FireScriptAvailable(nsresult aResult) @@ -103,7 +112,8 @@ public: bool mIsInline; // Is the script inline or loaded? bool mHasSourceMapURL; // Does the HTTP header have a source map url? nsString mSourceMapURL; // Holds source map url for loaded scripts - nsString mScriptText; // Holds script for text loaded scripts + jschar* mScriptTextBuf; // Holds script text for non-inline scripts. Don't + size_t mScriptTextLength; // use nsString so we can give ownership to jsapi. uint32_t mJSVersion; nsCOMPtr mURI; nsCOMPtr mOriginPrincipal; @@ -851,7 +861,7 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest) JS::CompileOptions options(cx); FillCompileOptionsForRequest(aRequest, global, &options); - if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.Length())) { + if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) { return NS_ERROR_FAILURE; } @@ -859,7 +869,7 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest) new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this); if (!JS::CompileOffThread(cx, options, - aRequest->mScriptText.get(), aRequest->mScriptText.Length(), + aRequest->mScriptTextBuf, aRequest->mScriptTextLength, OffThreadScriptLoaderCallback, static_cast(runnable))) { return NS_ERROR_OUT_OF_MEMORY; @@ -884,8 +894,11 @@ nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest, void **aOffThreadT } NS_ENSURE_ARG(aRequest); - nsAFlatString* script; nsAutoString textData; + const jschar* scriptBuf = nullptr; + size_t scriptLength = 0; + JS::SourceBufferHolder::Ownership giveScriptOwnership = + JS::SourceBufferHolder::NoOwnership; nsCOMPtr doc; @@ -897,14 +910,23 @@ nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest, void **aOffThreadT // copies. aRequest->mElement->GetScriptText(textData); - script = &textData; + scriptBuf = textData.get(); + scriptLength = textData.Length(); + giveScriptOwnership = JS::SourceBufferHolder::NoOwnership; } else { - script = &aRequest->mScriptText; + scriptBuf = aRequest->mScriptTextBuf; + scriptLength = aRequest->mScriptTextLength; + + giveScriptOwnership = JS::SourceBufferHolder::GiveOwnership; + aRequest->mScriptTextBuf = nullptr; + aRequest->mScriptTextLength = 0; doc = scriptElem->OwnerDoc(); } + JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, giveScriptOwnership); + nsCOMPtr oldParserInsertedScript; uint32_t parserCreated = aRequest->mElement->GetParserCreated(); if (parserCreated) { @@ -937,7 +959,7 @@ nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest, void **aOffThreadT doc->BeginEvaluatingExternalScript(); } aRequest->mElement->BeginEvaluating(); - rv = EvaluateScript(aRequest, *script, aOffThreadToken); + rv = EvaluateScript(aRequest, srcBuf, aOffThreadToken); aRequest->mElement->EndEvaluating(); if (doc) { doc->EndEvaluatingExternalScript(); @@ -1044,7 +1066,7 @@ nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest, nsresult nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest, - const nsAFlatString& aScript, + JS::SourceBufferHolder& aSrcBuf, void** aOffThreadToken) { // We need a document to evaluate scripts. @@ -1095,7 +1117,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest, JS::CompileOptions options(entryScript.cx()); FillCompileOptionsForRequest(aRequest, global, &options); - nsresult rv = nsJSUtils::EvaluateString(entryScript.cx(), aScript, global, options, + nsresult rv = nsJSUtils::EvaluateString(entryScript.cx(), aSrcBuf, global, options, aOffThreadToken); // Put the old script back in case it wants to do anything else. @@ -1246,10 +1268,12 @@ DetectByteOrderMark(const unsigned char* aBytes, int32_t aLen, nsCString& oChars /* static */ nsresult nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, uint32_t aLength, const nsAString& aHintCharset, - nsIDocument* aDocument, nsString& aString) + nsIDocument* aDocument, + jschar*& aBufOut, size_t& aLengthOut) { if (!aLength) { - aString.Truncate(); + aBufOut = nullptr; + aLengthOut = 0; return NS_OK; } @@ -1302,17 +1326,40 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, aLength, &unicodeLength); NS_ENSURE_SUCCESS(rv, rv); - if (!aString.SetLength(unicodeLength, fallible_t())) { + aBufOut = static_cast(js_malloc(unicodeLength * sizeof(jschar))); + if (!aBufOut) { + aLengthOut = 0; return NS_ERROR_OUT_OF_MEMORY; } - - char16_t *ustr = aString.BeginWriting(); + aLengthOut = unicodeLength; rv = unicodeDecoder->Convert(reinterpret_cast(aData), - (int32_t *) &aLength, ustr, + (int32_t *) &aLength, aBufOut, &unicodeLength); MOZ_ASSERT(NS_SUCCEEDED(rv)); - aString.SetLength(unicodeLength); + aLengthOut = unicodeLength; + if (NS_FAILED(rv)) { + js_free(aBufOut); + aBufOut = nullptr; + aLengthOut = 0; + } + return rv; +} + +/* static */ nsresult +nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, + uint32_t aLength, const nsAString& aHintCharset, + nsIDocument* aDocument, nsString& aString) +{ + jschar* bufOut = nullptr; + size_t lengthOut = 0; + nsresult rv = ConvertToUTF16(aChannel, aData, aLength, aHintCharset, + aDocument, bufOut, lengthOut); + aString.SetLength(lengthOut); + if (bufOut) { + memcpy(aString.BeginWriting(), bufOut, lengthOut * sizeof(jschar)); + js_free(bufOut); + } return rv; } @@ -1426,7 +1473,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, hintCharset = mPreloads[i].mCharset; } rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument, - aRequest->mScriptText); + aRequest->mScriptTextBuf, aRequest->mScriptTextLength); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/content/base/src/nsScriptLoader.h b/content/base/src/nsScriptLoader.h index a734ab61c4d5..a3a36d08aace 100644 --- a/content/base/src/nsScriptLoader.h +++ b/content/base/src/nsScriptLoader.h @@ -21,6 +21,10 @@ class nsScriptLoadRequest; class nsIURI; +namespace JS { + class SourceBufferHolder; +} + ////////////////////////////////////////////////////////////// // Script loader implementation ////////////////////////////////////////////////////////////// @@ -131,6 +135,27 @@ public: } } + /** + * Convert the given buffer to a UTF-16 string. + * @param aChannel Channel corresponding to the data. May be null. + * @param aData The data to convert + * @param aLength Length of the data + * @param aHintCharset Hint for the character set (e.g., from a charset + * attribute). May be the empty string. + * @param aDocument Document which the data is loaded for. Must not be + * null. + * @param aBufOut [out] jschar array allocated by ConvertToUTF16 and + * containing data converted to unicode. Caller must + * js_free() this data when no longer needed. + * @param aLengthOut [out] Length of array returned in aBufOut in number + * of jschars. + */ + static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, + uint32_t aLength, + const nsAString& aHintCharset, + nsIDocument* aDocument, + jschar*& aBufOut, size_t& aLengthOut); + /** * Convert the given buffer to a UTF-16 string. * @param aChannel Channel corresponding to the data. May be null. @@ -277,7 +302,7 @@ private: void FireScriptEvaluated(nsresult aResult, nsScriptLoadRequest* aRequest); nsresult EvaluateScript(nsScriptLoadRequest* aRequest, - const nsAFlatString& aScript, + JS::SourceBufferHolder& aSrcBuf, void **aOffThreadToken); already_AddRefed GetScriptGlobalObject();