nsresult nsXULPrototypeCache::PutScript(nsIURI* aURI, PRUint32 aLangID, void* aScriptObject) { CacheScriptEntry existingEntry; if (mScriptTable.Get(aURI, &existingEntry)) { NS_WARNING("loaded the same script twice (bug 392650)"); // Reuse the callback used for enumeration in FlushScripts ReleaseScriptObjectCallback(aURI, existingEntry, nsnull); } CacheScriptEntry entry = {aLangID, aScriptObject}; NS_ENSURE_TRUE(mScriptTable.Put(aURI, entry), NS_ERROR_OUT_OF_MEMORY); // Lock the object from being gc'd until it is removed from the cache nsCOMPtr<nsIScriptRuntime> rt; nsresult rv = NS_GetScriptRuntimeByID(aLangID, getter_AddRefs(rt)); if (NS_SUCCEEDED(rv)) rv = rt->HoldScriptObject(aScriptObject); NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to GC lock the object"); // On failure doing the lock, we should remove the map entry? return rv; }
/* static */ static PLDHashOperator ReleaseScriptObjectCallback(nsIURI* aKey, CacheScriptEntry &aData, void* aClosure) { nsCOMPtr<nsIScriptRuntime> rt; if (NS_SUCCEEDED(NS_GetScriptRuntimeByID(aData.mScriptTypeID, getter_AddRefs(rt)))) rt->DropScriptObject(aData.mScriptObject); return PL_DHASH_REMOVE; }
nsresult nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) { // We need a document to evaluate scripts. NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); // Check to see if scripts has been turned off. if (!mEnabled || !mDocument->IsScriptEnabled()) { return NS_ERROR_NOT_AVAILABLE; } NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script"); // Check that the script is not an eventhandler if (IsScriptEventHandler(aElement)) { return NS_CONTENT_SCRIPT_IS_EVENTHANDLER; } // Script evaluation can also be disabled in the current script // context even though it's enabled in the document. // XXX - still hard-coded for JS here, even though another language // may be specified. Should this check be made *after* we examine // the attributes to locate the script-type? // For now though, if JS is disabled we assume every language is // disabled. // XXX is this different from the mDocument->IsScriptEnabled() call? nsIScriptGlobalObject *globalObject = mDocument->GetScriptGlobalObject(); if (!globalObject) { return NS_ERROR_NOT_AVAILABLE; } nsIScriptContext *context = globalObject->GetScriptContext( nsIProgrammingLanguage::JAVASCRIPT); // If scripts aren't enabled in the current context, there's no // point in going on. if (!context || !context->GetScriptsEnabled()) { return NS_ERROR_NOT_AVAILABLE; } // Default script language is whatever the root content specifies // (which may come from a header or http-meta tag), or if there // is no root content, from the script global object. nsCOMPtr<nsIContent> rootContent = mDocument->GetRootContent(); PRUint32 typeID = rootContent ? rootContent->GetScriptTypeID() : context->GetScriptTypeID(); PRUint32 version = 0; nsAutoString language, type, src; nsresult rv = NS_OK; // Check the type attribute to determine language and version. // If type exists, it trumps the deprecated 'language=' aElement->GetScriptType(type); if (!type.IsEmpty()) { nsCOMPtr<nsIMIMEHeaderParam> mimeHdrParser = do_GetService("@mozilla.org/network/mime-hdrparam;1"); NS_ENSURE_TRUE(mimeHdrParser, NS_ERROR_FAILURE); NS_ConvertUTF16toUTF8 typeAndParams(type); nsAutoString mimeType; rv = mimeHdrParser->GetParameter(typeAndParams, nsnull, EmptyCString(), PR_FALSE, nsnull, mimeType); NS_ENSURE_SUCCESS(rv, rv); // Javascript keeps the fast path, optimized for most-likely type // Table ordered from most to least likely JS MIME types. // See bug 62485, feel free to add <script type="..."> survey data to it, // or to a new bug once 62485 is closed. static const char *jsTypes[] = { "text/javascript", "text/ecmascript", "application/javascript", "application/ecmascript", "application/x-javascript", nsnull }; PRBool isJavaScript = PR_FALSE; for (PRInt32 i = 0; jsTypes[i]; i++) { if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) { isJavaScript = PR_TRUE; break; } } if (isJavaScript) typeID = nsIProgrammingLanguage::JAVASCRIPT; else { // Use the object factory to locate a matching language. nsCOMPtr<nsIScriptRuntime> runtime; rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime)); if (NS_FAILED(rv) || runtime == nsnull) { // Failed to get the explicitly specified language NS_WARNING("Failed to find a scripting language"); typeID = nsIProgrammingLanguage::UNKNOWN; } else typeID = runtime->GetScriptTypeID(); } if (typeID != nsIProgrammingLanguage::UNKNOWN) { // Get the version string, and ensure the language supports it. nsAutoString versionName; rv = mimeHdrParser->GetParameter(typeAndParams, "version", EmptyCString(), PR_FALSE, nsnull, versionName); if (NS_FAILED(rv)) { // no version attribute - version remains 0. if (rv != NS_ERROR_INVALID_ARG) return rv; } else { nsCOMPtr<nsIScriptRuntime> runtime; rv = NS_GetScriptRuntimeByID(typeID, getter_AddRefs(runtime)); if (NS_FAILED(rv)) { NS_ERROR("Failed to locate the language with this ID"); return rv; } rv = runtime->ParseVersion(versionName, &version); if (NS_FAILED(rv)) { NS_WARNING("This script language version is not supported - ignored"); typeID = nsIProgrammingLanguage::UNKNOWN; } } } // Some js specifics yet to be abstracted. if (typeID == nsIProgrammingLanguage::JAVASCRIPT) { nsAutoString value; rv = mimeHdrParser->GetParameter(typeAndParams, "e4x", EmptyCString(), PR_FALSE, nsnull, value); if (NS_FAILED(rv)) { if (rv != NS_ERROR_INVALID_ARG) return rv; } else { if (value.Length() == 1 && value[0] == '1') // This means that we need to set JSOPTION_XML in the JS options. // We re-use our knowledge of the implementation to reuse // JSVERSION_HAS_XML as a safe version flag. // If version has JSVERSION_UNKNOWN (-1), then this is still OK. version |= JSVERSION_HAS_XML; } } } else { // no 'type=' element // "language" is a deprecated attribute of HTML, so we check it only for // HTML script elements. nsCOMPtr<nsIDOMHTMLScriptElement> htmlScriptElement = do_QueryInterface(aElement); if (htmlScriptElement) { htmlScriptElement->GetAttribute(NS_LITERAL_STRING("language"), language); if (!language.IsEmpty()) { if (nsParserUtils::IsJavaScriptLanguage(language, &version)) typeID = nsIProgrammingLanguage::JAVASCRIPT; else typeID = nsIProgrammingLanguage::UNKNOWN; // IE, Opera, etc. do not respect language version, so neither should // we at this late date in the browser wars saga. Note that this change // affects HTML but not XUL or SVG (but note also that XUL has its own // code to check nsParserUtils::IsJavaScriptLanguage -- that's probably // a separate bug, one we may not be able to fix short of XUL2). See // bug 255895 (https://bugzilla.mozilla.org/show_bug.cgi?id=255895). NS_ASSERTION(JSVERSION_DEFAULT == 0, "We rely on all languages having 0 as a version default"); version = 0; } } } // If we don't know the language, we don't know how to evaluate if (typeID == nsIProgrammingLanguage::UNKNOWN) { return NS_ERROR_NOT_AVAILABLE; } // If not from a chrome document (which is always trusted), we need some way // of checking the language is "safe". Currently the only other language // impl is Python, and that is *not* safe in untrusted code - so fixing // this isn't a priority.! // See also similar code in nsXULContentSink.cpp if (typeID != nsIProgrammingLanguage::JAVASCRIPT && !nsContentUtils::IsChromeDoc(mDocument)) { NS_WARNING("Untrusted language called from non-chrome - ignored"); return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement)); eltContent->SetScriptTypeID(typeID); // Create a request object for this script nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(aElement, version); NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY); // First check to see if this is an external script nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI(); if (scriptURI) { // Check that the containing page is allowed to load this URI. rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), scriptURI, nsIScriptSecurityManager::ALLOW_CHROME); NS_ENSURE_SUCCESS(rv, rv); // After the security manager, the content-policy stuff gets a veto PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, scriptURI, mDocument->NodePrincipal(), aElement, NS_LossyConvertUTF16toASCII(type), nsnull, //extra &shouldLoad, nsContentUtils::GetContentPolicy(), nsContentUtils::GetSecurityManager()); if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) { return NS_ERROR_CONTENT_BLOCKED; } return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT; } request->mURI = scriptURI; request->mIsInline = PR_FALSE; request->mLoading = PR_TRUE; nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup(); nsCOMPtr<nsIStreamLoader> loader; nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(globalObject)); nsIDocShell *docshell = window->GetDocShell(); nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell)); nsCOMPtr<nsIChannel> channel; rv = NS_NewChannel(getter_AddRefs(channel), scriptURI, nsnull, loadGroup, prompter, nsIRequest::LOAD_NORMAL); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); if (httpChannel) { // HTTP content negotation has little value in this context. httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("*/*"), PR_FALSE); httpChannel->SetReferrer(mDocument->GetDocumentURI()); } rv = NS_NewStreamLoader(getter_AddRefs(loader), this); NS_ENSURE_SUCCESS(rv, rv); rv = channel->AsyncOpen(loader, request); NS_ENSURE_SUCCESS(rv, rv); } else { request->mLoading = PR_FALSE; request->mIsInline = PR_TRUE; request->mURI = mDocument->GetDocumentURI(); request->mLineNo = aElement->GetScriptLineNumber(); // If we've got existing pending requests, add ourselves // to this list. if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript()) { return ProcessRequest(request); } } // Add the request to our pending requests list NS_ENSURE_TRUE(mPendingRequests.AppendObject(request), NS_ERROR_OUT_OF_MEMORY); // If there weren't any pending requests before, and this one is // ready to execute, do that as soon as it's safe. if (mPendingRequests.Count() == 1 && !request->mLoading && ReadyToExecuteScripts()) { nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsScriptLoader>(this, &nsScriptLoader::ProcessPendingRequests)); } // Added as pending request, now we can send blocking back return NS_ERROR_HTMLPARSER_BLOCK; }
nsresult XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes, const PRUint32 aLineNumber) { PRUint32 langID; nsresult rv = mContextStack.GetTopNodeScriptType(&langID); if (NS_FAILED(rv)) return rv; PRUint32 version = 0; // Look for SRC attribute and look for a LANGUAGE attribute nsAutoString src; while (*aAttributes) { const nsDependentString key(aAttributes[0]); if (key.EqualsLiteral("src")) { src.Assign(aAttributes[1]); } else if (key.EqualsLiteral("type")) { nsDependentString str(aAttributes[1]); nsContentTypeParser parser(str); nsAutoString mimeType; rv = parser.GetType(mimeType); if (NS_FAILED(rv)) { if (rv == NS_ERROR_INVALID_ARG) { // Might as well bail out now instead of setting langID to // nsIProgrammingLanguage::UNKNOWN and bailing out later. return NS_OK; } // We do want the warning here NS_ENSURE_SUCCESS(rv, rv); } // Javascript keeps the fast path, optimized for most-likely type // Table ordered from most to least likely JS MIME types. For .xul // files that we host, the likeliest type is application/x-javascript. // See bug 62485, feel free to add <script type="..."> survey data to it, // or to a new bug once 62485 is closed. static const char *jsTypes[] = { "application/x-javascript", "text/javascript", "text/ecmascript", "application/javascript", "application/ecmascript", nsnull }; bool isJavaScript = false; for (PRInt32 i = 0; jsTypes[i]; i++) { if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) { isJavaScript = true; break; } } if (isJavaScript) { langID = nsIProgrammingLanguage::JAVASCRIPT; version = JSVERSION_LATEST; } else { // Use the script object factory to locate the language. nsCOMPtr<nsIScriptRuntime> runtime; rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime)); if (NS_FAILED(rv) || runtime == nsnull) { // Failed to get the explicitly specified language NS_WARNING("Failed to find a scripting language"); langID = nsIProgrammingLanguage::UNKNOWN; } else langID = nsIProgrammingLanguage::JAVASCRIPT; } if (langID != nsIProgrammingLanguage::UNKNOWN) { // Get the version string, and ensure the language supports it. nsAutoString versionName; rv = parser.GetParameter("version", versionName); if (NS_FAILED(rv)) { if (rv != NS_ERROR_INVALID_ARG) return rv; // no version specified - version remains the default. } else { nsCOMPtr<nsIScriptRuntime> runtime; rv = NS_GetScriptRuntimeByID(langID, getter_AddRefs(runtime)); if (NS_FAILED(rv)) return rv; rv = runtime->ParseVersion(versionName, &version); if (NS_FAILED(rv)) { NS_WARNING("This script language version is not supported - ignored"); langID = nsIProgrammingLanguage::UNKNOWN; } } } // Some js specifics yet to be abstracted. if (langID == nsIProgrammingLanguage::JAVASCRIPT) { // By default scripts in XUL documents have E4X turned on. We use // our implementation knowledge to reuse JSVERSION_HAS_XML as a // safe version flag. This is still OK if version is // JSVERSION_UNKNOWN (-1), version = js::VersionSetXML(JSVersion(version), true); nsAutoString value; rv = parser.GetParameter("e4x", value); if (NS_FAILED(rv)) { if (rv != NS_ERROR_INVALID_ARG) return rv; } else { if (value.Length() == 1 && value[0] == '0') version = js::VersionSetXML(JSVersion(version), false); } } } else if (key.EqualsLiteral("language")) { // Language is deprecated, and the impl in nsScriptLoader ignores the // various version strings anyway. So we make no attempt to support // languages other than JS for language= nsAutoString lang(aAttributes[1]); if (nsContentUtils::IsJavaScriptLanguage(lang, &version)) { langID = nsIProgrammingLanguage::JAVASCRIPT; // Even when JS version < 1.6 is specified, E4X is // turned on in XUL. version = js::VersionSetXML(JSVersion(version), true); } } aAttributes += 2; } // Not all script languages have a "sandbox" concept. At time of // writing, Python is the only other language, and it does not. // For such languages, neither any inline script nor remote script are // safe to execute from untrusted sources. // So for such languages, we only allow script when the document // itself is from chrome. We then don't bother to check the // "src=" tag - we trust chrome to do the right thing. // (See also similar code in nsScriptLoader.cpp) nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument)); if (langID != nsIProgrammingLanguage::UNKNOWN && langID != nsIProgrammingLanguage::JAVASCRIPT && doc && !nsContentUtils::IsChromeDoc(doc)) { langID = nsIProgrammingLanguage::UNKNOWN; NS_WARNING("Non JS language called from non chrome - ignored"); } // Don't process scripts that aren't known if (langID != nsIProgrammingLanguage::UNKNOWN) { nsIScriptGlobalObject* globalObject = nsnull; // borrowed reference if (doc) globalObject = doc->GetScriptGlobalObject(); nsRefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(langID, aLineNumber, version); if (! script) return NS_ERROR_OUT_OF_MEMORY; // If there is a SRC attribute... if (! src.IsEmpty()) { // Use the SRC attribute value to load the URL rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nsnull, mDocumentURL); // Check if this document is allowed to load a script from this source // NOTE: if we ever allow scripts added via the DOM to run, we need to // add a CheckLoadURI call for that as well. if (NS_SUCCEEDED(rv)) { if (!mSecMan) mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv); if (NS_SUCCEEDED(rv)) { rv = mSecMan-> CheckLoadURIWithPrincipal(doc->NodePrincipal(), script->mSrcURI, nsIScriptSecurityManager::ALLOW_CHROME); } } } if (NS_FAILED(rv)) { return rv; } // Attempt to deserialize an out-of-line script from the FastLoad // file right away. Otherwise we'll end up reloading the script and // corrupting the FastLoad file trying to serialize it, in the case // where it's already there. if (globalObject) script->DeserializeOutOfLine(nsnull, globalObject); } nsPrototypeArray* children = nsnull; rv = mContextStack.GetTopChildren(&children); if (NS_FAILED(rv)) { return rv; } children->AppendElement(script); mConstrainSize = false; mContextStack.Push(script, mState); mState = eInScript; } return NS_OK; }