1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim: ft=cpp tw=78 sw=2 et ts=2
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications.
20 * Portions created by the Initial Developer are Copyright (C) 2001
21 * the Initial Developer. All Rights Reserved.
24 * Vidur Apparao <vidur@netscape.com> (original author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 * A class that handles loading and evaluation of <script> elements.
44 #include "nsScriptLoader.h"
45 #include "nsIDOMCharacterData.h"
46 #include "nsParserUtils.h"
47 #include "nsIMIMEHeaderParam.h"
48 #include "nsICharsetConverterManager.h"
49 #include "nsIUnicodeDecoder.h"
50 #include "nsIContent.h"
51 #include "nsGkAtoms.h"
52 #include "nsNetUtil.h"
53 #include "nsIScriptGlobalObject.h"
54 #include "nsIScriptContext.h"
55 #include "nsIScriptRuntime.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsIPrincipal.h"
58 #include "nsContentPolicyUtils.h"
59 #include "nsIDOMWindow.h"
60 #include "nsIHttpChannel.h"
61 #include "nsIScriptElement.h"
62 #include "nsIDOMHTMLScriptElement.h"
63 #include "nsIDocShell.h"
65 #include "nsContentUtils.h"
66 #include "nsUnicharUtils.h"
67 #include "nsAutoPtr.h"
68 #include "nsIXPConnect.h"
69 #include "nsContentErrors.h"
70 #include "nsIParser.h"
71 #include "nsThreadUtils.h"
73 //////////////////////////////////////////////////////////////
74 // Per-request data structure
75 //////////////////////////////////////////////////////////////
77 class nsScriptLoadRequest
: public nsISupports
{
79 nsScriptLoadRequest(nsIScriptElement
* aElement
,
84 mJSVersion(aVersion
), mLineNo(1)
90 void FireScriptAvailable(nsresult aResult
)
92 mElement
->ScriptAvailable(aResult
, mElement
, mIsInline
, mURI
, mLineNo
);
94 void FireScriptEvaluated(nsresult aResult
)
96 mElement
->ScriptEvaluated(aResult
, mElement
, mIsInline
);
101 return mElement
== nsnull
;
104 nsCOMPtr
<nsIScriptElement
> mElement
;
105 PRPackedBool mLoading
; // Are we still waiting for a load to complete?
106 PRPackedBool mDefer
; // Is execution defered?
107 PRPackedBool mIsInline
; // Is the script inline or loaded?
108 nsString mScriptText
; // Holds script for loaded scripts
110 nsCOMPtr
<nsIURI
> mURI
;
111 nsCOMPtr
<nsIURI
> mFinalURI
;
115 // The nsScriptLoadRequest is passed as the context to necko, and thus
116 // it needs to be threadsafe. Necko won't do anything with this
117 // context, but it will AddRef and Release it on other threads.
118 NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLoadRequest
)
120 //////////////////////////////////////////////////////////////
122 //////////////////////////////////////////////////////////////
124 nsScriptLoader::nsScriptLoader(nsIDocument
*aDocument
)
125 : mDocument(aDocument
),
131 nsScriptLoader::~nsScriptLoader()
135 for (PRInt32 i
= 0; i
< mRequests
.Count(); i
++) {
136 mRequests
[i
]->FireScriptAvailable(NS_ERROR_ABORT
);
139 // Unblock the kids, in case any of them moved to a different document
140 // subtree in the meantime and therefore aren't actually going away.
141 for (PRUint32 j
= 0; j
< mPendingChildLoaders
.Length(); ++j
) {
142 mPendingChildLoaders
[j
]->RemoveExecuteBlocker();
146 NS_IMPL_ISUPPORTS1(nsScriptLoader
, nsIStreamLoaderObserver
)
148 // Helper method for checking if the script element is an event-handler
149 // This means that it has both a for-attribute and a event-attribute.
150 // Also, if the for-attribute has a value that matches "\s*window\s*",
151 // and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
152 // eventhandler. (both matches are case insensitive).
153 // This is how IE seems to filter out a window's onload handler from a
154 // <script for=... event=...> element.
157 IsScriptEventHandler(nsIScriptElement
*aScriptElement
)
159 nsCOMPtr
<nsIContent
> contElement
= do_QueryInterface(aScriptElement
);
160 NS_ASSERTION(contElement
, "nsIScriptElement isn't nsIContent");
162 nsAutoString forAttr
, eventAttr
;
163 if (!contElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::_for
, forAttr
) ||
164 !contElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::event
, eventAttr
)) {
168 const nsAString
& for_str
= nsContentUtils::TrimWhitespace(forAttr
);
169 if (!for_str
.LowerCaseEqualsLiteral("window")) {
173 // We found for="window", now check for event="onload".
174 const nsAString
& event_str
= nsContentUtils::TrimWhitespace(eventAttr
, PR_FALSE
);
175 if (!StringBeginsWith(event_str
, NS_LITERAL_STRING("onload"),
176 nsCaseInsensitiveStringComparator())) {
177 // It ain't "onload.*".
182 nsAutoString::const_iterator start
, end
;
183 event_str
.BeginReading(start
);
184 event_str
.EndReading(end
);
186 start
.advance(6); // advance past "onload"
188 if (start
!= end
&& *start
!= '(' && *start
!= ' ') {
189 // We got onload followed by something other than space or
190 // '('. Not good enough.
199 nsScriptLoader::CheckContentPolicy(nsScriptLoadRequest
*aRequest
,
200 nsISupports
*aContext
,
201 const nsAString
&aType
)
203 PRInt16 shouldLoad
= nsIContentPolicy::ACCEPT
;
204 nsresult rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT
,
206 mDocument
->NodePrincipal(),
208 NS_LossyConvertUTF16toASCII(aType
),
211 nsContentUtils::GetContentPolicy(),
212 nsContentUtils::GetSecurityManager());
213 if (NS_FAILED(rv
) || NS_CP_REJECTED(shouldLoad
)) {
214 if (NS_FAILED(rv
) || shouldLoad
!= nsIContentPolicy::REJECT_TYPE
) {
215 return NS_ERROR_CONTENT_BLOCKED
;
217 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT
;
224 nsScriptLoader::StartLoad(nsScriptLoadRequest
*aRequest
, const nsAString
&aType
)
226 // Check that the containing page is allowed to load this URI.
227 nsresult rv
= nsContentUtils::GetSecurityManager()->
228 CheckLoadURIWithPrincipal(mDocument
->NodePrincipal(), aRequest
->mURI
,
229 nsIScriptSecurityManager::ALLOW_CHROME
);
231 NS_ENSURE_SUCCESS(rv
, rv
);
233 // After the security manager, the content-policy stuff gets a veto
234 nsISupports
*context
= aRequest
->mElement
.get()
235 ? static_cast<nsISupports
*>(aRequest
->mElement
.get())
236 : static_cast<nsISupports
*>(mDocument
);
237 rv
= CheckContentPolicy(aRequest
, context
, aType
);
242 nsCOMPtr
<nsILoadGroup
> loadGroup
= mDocument
->GetDocumentLoadGroup();
243 nsCOMPtr
<nsIStreamLoader
> loader
;
245 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(mDocument
->GetScriptGlobalObject()));
246 nsIDocShell
*docshell
= window
->GetDocShell();
248 nsCOMPtr
<nsIInterfaceRequestor
> prompter(do_QueryInterface(docshell
));
250 nsCOMPtr
<nsIChannel
> channel
;
251 rv
= NS_NewChannel(getter_AddRefs(channel
),
252 aRequest
->mURI
, nsnull
, loadGroup
,
253 prompter
, nsIRequest::LOAD_NORMAL
);
254 NS_ENSURE_SUCCESS(rv
, rv
);
256 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
258 // HTTP content negotation has little value in this context.
259 httpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
260 NS_LITERAL_CSTRING("*/*"),
262 httpChannel
->SetReferrer(mDocument
->GetDocumentURI());
265 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), this);
266 NS_ENSURE_SUCCESS(rv
, rv
);
268 return channel
->AsyncOpen(loader
, aRequest
);
272 nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo
&aPi
,
273 nsIURI
* const &aURI
) const
276 return NS_SUCCEEDED(aPi
.mRequest
->mURI
->Equals(aURI
, &same
)) &&
281 nsScriptLoader::ProcessScriptElement(nsIScriptElement
*aElement
)
283 // We need a document to evaluate scripts.
284 NS_ENSURE_TRUE(mDocument
, NS_ERROR_FAILURE
);
286 // Check to see if scripts has been turned off.
287 if (!mEnabled
|| !mDocument
->IsScriptEnabled()) {
288 return NS_ERROR_NOT_AVAILABLE
;
291 NS_ASSERTION(!aElement
->IsMalformed(), "Executing malformed script");
293 // Check that the script is not an eventhandler
294 if (IsScriptEventHandler(aElement
)) {
295 return NS_CONTENT_SCRIPT_IS_EVENTHANDLER
;
298 // Script evaluation can also be disabled in the current script
299 // context even though it's enabled in the document.
300 // XXX - still hard-coded for JS here, even though another language
301 // may be specified. Should this check be made *after* we examine
302 // the attributes to locate the script-type?
303 // For now though, if JS is disabled we assume every language is
305 // XXX is this different from the mDocument->IsScriptEnabled() call?
306 nsIScriptGlobalObject
*globalObject
= mDocument
->GetScriptGlobalObject();
308 return NS_ERROR_NOT_AVAILABLE
;
311 nsIScriptContext
*context
= globalObject
->GetScriptContext(
312 nsIProgrammingLanguage::JAVASCRIPT
);
314 // If scripts aren't enabled in the current context, there's no
315 // point in going on.
316 if (!context
|| !context
->GetScriptsEnabled()) {
317 return NS_ERROR_NOT_AVAILABLE
;
320 // Default script language is whatever the root content specifies
321 // (which may come from a header or http-meta tag), or if there
322 // is no root content, from the script global object.
323 nsCOMPtr
<nsIContent
> rootContent
= mDocument
->GetRootContent();
324 PRUint32 typeID
= rootContent
? rootContent
->GetScriptTypeID() :
325 context
->GetScriptTypeID();
326 PRUint32 version
= 0;
327 nsAutoString language
, type
, src
;
330 // Check the type attribute to determine language and version.
331 // If type exists, it trumps the deprecated 'language='
332 aElement
->GetScriptType(type
);
333 if (!type
.IsEmpty()) {
334 nsCOMPtr
<nsIMIMEHeaderParam
> mimeHdrParser
=
335 do_GetService("@mozilla.org/network/mime-hdrparam;1");
336 NS_ENSURE_TRUE(mimeHdrParser
, NS_ERROR_FAILURE
);
338 NS_ConvertUTF16toUTF8
typeAndParams(type
);
340 nsAutoString mimeType
;
341 rv
= mimeHdrParser
->GetParameter(typeAndParams
, nsnull
,
342 EmptyCString(), PR_FALSE
, nsnull
,
344 NS_ENSURE_SUCCESS(rv
, rv
);
346 // Javascript keeps the fast path, optimized for most-likely type
347 // Table ordered from most to least likely JS MIME types.
348 // See bug 62485, feel free to add <script type="..."> survey data to it,
349 // or to a new bug once 62485 is closed.
350 static const char *jsTypes
[] = {
353 "application/javascript",
354 "application/ecmascript",
355 "application/x-javascript",
359 PRBool isJavaScript
= PR_FALSE
;
360 for (PRInt32 i
= 0; jsTypes
[i
]; i
++) {
361 if (mimeType
.LowerCaseEqualsASCII(jsTypes
[i
])) {
362 isJavaScript
= PR_TRUE
;
367 typeID
= nsIProgrammingLanguage::JAVASCRIPT
;
369 // Use the object factory to locate a matching language.
370 nsCOMPtr
<nsIScriptRuntime
> runtime
;
371 rv
= NS_GetScriptRuntime(mimeType
, getter_AddRefs(runtime
));
372 if (NS_FAILED(rv
) || runtime
== nsnull
) {
373 // Failed to get the explicitly specified language
374 NS_WARNING("Failed to find a scripting language");
375 typeID
= nsIProgrammingLanguage::UNKNOWN
;
377 typeID
= runtime
->GetScriptTypeID();
379 if (typeID
!= nsIProgrammingLanguage::UNKNOWN
) {
380 // Get the version string, and ensure the language supports it.
381 nsAutoString versionName
;
382 rv
= mimeHdrParser
->GetParameter(typeAndParams
, "version",
383 EmptyCString(), PR_FALSE
, nsnull
,
386 // no version attribute - version remains 0.
387 if (rv
!= NS_ERROR_INVALID_ARG
)
390 nsCOMPtr
<nsIScriptRuntime
> runtime
;
391 rv
= NS_GetScriptRuntimeByID(typeID
, getter_AddRefs(runtime
));
393 NS_ERROR("Failed to locate the language with this ID");
396 rv
= runtime
->ParseVersion(versionName
, &version
);
398 NS_WARNING("This script language version is not supported - ignored");
399 typeID
= nsIProgrammingLanguage::UNKNOWN
;
404 // Some js specifics yet to be abstracted.
405 if (typeID
== nsIProgrammingLanguage::JAVASCRIPT
) {
408 rv
= mimeHdrParser
->GetParameter(typeAndParams
, "e4x",
409 EmptyCString(), PR_FALSE
, nsnull
,
412 if (rv
!= NS_ERROR_INVALID_ARG
)
415 if (value
.Length() == 1 && value
[0] == '1')
416 // This means that we need to set JSOPTION_XML in the JS options.
417 // We re-use our knowledge of the implementation to reuse
418 // JSVERSION_HAS_XML as a safe version flag.
419 // If version has JSVERSION_UNKNOWN (-1), then this is still OK.
420 version
|= JSVERSION_HAS_XML
;
424 // no 'type=' element
425 // "language" is a deprecated attribute of HTML, so we check it only for
426 // HTML script elements.
427 nsCOMPtr
<nsIDOMHTMLScriptElement
> htmlScriptElement
=
428 do_QueryInterface(aElement
);
429 if (htmlScriptElement
) {
430 htmlScriptElement
->GetAttribute(NS_LITERAL_STRING("language"), language
);
431 if (!language
.IsEmpty()) {
432 if (nsParserUtils::IsJavaScriptLanguage(language
, &version
))
433 typeID
= nsIProgrammingLanguage::JAVASCRIPT
;
435 typeID
= nsIProgrammingLanguage::UNKNOWN
;
436 // IE, Opera, etc. do not respect language version, so neither should
437 // we at this late date in the browser wars saga. Note that this change
438 // affects HTML but not XUL or SVG (but note also that XUL has its own
439 // code to check nsParserUtils::IsJavaScriptLanguage -- that's probably
440 // a separate bug, one we may not be able to fix short of XUL2). See
441 // bug 255895 (https://bugzilla.mozilla.org/show_bug.cgi?id=255895).
442 NS_ASSERTION(JSVERSION_DEFAULT
== 0,
443 "We rely on all languages having 0 as a version default");
449 // If we don't know the language, we don't know how to evaluate
450 if (typeID
== nsIProgrammingLanguage::UNKNOWN
) {
451 return NS_ERROR_NOT_AVAILABLE
;
453 // If not from a chrome document (which is always trusted), we need some way
454 // of checking the language is "safe". Currently the only other language
455 // impl is Python, and that is *not* safe in untrusted code - so fixing
456 // this isn't a priority.!
457 // See also similar code in nsXULContentSink.cpp
458 if (typeID
!= nsIProgrammingLanguage::JAVASCRIPT
&&
459 !nsContentUtils::IsChromeDoc(mDocument
)) {
460 NS_WARNING("Untrusted language called from non-chrome - ignored");
461 return NS_ERROR_NOT_AVAILABLE
;
464 nsCOMPtr
<nsIContent
> eltContent(do_QueryInterface(aElement
));
465 eltContent
->SetScriptTypeID(typeID
);
467 PRBool hadPendingRequests
= !!GetFirstPendingRequest();
469 // Did we preload this request?
470 nsCOMPtr
<nsIURI
> scriptURI
= aElement
->GetScriptURI();
471 nsRefPtr
<nsScriptLoadRequest
> request
;
473 nsTArray
<PreloadInfo
>::index_type i
=
474 mPreloads
.IndexOf(scriptURI
.get(), 0, PreloadURIComparator());
475 if (i
!= nsTArray
<PreloadInfo
>::NoIndex
) {
476 request
= mPreloads
[i
].mRequest
;
477 request
->mElement
= aElement
;
478 request
->mJSVersion
= version
;
479 request
->mDefer
= mDeferEnabled
&& aElement
->GetScriptDeferred();
480 mPreloads
.RemoveElementAt(i
);
482 rv
= CheckContentPolicy(request
, aElement
, type
);
484 // Note, we're dropping our last ref to request here.
488 if (!request
->mLoading
&& !request
->mDefer
&& !hadPendingRequests
&&
489 ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript()) {
490 return ProcessRequest(request
);
493 // Not done loading yet. Move into the real requests queue and wait.
494 mRequests
.AppendObject(request
);
496 if (!request
->mLoading
&& !hadPendingRequests
&& ReadyToExecuteScripts() &&
498 nsContentUtils::AddScriptRunner(new nsRunnableMethod
<nsScriptLoader
>(this,
499 &nsScriptLoader::ProcessPendingRequests
));
502 return request
->mDefer
? NS_OK
: NS_ERROR_HTMLPARSER_BLOCK
;
506 // Create a request object for this script
507 request
= new nsScriptLoadRequest(aElement
, version
);
508 NS_ENSURE_TRUE(request
, NS_ERROR_OUT_OF_MEMORY
);
510 request
->mDefer
= mDeferEnabled
&& aElement
->GetScriptDeferred();
512 // First check to see if this is an external script
514 request
->mURI
= scriptURI
;
515 request
->mIsInline
= PR_FALSE
;
516 request
->mLoading
= PR_TRUE
;
518 rv
= StartLoad(request
, type
);
523 request
->mLoading
= PR_FALSE
;
524 request
->mIsInline
= PR_TRUE
;
525 request
->mURI
= mDocument
->GetDocumentURI();
527 request
->mLineNo
= aElement
->GetScriptLineNumber();
529 // If we've got existing pending requests, add ourselves
531 if (!request
->mDefer
&& !hadPendingRequests
&&
532 ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript()) {
533 return ProcessRequest(request
);
537 // Add the request to our requests list
538 NS_ENSURE_TRUE(mRequests
.AppendObject(request
),
539 NS_ERROR_OUT_OF_MEMORY
);
541 if (request
->mDefer
) {
545 // If there weren't any pending requests before, and this one is
546 // ready to execute, do that as soon as it's safe.
547 if (!request
->mLoading
&& !hadPendingRequests
&& ReadyToExecuteScripts()) {
548 nsContentUtils::AddScriptRunner(new nsRunnableMethod
<nsScriptLoader
>(this,
549 &nsScriptLoader::ProcessPendingRequests
));
552 // Added as pending request, now we can send blocking back
553 return NS_ERROR_HTMLPARSER_BLOCK
;
557 nsScriptLoader::ProcessRequest(nsScriptLoadRequest
* aRequest
)
559 NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(),
560 "Caller forgot to check ReadyToExecuteScripts()");
562 NS_ENSURE_ARG(aRequest
);
563 nsAFlatString
* script
;
564 nsAutoString textData
;
566 // If there's no script text, we try to get it from the element
567 if (aRequest
->mIsInline
) {
568 // XXX This is inefficient - GetText makes multiple
570 aRequest
->mElement
->GetScriptText(textData
);
575 script
= &aRequest
->mScriptText
;
578 FireScriptAvailable(NS_OK
, aRequest
);
579 nsresult rv
= EvaluateScript(aRequest
, *script
);
580 FireScriptEvaluated(rv
, aRequest
);
586 nsScriptLoader::FireScriptAvailable(nsresult aResult
,
587 nsScriptLoadRequest
* aRequest
)
589 for (PRInt32 i
= 0; i
< mObservers
.Count(); i
++) {
590 nsCOMPtr
<nsIScriptLoaderObserver
> obs
= mObservers
[i
];
591 obs
->ScriptAvailable(aResult
, aRequest
->mElement
,
592 aRequest
->mIsInline
, aRequest
->mURI
,
596 aRequest
->FireScriptAvailable(aResult
);
600 nsScriptLoader::FireScriptEvaluated(nsresult aResult
,
601 nsScriptLoadRequest
* aRequest
)
603 for (PRInt32 i
= 0; i
< mObservers
.Count(); i
++) {
604 nsCOMPtr
<nsIScriptLoaderObserver
> obs
= mObservers
[i
];
605 obs
->ScriptEvaluated(aResult
, aRequest
->mElement
,
606 aRequest
->mIsInline
);
609 aRequest
->FireScriptEvaluated(aResult
);
613 nsScriptLoader::EvaluateScript(nsScriptLoadRequest
* aRequest
,
614 const nsAFlatString
& aScript
)
618 // We need a document to evaluate scripts.
620 return NS_ERROR_FAILURE
;
623 nsPIDOMWindow
*pwin
= mDocument
->GetInnerWindow();
624 if (!pwin
|| !pwin
->IsInnerWindow()) {
625 return NS_ERROR_FAILURE
;
627 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
= do_QueryInterface(pwin
);
628 NS_ASSERTION(globalObject
, "windows must be global objects");
630 // Get the script-type to be used by this element.
631 nsCOMPtr
<nsIContent
> scriptContent(do_QueryInterface(aRequest
->mElement
));
632 NS_ASSERTION(scriptContent
, "no content - what is default script-type?");
633 PRUint32 stid
= scriptContent
? scriptContent
->GetScriptTypeID() :
634 nsIProgrammingLanguage::JAVASCRIPT
;
635 // and make sure we are setup for this type of script.
636 rv
= globalObject
->EnsureScriptEnvironment(stid
);
640 // Make sure context is a strong reference since we access it after
641 // we've executed a script, which may cause all other references to
642 // the context to go away.
643 nsCOMPtr
<nsIScriptContext
> context
= globalObject
->GetScriptContext(stid
);
645 return NS_ERROR_FAILURE
;
648 nsIURI
* uri
= aRequest
->mFinalURI
? aRequest
->mFinalURI
: aRequest
->mURI
;
650 PRBool oldProcessingScriptTag
= context
->GetProcessingScriptTag();
651 context
->SetProcessingScriptTag(PR_TRUE
);
653 // Update our current script.
654 nsCOMPtr
<nsIScriptElement
> oldCurrent
= mCurrentScript
;
655 mCurrentScript
= aRequest
->mElement
;
658 nsContentUtils::GetWrapperSafeScriptFilename(mDocument
, uri
, url
);
661 rv
= context
->EvaluateString(aScript
,
662 globalObject
->GetScriptGlobal(stid
),
663 mDocument
->NodePrincipal(), url
.get(),
664 aRequest
->mLineNo
, aRequest
->mJSVersion
, nsnull
,
667 // Put the old script back in case it wants to do anything else.
668 mCurrentScript
= oldCurrent
;
670 JSContext
*cx
= nsnull
; // Initialize this to keep GCC happy.
671 if (stid
== nsIProgrammingLanguage::JAVASCRIPT
) {
672 cx
= (JSContext
*)context
->GetNativeContext();
673 ::JS_BeginRequest(cx
);
674 ::JS_ReportPendingException(cx
);
677 context
->SetProcessingScriptTag(oldProcessingScriptTag
);
679 if (stid
== nsIProgrammingLanguage::JAVASCRIPT
) {
680 NS_ASSERTION(!::JS_IsExceptionPending(cx
),
681 "JS_ReportPendingException wasn't called");
688 nsScriptLoader::GetFirstPendingRequest()
690 for (PRInt32 i
= 0; i
< mRequests
.Count(); ++i
) {
691 if (!mRequests
[i
]->mDefer
) {
700 nsScriptLoader::ProcessPendingRequestsAsync()
702 if (GetFirstPendingRequest() || !mPendingChildLoaders
.IsEmpty()) {
703 nsCOMPtr
<nsIRunnable
> ev
= new nsRunnableMethod
<nsScriptLoader
>(this,
704 &nsScriptLoader::ProcessPendingRequests
);
706 NS_DispatchToCurrentThread(ev
);
711 nsScriptLoader::ProcessPendingRequests()
713 nsRefPtr
<nsScriptLoadRequest
> request
;
714 while (ReadyToExecuteScripts() &&
715 (request
= GetFirstPendingRequest()) &&
716 !request
->mLoading
) {
717 mRequests
.RemoveObject(request
);
718 ProcessRequest(request
);
721 while (!mPendingChildLoaders
.IsEmpty() && ReadyToExecuteScripts()) {
722 nsRefPtr
<nsScriptLoader
> child
= mPendingChildLoaders
[0];
723 mPendingChildLoaders
.RemoveElementAt(0);
724 child
->RemoveExecuteBlocker();
729 nsScriptLoader::ReadyToExecuteScripts()
731 // Make sure the SelfReadyToExecuteScripts check is first, so that
732 // we don't block twice on an ancestor.
733 if (!SelfReadyToExecuteScripts()) {
737 for (nsIDocument
* doc
= mDocument
; doc
; doc
= doc
->GetParentDocument()) {
738 nsScriptLoader
* ancestor
= doc
->ScriptLoader();
739 if (!ancestor
->SelfReadyToExecuteScripts() &&
740 ancestor
->AddPendingChildLoader(this)) {
750 // This function was copied from nsParser.cpp. It was simplified a bit.
752 DetectByteOrderMark(const unsigned char* aBytes
, PRInt32 aLen
, nsCString
& oCharset
)
759 if (aLen
>= 3 && 0xBB == aBytes
[1] && 0xBF == aBytes
[2]) {
762 oCharset
.Assign("UTF-8");
766 if (0xFF == aBytes
[1]) {
768 // UTF-16, big-endian
769 oCharset
.Assign("UTF-16BE");
773 if (0xFE == aBytes
[1]) {
775 // UTF-16, little-endian
776 oCharset
.Assign("UTF-16LE");
780 return !oCharset
.IsEmpty();
783 /* static */ nsresult
784 nsScriptLoader::ConvertToUTF16(nsIChannel
* aChannel
, const PRUint8
* aData
,
785 PRUint32 aLength
, const nsString
& aHintCharset
,
786 nsIDocument
* aDocument
, nsString
& aString
)
793 nsCAutoString characterSet
;
797 rv
= aChannel
->GetContentCharset(characterSet
);
800 if (!aHintCharset
.IsEmpty() && (NS_FAILED(rv
) || characterSet
.IsEmpty())) {
801 // charset name is always ASCII.
802 LossyCopyUTF16toASCII(aHintCharset
, characterSet
);
805 if (NS_FAILED(rv
) || characterSet
.IsEmpty()) {
806 DetectByteOrderMark(aData
, aLength
, characterSet
);
809 if (characterSet
.IsEmpty()) {
810 // charset from document default
811 characterSet
= aDocument
->GetDocumentCharacterSet();
814 if (characterSet
.IsEmpty()) {
815 // fall back to ISO-8859-1, see bug 118404
816 characterSet
.AssignLiteral("ISO-8859-1");
819 nsCOMPtr
<nsICharsetConverterManager
> charsetConv
=
820 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID
, &rv
);
822 nsCOMPtr
<nsIUnicodeDecoder
> unicodeDecoder
;
824 if (NS_SUCCEEDED(rv
) && charsetConv
) {
825 rv
= charsetConv
->GetUnicodeDecoder(characterSet
.get(),
826 getter_AddRefs(unicodeDecoder
));
828 // fall back to ISO-8859-1 if charset is not supported. (bug 230104)
829 rv
= charsetConv
->GetUnicodeDecoderRaw("ISO-8859-1",
830 getter_AddRefs(unicodeDecoder
));
834 // converts from the charset to unicode
835 if (NS_SUCCEEDED(rv
)) {
836 PRInt32 unicodeLength
= 0;
838 rv
= unicodeDecoder
->GetMaxLength(reinterpret_cast<const char*>(aData
),
839 aLength
, &unicodeLength
);
840 if (NS_SUCCEEDED(rv
)) {
841 if (!EnsureStringLength(aString
, unicodeLength
))
842 return NS_ERROR_OUT_OF_MEMORY
;
844 PRUnichar
*ustr
= aString
.BeginWriting();
846 PRInt32 consumedLength
= 0;
847 PRInt32 originalLength
= aLength
;
848 PRInt32 convertedLength
= 0;
849 PRInt32 bufferLength
= unicodeLength
;
851 rv
= unicodeDecoder
->Convert(reinterpret_cast<const char*>(aData
),
852 (PRInt32
*) &aLength
, ustr
,
855 // if we failed, we consume one byte, replace it with U+FFFD
856 // and try the conversion again.
857 ustr
[unicodeLength
++] = (PRUnichar
)0xFFFD;
858 ustr
+= unicodeLength
;
860 unicodeDecoder
->Reset();
863 consumedLength
+= aLength
;
864 aLength
= originalLength
- consumedLength
;
865 convertedLength
+= unicodeLength
;
866 unicodeLength
= bufferLength
- convertedLength
;
867 } while (NS_FAILED(rv
) && (originalLength
> consumedLength
) && (bufferLength
> convertedLength
));
868 aString
.SetLength(convertedLength
);
875 nsScriptLoader::OnStreamComplete(nsIStreamLoader
* aLoader
,
876 nsISupports
* aContext
,
879 const PRUint8
* aString
)
881 nsScriptLoadRequest
* request
= static_cast<nsScriptLoadRequest
*>(aContext
);
882 NS_ASSERTION(request
, "null request in stream complete handler");
883 NS_ENSURE_TRUE(request
, NS_ERROR_FAILURE
);
885 nsresult rv
= PrepareLoadedRequest(request
, aLoader
, aStatus
, aStringLen
,
888 if (!mRequests
.RemoveObject(request
)) {
889 mPreloads
.RemoveElement(request
, PreloadRequestComparator());
891 FireScriptAvailable(rv
, request
);
895 // Process our request and/or any pending ones
896 ProcessPendingRequests();
902 nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest
* aRequest
,
903 nsIStreamLoader
* aLoader
,
906 const PRUint8
* aString
)
908 if (NS_FAILED(aStatus
)) {
912 // If we don't have a document, then we need to abort further
915 return NS_ERROR_NOT_AVAILABLE
;
918 // If the load returned an error page, then we need to abort
919 nsCOMPtr
<nsIRequest
> req
;
920 nsresult rv
= aLoader
->GetRequest(getter_AddRefs(req
));
921 NS_ASSERTION(req
, "StreamLoader's request went away prematurely");
922 NS_ENSURE_SUCCESS(rv
, rv
);
924 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(req
);
926 PRBool requestSucceeded
;
927 rv
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
928 if (NS_SUCCEEDED(rv
) && !requestSucceeded
) {
929 return NS_ERROR_NOT_AVAILABLE
;
933 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(req
);
934 NS_GetFinalChannelURI(channel
, getter_AddRefs(aRequest
->mFinalURI
));
936 // Check the charset attribute to determine script charset.
937 nsAutoString hintCharset
;
938 if (!aRequest
->IsPreload()) {
939 aRequest
->mElement
->GetScriptCharset(hintCharset
);
941 nsTArray
<PreloadInfo
>::index_type i
=
942 mPreloads
.IndexOf(aRequest
, 0, PreloadRequestComparator());
943 NS_ASSERTION(i
!= mPreloads
.NoIndex
, "Incorrect preload bookkeeping");
944 hintCharset
= mPreloads
[i
].mCharset
;
946 rv
= ConvertToUTF16(channel
, aString
, aStringLen
, hintCharset
, mDocument
,
947 aRequest
->mScriptText
);
949 NS_ENSURE_SUCCESS(rv
, rv
);
951 if (!ShouldExecuteScript(mDocument
, channel
)) {
952 return NS_ERROR_NOT_AVAILABLE
;
956 // This assertion could fire errorously if we ran out of memory when
957 // inserting the request in the array. However it's an unlikely case
958 // so if you see this assertion it is likely something else that is
959 // wrong, especially if you see it more than once.
960 NS_ASSERTION(mRequests
.IndexOf(aRequest
) >= 0 ||
961 mPreloads
.Contains(aRequest
, PreloadRequestComparator()),
962 "aRequest should be pending!");
964 // Mark this as loaded
965 aRequest
->mLoading
= PR_FALSE
;
972 nsScriptLoader::ShouldExecuteScript(nsIDocument
* aDocument
,
973 nsIChannel
* aChannel
)
980 nsIPrincipal
* docPrincipal
= aDocument
->NodePrincipal();
981 docPrincipal
->GetHasCertificate(&hasCert
);
986 nsCOMPtr
<nsIPrincipal
> channelPrincipal
;
987 nsresult rv
= nsContentUtils::GetSecurityManager()->
988 GetChannelPrincipal(aChannel
, getter_AddRefs(channelPrincipal
));
989 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
991 NS_ASSERTION(channelPrincipal
, "Gotta have a principal here!");
993 // If the channel principal isn't at least as powerful as the
994 // document principal, then we don't execute the script.
996 rv
= channelPrincipal
->Subsumes(docPrincipal
, &subsumes
);
997 return NS_SUCCEEDED(rv
) && subsumes
;
1001 nsScriptLoader::EndDeferringScripts()
1003 mDeferEnabled
= PR_FALSE
;
1004 for (PRUint32 i
= 0; i
< (PRUint32
)mRequests
.Count(); ++i
) {
1005 mRequests
[i
]->mDefer
= PR_FALSE
;
1008 ProcessPendingRequests();
1012 nsScriptLoader::PreloadURI(nsIURI
*aURI
, const nsAString
&aCharset
,
1013 const nsAString
&aType
)
1015 nsRefPtr
<nsScriptLoadRequest
> request
= new nsScriptLoadRequest(nsnull
, 0);
1020 request
->mURI
= aURI
;
1021 request
->mIsInline
= PR_FALSE
;
1022 request
->mLoading
= PR_TRUE
;
1023 request
->mDefer
= PR_FALSE
; // This is computed later when we go to execute the
1025 nsresult rv
= StartLoad(request
, aType
);
1026 if (NS_FAILED(rv
)) {
1030 PreloadInfo
*pi
= mPreloads
.AppendElement();
1031 pi
->mRequest
= request
;
1032 pi
->mCharset
= aCharset
;