diff --git a/content/base/public/nsIScriptLoader.idl b/content/base/public/nsIScriptLoader.idl new file mode 100644 index 000000000000..bb48a08dfb42 --- /dev/null +++ b/content/base/public/nsIScriptLoader.idl @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Vidur Apparao (original author) + */ + +#include "nsISupports.idl" + +interface nsIDocument; +interface nsIDOMHTMLScriptElement; +interface nsIScriptLoaderObserver; + +[scriptable, uuid(501209d3-7edf-437d-9948-3c6d1c08ef7a)] +interface nsIScriptLoader : nsISupports { + /** + * Initialize loader with a document. The container of this document + * will be used for getting script evaluation information, including + * the context in which to do the evaluation. The loader maintains a + * strong reference to the document. + * + * @param aDocument The document to use as the basis for script + * processing. + */ + void init(in nsIDocument aDocument); + + /** + * The loader maintains a strong reference to the document with + * which it is initialized. This call forces the reference to + * be dropped. + */ + void dropDocumentReference(); + + /** + * Add an observer for all scripts loaded through this loader. + * + * @param aObserver observer for all script processing. + */ + void addObserver(in nsIScriptLoaderObserver aObserver); + + /** + * Remove an observer. + * + * @param aObserver observer to be removed + */ + void removeObserver(in nsIScriptLoaderObserver aObserver); + + /** + * Process a script element. This will include both loading the + * source of the element if it is not inline and evaluating + * the script itself. + * + * @param aElement The element representing the script to be loaded and + * evaluated. + * @param aObserver An observer for this script load only + * + */ + void processScriptElement(in nsIDOMHTMLScriptElement aElement, + in nsIScriptLoaderObserver aObserver); +}; diff --git a/content/base/public/nsIScriptLoaderObserver.idl b/content/base/public/nsIScriptLoaderObserver.idl new file mode 100644 index 000000000000..0ddac7043771 --- /dev/null +++ b/content/base/public/nsIScriptLoaderObserver.idl @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Vidur Apparao (original author) + */ + +#include "nsISupports.idl" + +interface nsIDOMHTMLScriptElement; +interface nsIURI; + +[scriptable, uuid(501209d3-7edf-437d-9948-3c6d1c08ef7f)] +interface nsIScriptLoaderObserver : nsISupports { + + /** + * The script is available for evaluation. For inline scripts, this + * method will be called synchronously. For externally loaded scripts, + * this method will be called when the load completes. + * + * @param aResult A result code representing the result of loading + * a script. If this is a failure code, script evaluation + * will not occur. + * @param aElement The element being processed. + * @param aIsInline Is this an inline script or externally loaded? + * @param aWasPending Did script processing have to be delayed, + * either for loading of an external script or + * because processing of an earlier scheduled + * script was delayed? + * @param aURI What is the URI of the script (the document URI if + * it is inline). + * @param aLineNo At what line does the script appear (generally 1 + * if it is a loaded script). + * @param aScript String representation of the string to be evaluated. + */ + void scriptAvailable(in nsresult aResult, + in nsIDOMHTMLScriptElement aElement, + in boolean aIsInline, + in boolean aWasPending, + in nsIURI aURI, + in PRInt32 aLineNo, + in AString aScript); + + /** + * The script has been evaluated. + * + * @param aResult A result code representing the success or failure of + * the script evaluation. + * @param aElement The element being processed. + * @param aIsInline Is this an inline script or externally loaded? + * @param aWasPending Did script processing have to be delayed, + * either for loading of an external script or + * because processing of an earlier scheduled + * script was delayed? + */ + void scriptEvaluated(in nsresult aResult, + in nsIDOMHTMLScriptElement aElement, + in boolean aIsInline, + in boolean aWasPending); + +}; diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp new file mode 100644 index 000000000000..6a54aeec0d5f --- /dev/null +++ b/content/base/src/nsScriptLoader.cpp @@ -0,0 +1,763 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Vidur Apparao (original author) + */ + +#include "nsScriptLoader.h" +#include "nsIDOMCharacterData.h" +#include "nsParserUtils.h" +#include "nsICharsetConverterManager.h" +#include "nsICharsetConverterManager2.h" +#include "nsIUnicodeDecoder.h" +#include "nsICharsetAlias.h" +#include "nsIContent.h" +#include "nsHTMLAtoms.h" +#include "nsNetUtil.h" +#include "nsIScriptGlobalObject.h" +#include "nsINodeInfo.h" +#include "nsINameSpaceManager.h" +#include "nsIScriptSecurityManager.h" +#include "nsIPrincipal.h" +#include "nsContentPolicyUtils.h" +#include "nsIHttpChannel.h" +#include "nsIScriptElement.h" +#include "nsIDocShell.h" +#include "jsapi.h" + +static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); + +////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////// +// Per-request data structure +////////////////////////////////////////////////////////////// + +class nsScriptLoadRequest : public nsISupports { +public: + nsScriptLoadRequest(nsIDOMHTMLScriptElement* aElement, + nsIScriptLoaderObserver* aObserver, + const char* aVersionString); + ~nsScriptLoadRequest(); + + NS_DECL_ISUPPORTS + + void FireScriptAvailable(nsresult aResult, + const nsAFlatString& aScript); + void FireScriptEvaluated(nsresult aResult); + + nsCOMPtr mElement; + nsCOMPtr mObserver; + PRBool mLoading; // Are we still waiting for a load to complete? + PRBool mWasPending; // Processed immediately or pending + PRBool mIsInline; // Is the script inline or loaded? + // nsSharableString mScriptText; // Holds script for loaded scripts + nsString mScriptText; + const char* mJSVersion; // We don't own this string + nsCOMPtr mURI; + PRInt32 mLineNo; +}; + +nsScriptLoadRequest::nsScriptLoadRequest(nsIDOMHTMLScriptElement* aElement, + nsIScriptLoaderObserver* aObserver, + const char* aVersionString) : + mElement(aElement), mObserver(aObserver), + mJSVersion(aVersionString), mLoading(PR_TRUE), mWasPending(PR_FALSE), + mIsInline(PR_TRUE), mLineNo(1) +{ + NS_INIT_ISUPPORTS(); +} + +nsScriptLoadRequest::~nsScriptLoadRequest() +{ +} + +NS_IMPL_ISUPPORTS0(nsScriptLoadRequest) + +void +nsScriptLoadRequest::FireScriptAvailable(nsresult aResult, + const nsAFlatString& aScript) +{ + if (mObserver) { + mObserver->ScriptAvailable(aResult, mElement, mIsInline, mWasPending, + mURI, mLineNo, + aScript); + } +} + +void +nsScriptLoadRequest::FireScriptEvaluated(nsresult aResult) +{ + if (mObserver) { + mObserver->ScriptEvaluated(aResult, mElement, mIsInline, mWasPending); + } +} + +////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////// + +nsScriptLoader::nsScriptLoader() + : mDocument(nsnull) +{ + NS_INIT_ISUPPORTS(); +} + +nsScriptLoader::~nsScriptLoader() +{ + mObservers.Clear(); + + PRUint32 i, count; + mPendingRequests.Count(&count); + for (i = 0; i < count; i++) { + nsCOMPtr sup(mPendingRequests.ElementAt(i)); + if (sup) { + nsScriptLoadRequest* req = NS_REINTERPRET_CAST(nsScriptLoadRequest*, sup.get()); + req->FireScriptAvailable(NS_ERROR_ABORT, NS_LITERAL_STRING("")); + } + } + + mPendingRequests.Clear(); +} + +NS_INTERFACE_MAP_BEGIN(nsScriptLoader) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptLoader) + NS_INTERFACE_MAP_ENTRY(nsIScriptLoader) + NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsScriptLoader) +NS_IMPL_RELEASE(nsScriptLoader) + +/* void init (in nsIDocument aDocument); */ +NS_IMETHODIMP +nsScriptLoader::Init(nsIDocument *aDocument) +{ + mDocument = aDocument; + + return NS_OK; +} + +/* void dropDocumentReference (); */ +NS_IMETHODIMP +nsScriptLoader::DropDocumentReference() +{ + mDocument = nsnull; + + return NS_OK; +} + +/* void addObserver (in nsIScriptLoaderObserver aObserver); */ +NS_IMETHODIMP +nsScriptLoader::AddObserver(nsIScriptLoaderObserver *aObserver) +{ + NS_ENSURE_ARG(aObserver); + + mObservers.AppendElement(aObserver); + + return NS_OK; +} + +/* void removeObserver (in nsIScriptLoaderObserver aObserver); */ +NS_IMETHODIMP +nsScriptLoader::RemoveObserver(nsIScriptLoaderObserver *aObserver) +{ + NS_ENSURE_ARG(aObserver); + + mObservers.RemoveElement(aObserver); + + return NS_OK; +} + +static void SplitMimeType(const nsString& aValue, nsString& aType, nsString& aParams) +{ + aType.Truncate(); + aParams.Truncate(); + PRInt32 semiIndex = aValue.FindChar(PRUnichar(';')); + if (-1 != semiIndex) { + aValue.Left(aType, semiIndex); + aValue.Right(aParams, (aValue.Length() - semiIndex) - 1); + aParams.StripWhitespace(); + } + else { + aType = aValue; + } + aType.StripWhitespace(); +} + +PRBool +nsScriptLoader::InNonScriptingContainer(nsIDOMHTMLScriptElement* aScriptElement) +{ + nsCOMPtr node(do_QueryInterface(aScriptElement)); + nsCOMPtr parent; + + node->GetParentNode(getter_AddRefs(parent)); + while (parent) { + nsCOMPtr content(do_QueryInterface(parent)); + if (!content) { + break; + } + + nsCOMPtr nodeInfo; + content->GetNodeInfo(*getter_AddRefs(nodeInfo)); + NS_ASSERTION(nodeInfo, "element without node info"); + + if (nodeInfo) { + nsCOMPtr localName; + nodeInfo->GetNameAtom(*getter_AddRefs(localName)); + + // XXX noframes and noembed are currently unconditionally not + // displayed and processed. This might change if we support either + // prefs or per-document container settings for not allowing + // frames or plugins. + if (content->IsContentOfType(nsIContent::eHTML) && + ((localName.get() == nsHTMLAtoms::iframe) || + (localName.get() == nsHTMLAtoms::noframes) || + (localName.get() == nsHTMLAtoms::noembed))) { + return PR_TRUE; + } + } + + node = parent; + node->GetParentNode(getter_AddRefs(parent)); + } + + return PR_FALSE; +} + +/* void processScriptElement (in nsIDOMHTMLScriptElement aElement, in nsIScriptLoaderObserver aObserver); */ +NS_IMETHODIMP +nsScriptLoader::ProcessScriptElement(nsIDOMHTMLScriptElement *aElement, + nsIScriptLoaderObserver *aObserver) +{ + NS_ENSURE_ARG(aElement); + + nsresult rv = NS_OK; + + // We need a document to evaluate scripts. + if (!mDocument) { + return FireErrorNotification(NS_ERROR_FAILURE, aElement, aObserver); + } + + // Check to see that the element is not in a container that + // suppresses script evaluation within it. + if (InNonScriptingContainer(aElement)) { + return FireErrorNotification(NS_ERROR_NOT_AVAILABLE, aElement, aObserver); + } + + // See if script evaluation is enabled. + PRBool scriptsEnabled = PR_TRUE; + nsCOMPtr globalObject; + mDocument->GetScriptGlobalObject(getter_AddRefs(globalObject)); + if (globalObject) + { + nsCOMPtr context; + if (NS_SUCCEEDED(globalObject->GetContext(getter_AddRefs(context))) + && context) + context->GetScriptsEnabled(&scriptsEnabled); + } + + // If scripts aren't enabled there's no point in going on. + if (!scriptsEnabled) { + return FireErrorNotification(NS_ERROR_NOT_AVAILABLE, aElement, aObserver); + } + + PRBool isJavaScript = PR_TRUE; + const char* jsVersionString = nsnull; + nsAutoString src, type, language; + + // Check the type attribute to determine language and version. + aElement->GetType(type); + if (!type.IsEmpty()) { + nsAutoString mimeType; + nsAutoString params; + SplitMimeType(type, mimeType, params); + + isJavaScript = mimeType.EqualsIgnoreCase("application/x-javascript") || + mimeType.EqualsIgnoreCase("text/javascript"); + if (isJavaScript) { + JSVersion jsVersion = JSVERSION_DEFAULT; + if (params.Find("version=", PR_TRUE) == 0) { + if (params.Length() != 11 || params[8] != '1' || params[9] != '.') + jsVersion = JSVERSION_UNKNOWN; + else switch (params[10]) { + case '0': jsVersion = JSVERSION_1_0; break; + case '1': jsVersion = JSVERSION_1_1; break; + case '2': jsVersion = JSVERSION_1_2; break; + case '3': jsVersion = JSVERSION_1_3; break; + case '4': jsVersion = JSVERSION_1_4; break; + case '5': jsVersion = JSVERSION_1_5; break; + default: jsVersion = JSVERSION_UNKNOWN; + } + } + jsVersionString = JS_VersionToString(jsVersion); + } + } + + // Check the language attribute. + aElement->GetAttribute(NS_LITERAL_STRING("language"), language); + if (!language.IsEmpty()) { + isJavaScript = nsParserUtils::IsJavaScriptLanguage(language, &jsVersionString); + } + + // If this isn't JavaScript, we don't know how to evaluate. + // XXX How and where should we deal with other scripting languages?? + if (!isJavaScript) { + return FireErrorNotification(NS_ERROR_NOT_AVAILABLE, aElement, aObserver); + } + + // Create a request object for this script + nsScriptLoadRequest* request = new nsScriptLoadRequest(aElement, aObserver, jsVersionString); + if (!request) { + return FireErrorNotification(NS_ERROR_OUT_OF_MEMORY, aElement, aObserver); + } + // Temporarily hold on to the request + nsCOMPtr reqsup(request); + + // Check to see if we have a src attribute. + aElement->GetSrc(src); + if (!src.IsEmpty()) { + nsCOMPtr baseURI, scriptURI; + + // Use the SRC attribute value to load the URL + mDocument->GetBaseURL(*getter_AddRefs(baseURI)); + rv = NS_NewURI(getter_AddRefs(scriptURI), src, baseURI); + if (NS_FAILED(rv)) { + return FireErrorNotification(rv, aElement, aObserver); + } + + // Check that the containing page is allowed to load this URI. + nsCOMPtr securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + return FireErrorNotification(rv, aElement, aObserver); + } + rv = securityManager->CheckLoadURI(baseURI, scriptURI, + nsIScriptSecurityManager::ALLOW_CHROME); + if (NS_FAILED(rv)) { + return FireErrorNotification(rv, aElement, aObserver); + } + + // After the security manager, the content-policy stuff gets a veto + // For pinkerton: a symphony for string conversion, in 3 parts. + nsXPIDLCString urlCString; + scriptURI->GetSpec(getter_Copies(urlCString)); + nsAutoString url; + url.AssignWithConversion(urlCString.get()); + + PRBool shouldLoad = PR_TRUE; + if (NS_SUCCEEDED(rv) && + (rv = NS_CheckContentLoadPolicy(nsIContentPolicy::CONTENT_SCRIPT, + url, aElement, &shouldLoad), + NS_SUCCEEDED(rv)) && + !shouldLoad) { + + return FireErrorNotification(NS_ERROR_NOT_AVAILABLE, aElement, aObserver); + } + + request->mURI = scriptURI; + request->mIsInline = PR_FALSE; + request->mWasPending = PR_TRUE; + request->mLoading = PR_TRUE; + + // Add the request to our pending requests list + mPendingRequests.AppendElement(reqsup); + + nsCOMPtr loadGroup; + nsCOMPtr loader; + + mDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup)); + + nsCOMPtr docshell; + globalObject->GetDocShell(getter_AddRefs(docshell)); + nsCOMPtr prompter(do_QueryInterface(docshell)); + + rv = NS_NewStreamLoader(getter_AddRefs(loader), scriptURI, this, + reqsup, loadGroup, prompter, + nsIChannel::LOAD_NORMAL); + if (NS_FAILED(rv)) { + mPendingRequests.RemoveElement(reqsup); + return FireErrorNotification(rv, aElement, aObserver); + } + } + else { + request->mLoading = PR_FALSE; + request->mIsInline = PR_TRUE; + request->mURI = dont_AddRef(mDocument->GetDocumentURL()); + + nsCOMPtr scriptElement(do_QueryInterface(aElement)); + if (scriptElement) { + PRUint32 lineNumber; + scriptElement->GetLineNumber(&lineNumber); + request->mLineNo = lineNumber; + } + + PRUint32 pendingRequestCount; + mPendingRequests.Count(&pendingRequestCount); + + // If we've got existing pending requests, add ourselves + // to this list. + if (pendingRequestCount) { + request->mWasPending = PR_TRUE; + mPendingRequests.AppendElement(reqsup); + } + else { + request->mWasPending = PR_FALSE; + rv = ProcessRequest(request); + } + } + + return rv; +} + +nsresult +nsScriptLoader::FireErrorNotification(nsresult aResult, + nsIDOMHTMLScriptElement* aElement, + nsIScriptLoaderObserver* aObserver) +{ + PRUint32 i, count; + + mObservers.Count(&count); + for (i = 0; i < count; i++) { + nsCOMPtr sup(dont_AddRef(mObservers.ElementAt(i))); + nsCOMPtr observer(do_QueryInterface(sup)); + + if (observer) { + observer->ScriptAvailable(aResult, aElement, + PR_TRUE, PR_FALSE, + nsnull, 0, + NS_LITERAL_STRING("")); + } + } + + if (aObserver) { + aObserver->ScriptAvailable(aResult, aElement, + PR_TRUE, PR_FALSE, + nsnull, 0, + NS_LITERAL_STRING("")); + } + + return aResult; +} + +nsresult +nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest) +{ + NS_ENSURE_ARG(aRequest); + nsAFlatString* script; + nsAutoString textData; + + // If there's no script text, we try to get it from the element + if (aRequest->mIsInline) { + // XXX This is inefficient - GetText makes multiple + // copies. + aRequest->mElement->GetText(textData); + + script = &textData; + } + else { + script = &aRequest->mScriptText; + } + + FireScriptAvailable(NS_OK, aRequest, *script); + nsresult rv = EvaluateScript(aRequest, *script); + FireScriptEvaluated(rv, aRequest); + + return rv; +} + +void +nsScriptLoader::FireScriptAvailable(nsresult aResult, + nsScriptLoadRequest* aRequest, + const nsAFlatString& aScript) +{ + PRUint32 i, count; + + mObservers.Count(&count); + for (i = 0; i < count; i++) { + nsCOMPtr sup(dont_AddRef(mObservers.ElementAt(i))); + nsCOMPtr observer(do_QueryInterface(sup)); + + if (observer) { + observer->ScriptAvailable(aResult, aRequest->mElement, + aRequest->mIsInline, aRequest->mWasPending, + aRequest->mURI, aRequest->mLineNo, + aScript); + } + } + + aRequest->FireScriptAvailable(aResult, aScript); +} + +void +nsScriptLoader::FireScriptEvaluated(nsresult aResult, + nsScriptLoadRequest* aRequest) +{ + PRUint32 i, count; + + mObservers.Count(&count); + for (i = 0; i < count; i++) { + nsCOMPtr sup(dont_AddRef(mObservers.ElementAt(i))); + nsCOMPtr observer(do_QueryInterface(sup)); + + if (observer) { + observer->ScriptEvaluated(aResult, aRequest->mElement, + aRequest->mIsInline, aRequest->mWasPending); + } + } + + aRequest->FireScriptEvaluated(aResult); +} + +nsresult +nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest, + const nsAFlatString& aScript) +{ + nsresult rv = NS_OK; + + nsCOMPtr globalObject; + mDocument->GetScriptGlobalObject(getter_AddRefs(globalObject)); + NS_ENSURE_TRUE(globalObject, NS_ERROR_FAILURE); + + nsCOMPtr context; + rv = globalObject->GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv) || !context) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr principal; + mDocument->GetPrincipal(getter_AddRefs(principal)); + // We can survive without a principal, but we really should + // have one. + NS_ASSERTION(principal, "principal required for document"); + + nsAutoString ret; + nsXPIDLCString url; + + if (aRequest->mURI) { + rv = aRequest->mURI->GetSpec(getter_Copies(url)); + if (NS_FAILED(rv)) { + return rv; + } + } + + context->SetProcessingScriptTag(PR_TRUE); + + PRBool isUndefined; + context->EvaluateString(aScript, nsnull, principal, url, + aRequest->mLineNo, aRequest->mJSVersion, + ret, &isUndefined); + + context->SetProcessingScriptTag(PR_FALSE); + + return rv; +} + +void +nsScriptLoader::ProcessPendingReqests() +{ + nsCOMPtr reqsup(dont_AddRef(mPendingRequests.ElementAt(0))); + nsScriptLoadRequest* request = NS_REINTERPRET_CAST(nsScriptLoadRequest*, + reqsup.get()); + while (request && !request->mLoading) { + mPendingRequests.RemoveElement(reqsup); + ProcessRequest(request); + reqsup = dont_AddRef(mPendingRequests.ElementAt(0)); + request = NS_REINTERPRET_CAST(nsScriptLoadRequest*, reqsup.get()); + } +} + +NS_IMETHODIMP +nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + PRUint32 stringLen, + const char* string) +{ + nsresult rv; + nsScriptLoadRequest* request = NS_REINTERPRET_CAST(nsScriptLoadRequest*, aContext); + NS_ASSERTION(request, "null request in stream complete handler"); + if (!request) { + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(aStatus)) { + mPendingRequests.RemoveElement(aContext); + FireScriptAvailable(aStatus, request, NS_LITERAL_STRING("")); + ProcessPendingReqests(); + return NS_OK; + } + + // If we don't have a document, then we need to abort further + // evaluation. + if (!mDocument) { + mPendingRequests.RemoveElement(aContext); + FireScriptAvailable(NS_ERROR_NOT_AVAILABLE, request, + NS_LITERAL_STRING("")); + ProcessPendingReqests(); + return NS_OK; + } + + if (stringLen) { + nsAutoString characterSet, preferred; + nsCOMPtr unicodeDecoder; + nsXPIDLCString contenttypeheader; + nsCOMPtr httpChannel; + + nsCOMPtr channel; + nsCOMPtr req; + rv = aLoader->GetRequest(getter_AddRefs(req)); + NS_ASSERTION(req, "StreamLoader's request went away prematurely"); + if (NS_FAILED(rv)) return rv; + + channel = do_QueryInterface(req); + + httpChannel = do_QueryInterface(channel); + if (httpChannel) { + rv = httpChannel->GetResponseHeader("content-type", + getter_Copies(contenttypeheader)); + } + + if (NS_SUCCEEDED(rv)) { + nsAutoString contentType; + contentType.AssignWithConversion(contenttypeheader.get()); + + PRInt32 start = contentType.RFind("charset=", PR_TRUE ) ; + + if(kNotFound != start) { + start += 8; // 8 = "charset=".length + PRInt32 end = contentType.FindCharInSet(";\n\r ", start ); + if(kNotFound == end ) end = contentType.Length(); + + contentType.Mid(characterSet, start, end - start); + nsCOMPtr calias(do_GetService(kCharsetAliasCID,&rv)); + + if(NS_SUCCEEDED(rv) && calias) { + rv = calias->GetPreferred(characterSet, preferred); + + if(NS_SUCCEEDED(rv)) { + characterSet = preferred; + } + } + } + } + + if (NS_FAILED(rv) || characterSet.IsEmpty()) { + nsAutoString charset; + // Check the charset attribute to determine script charset. + request->mElement->GetCharset(charset); + if (!charset.IsEmpty()) { + // Get the preferred charset from charset alias service if there + // is one. + nsCOMPtr calias(do_GetService(kCharsetAliasCID,&rv)); + if (NS_SUCCEEDED(rv)) { + rv = calias->GetPreferred(characterSet, preferred); + + if(NS_SUCCEEDED(rv)) { + characterSet = preferred; + } + } + } + } + + if (NS_FAILED(rv) || characterSet.IsEmpty()) { + // charset from document default + rv = mDocument->GetDocumentCharacterSet(characterSet); + } + + NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get document charset!"); + + nsCOMPtr charsetConv = + do_GetService(kCharsetConverterManagerCID, &rv); + + if (NS_SUCCEEDED(rv) && charsetConv) { + rv = charsetConv->GetUnicodeDecoder(&characterSet, + getter_AddRefs(unicodeDecoder)); + } + + // converts from the charset to unicode + if (NS_SUCCEEDED(rv)) { + PRInt32 unicodeLength = 0; + + rv = unicodeDecoder->GetMaxLength(string, stringLen, &unicodeLength); + if (NS_SUCCEEDED(rv)) { + typedef nsSharedBufferHandle* HandlePtr; + typedef nsAString* StrPtr; + HandlePtr handle = NS_AllocateContiguousHandleWithData(HandlePtr(0), unicodeLength+1, StrPtr(0)); + PRUnichar *ustr = (PRUnichar *)handle->DataStart(); + + rv = unicodeDecoder->Convert(string, (PRInt32 *) &stringLen, ustr, + &unicodeLength); + + if (NS_SUCCEEDED(rv)) { + handle->DataEnd(handle->DataStart() + unicodeLength); + } + nsSharableString tempStr(handle); + request->mScriptText = tempStr; + } + } + + NS_ASSERTION(NS_SUCCEEDED(rv), + "Could not convert external JavaScript to Unicode!"); + if (NS_FAILED(rv)) { + mPendingRequests.RemoveElement(aContext); + FireScriptAvailable(rv, request, NS_LITERAL_STRING("")); + ProcessPendingReqests(); + return NS_OK; + } + + //-- Merge the principal of the script file with that of the document + if (channel) { + nsCOMPtr owner; + channel->GetOwner(getter_AddRefs(owner)); + nsCOMPtr prin; + + if (owner) { + prin = do_QueryInterface(owner, &rv); + } + + rv = mDocument->AddPrincipal(prin); + if (NS_FAILED(rv)) { + mPendingRequests.RemoveElement(aContext); + FireScriptAvailable(rv, request, NS_LITERAL_STRING("")); + ProcessPendingReqests(); + return NS_OK; + } + } + } + + + // If we're not the first in the pending list, we mark ourselves + // as loaded and just stay on the list. + nsCOMPtr first(dont_AddRef(mPendingRequests.ElementAt(0))); + if (first != aContext) { + request->mLoading = PR_FALSE; + return NS_OK; + } + + mPendingRequests.RemoveElement(aContext); + ProcessRequest(request); + + // Process any pending requests + ProcessPendingReqests(); + + return NS_OK; +} diff --git a/content/base/src/nsScriptLoader.h b/content/base/src/nsScriptLoader.h new file mode 100644 index 000000000000..6faa27554074 --- /dev/null +++ b/content/base/src/nsScriptLoader.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Vidur Apparao (original author) + */ + +#ifndef __nsScriptLoader_h__ +#define __nsScriptLoader_h__ + +#include "nsCOMPtr.h" +#include "nsIScriptLoader.h" +#include "nsIDOMHTMLScriptElement.h" +#include "nsIScriptLoaderObserver.h" +#include "nsCommonString.h" +#include "nsIURI.h" +#include "nsSupportsArray.h" +#include "nsIDocument.h" +#include "nsIStreamLoader.h" + +class nsScriptLoadRequest; + +////////////////////////////////////////////////////////////// +// Script loader implementation +////////////////////////////////////////////////////////////// + +class nsScriptLoader : public nsIScriptLoader, + public nsIStreamLoaderObserver +{ +public: + nsScriptLoader(); + virtual ~nsScriptLoader(); + + NS_DECL_ISUPPORTS + NS_DECL_NSISCRIPTLOADER + NS_DECL_NSISTREAMLOADEROBSERVER + +protected: + PRBool InNonScriptingContainer(nsIDOMHTMLScriptElement* aScriptElement); + nsresult FireErrorNotification(nsresult aResult, + nsIDOMHTMLScriptElement* aElement, + nsIScriptLoaderObserver* aObserver); + nsresult ProcessRequest(nsScriptLoadRequest* aRequest); + void FireScriptAvailable(nsresult aResult, + nsScriptLoadRequest* aRequest, + const nsAFlatString& aScript); + void FireScriptEvaluated(nsresult aResult, + nsScriptLoadRequest* aRequest); + nsresult EvaluateScript(nsScriptLoadRequest* aRequest, + const nsAFlatString& aScript); + void ProcessPendingReqests(); + + nsIDocument* mDocument; // [WEAK] + nsSupportsArray mObservers; + nsSupportsArray mPendingRequests; +}; + +#endif //__nsScriptLoader_h__ diff --git a/content/html/content/public/nsIScriptElement.h b/content/html/content/public/nsIScriptElement.h new file mode 100644 index 000000000000..7374cd2c4154 --- /dev/null +++ b/content/html/content/public/nsIScriptElement.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Vidur Apparao (original author) + */ + +#ifndef nsIScriptElement_h___ +#define nsIScriptElement_h___ + +#include "nsISupports.h" + +#define NS_ISCRIPTELEMENT_IID \ +{ /* c9cbf78e-b7c1-48b0-a933-78d62b83a675 */ \ + 0xc9cbf78e, 0xb7c1, 0x48b0, \ +{0xa9, 0x33, 0x78, 0xd6, 0x2b, 0x83, 0xa6, 0x75}} \ + +/** + * Internal interface so that the content sink can let a + * script element know about its origin line number. + */ +class nsIScriptElement : public nsISupports { +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISCRIPTELEMENT_IID) + + NS_IMETHOD SetLineNumber(PRUint32 aLineNumber) = 0; + NS_IMETHOD GetLineNumber(PRUint32* aLineNumber) = 0; +}; + +#endif // nsIScriptElement_h___