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
5 : *
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/
10 : *
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
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Vidur Apparao <vidur@netscape.com> (original author)
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * A class that handles loading and evaluation of <script> elements.
42 : */
43 :
44 : #include "jsapi.h"
45 : #include "jsfriendapi.h"
46 : #include "nsScriptLoader.h"
47 : #include "nsICharsetConverterManager.h"
48 : #include "nsIUnicodeDecoder.h"
49 : #include "nsIContent.h"
50 : #include "mozilla/dom/Element.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 "nsIHttpChannel.h"
60 : #include "nsIScriptElement.h"
61 : #include "nsIDOMHTMLScriptElement.h"
62 : #include "nsIDocShell.h"
63 : #include "nsContentUtils.h"
64 : #include "nsUnicharUtils.h"
65 : #include "nsAutoPtr.h"
66 : #include "nsIXPConnect.h"
67 : #include "nsContentErrors.h"
68 : #include "nsIParser.h"
69 : #include "nsThreadUtils.h"
70 : #include "nsDocShellCID.h"
71 : #include "nsIContentSecurityPolicy.h"
72 : #include "prlog.h"
73 : #include "nsIChannelPolicy.h"
74 : #include "nsChannelPolicy.h"
75 : #include "nsCRT.h"
76 : #include "nsContentCreatorFunctions.h"
77 : #include "nsGenericElement.h"
78 : #include "nsCrossSiteListenerProxy.h"
79 :
80 : #include "mozilla/FunctionTimer.h"
81 : #include "mozilla/CORSMode.h"
82 :
83 : #ifdef PR_LOGGING
84 : static PRLogModuleInfo* gCspPRLog;
85 : #endif
86 :
87 : using namespace mozilla;
88 : using namespace mozilla::dom;
89 :
90 : //////////////////////////////////////////////////////////////
91 : // Per-request data structure
92 : //////////////////////////////////////////////////////////////
93 :
94 0 : class nsScriptLoadRequest : public nsISupports {
95 : public:
96 0 : nsScriptLoadRequest(nsIScriptElement* aElement,
97 : PRUint32 aVersion,
98 : CORSMode aCORSMode)
99 : : mElement(aElement),
100 : mLoading(true),
101 : mIsInline(true),
102 : mJSVersion(aVersion),
103 : mLineNo(1),
104 0 : mCORSMode(aCORSMode)
105 : {
106 0 : }
107 :
108 : NS_DECL_ISUPPORTS
109 :
110 0 : void FireScriptAvailable(nsresult aResult)
111 : {
112 0 : mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
113 0 : }
114 0 : void FireScriptEvaluated(nsresult aResult)
115 : {
116 0 : mElement->ScriptEvaluated(aResult, mElement, mIsInline);
117 0 : }
118 :
119 0 : bool IsPreload()
120 : {
121 0 : return mElement == nsnull;
122 : }
123 :
124 : nsCOMPtr<nsIScriptElement> mElement;
125 : bool mLoading; // Are we still waiting for a load to complete?
126 : bool mIsInline; // Is the script inline or loaded?
127 : nsString mScriptText; // Holds script for loaded scripts
128 : PRUint32 mJSVersion;
129 : nsCOMPtr<nsIURI> mURI;
130 : nsCOMPtr<nsIPrincipal> mOriginPrincipal;
131 : PRInt32 mLineNo;
132 : const CORSMode mCORSMode;
133 : };
134 :
135 : // The nsScriptLoadRequest is passed as the context to necko, and thus
136 : // it needs to be threadsafe. Necko won't do anything with this
137 : // context, but it will AddRef and Release it on other threads.
138 0 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLoadRequest)
139 :
140 : //////////////////////////////////////////////////////////////
141 : //
142 : //////////////////////////////////////////////////////////////
143 :
144 1273 : nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
145 : : mDocument(aDocument),
146 : mBlockerCount(0),
147 : mEnabled(true),
148 : mDeferEnabled(false),
149 1273 : mDocumentParsingDone(false)
150 : {
151 : // enable logging for CSP
152 : #ifdef PR_LOGGING
153 1273 : if (!gCspPRLog)
154 249 : gCspPRLog = PR_NewLogModule("CSP");
155 : #endif
156 1273 : }
157 :
158 3813 : nsScriptLoader::~nsScriptLoader()
159 : {
160 1271 : mObservers.Clear();
161 :
162 1271 : if (mParserBlockingRequest) {
163 0 : mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
164 : }
165 :
166 1271 : for (PRUint32 i = 0; i < mXSLTRequests.Length(); i++) {
167 0 : mXSLTRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
168 : }
169 :
170 1271 : for (PRUint32 i = 0; i < mDeferRequests.Length(); i++) {
171 0 : mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
172 : }
173 :
174 1271 : for (PRUint32 i = 0; i < mAsyncRequests.Length(); i++) {
175 0 : mAsyncRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
176 : }
177 :
178 1271 : for (PRUint32 i = 0; i < mNonAsyncExternalScriptInsertedRequests.Length(); i++) {
179 0 : mNonAsyncExternalScriptInsertedRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
180 : }
181 :
182 : // Unblock the kids, in case any of them moved to a different document
183 : // subtree in the meantime and therefore aren't actually going away.
184 1271 : for (PRUint32 j = 0; j < mPendingChildLoaders.Length(); ++j) {
185 0 : mPendingChildLoaders[j]->RemoveExecuteBlocker();
186 : }
187 5084 : }
188 :
189 6552 : NS_IMPL_ISUPPORTS1(nsScriptLoader, nsIStreamLoaderObserver)
190 :
191 : // Helper method for checking if the script element is an event-handler
192 : // This means that it has both a for-attribute and a event-attribute.
193 : // Also, if the for-attribute has a value that matches "\s*window\s*",
194 : // and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
195 : // eventhandler. (both matches are case insensitive).
196 : // This is how IE seems to filter out a window's onload handler from a
197 : // <script for=... event=...> element.
198 :
199 : static bool
200 0 : IsScriptEventHandler(nsIContent* aScriptElement)
201 : {
202 0 : if (!aScriptElement->IsHTML()) {
203 0 : return false;
204 : }
205 :
206 0 : nsAutoString forAttr, eventAttr;
207 0 : if (!aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, forAttr) ||
208 0 : !aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, eventAttr)) {
209 0 : return false;
210 : }
211 :
212 : const nsAString& for_str =
213 0 : nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr);
214 0 : if (!for_str.LowerCaseEqualsLiteral("window")) {
215 0 : return true;
216 : }
217 :
218 : // We found for="window", now check for event="onload".
219 : const nsAString& event_str =
220 0 : nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr, false);
221 0 : if (!StringBeginsWith(event_str, NS_LITERAL_STRING("onload"),
222 0 : nsCaseInsensitiveStringComparator())) {
223 : // It ain't "onload.*".
224 :
225 0 : return true;
226 : }
227 :
228 0 : nsAutoString::const_iterator start, end;
229 0 : event_str.BeginReading(start);
230 0 : event_str.EndReading(end);
231 :
232 0 : start.advance(6); // advance past "onload"
233 :
234 0 : if (start != end && *start != '(' && *start != ' ') {
235 : // We got onload followed by something other than space or
236 : // '('. Not good enough.
237 :
238 0 : return true;
239 : }
240 :
241 0 : return false;
242 : }
243 :
244 : nsresult
245 0 : nsScriptLoader::CheckContentPolicy(nsIDocument* aDocument,
246 : nsISupports *aContext,
247 : nsIURI *aURI,
248 : const nsAString &aType)
249 : {
250 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
251 : nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
252 : aURI,
253 : aDocument->NodePrincipal(),
254 : aContext,
255 0 : NS_LossyConvertUTF16toASCII(aType),
256 : nsnull, //extra
257 : &shouldLoad,
258 : nsContentUtils::GetContentPolicy(),
259 0 : nsContentUtils::GetSecurityManager());
260 0 : if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
261 0 : if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
262 0 : return NS_ERROR_CONTENT_BLOCKED;
263 : }
264 0 : return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
265 : }
266 :
267 0 : return NS_OK;
268 : }
269 :
270 : nsresult
271 0 : nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
272 : nsISupports* aContext,
273 : nsIURI* aURI,
274 : const nsAString &aType)
275 : {
276 : // Check that the containing page is allowed to load this URI.
277 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
278 : CheckLoadURIWithPrincipal(aDocument->NodePrincipal(), aURI,
279 0 : nsIScriptSecurityManager::ALLOW_CHROME);
280 :
281 0 : NS_ENSURE_SUCCESS(rv, rv);
282 :
283 : // After the security manager, the content-policy stuff gets a veto
284 0 : rv = CheckContentPolicy(aDocument, aContext, aURI, aType);
285 0 : if (NS_FAILED(rv)) {
286 0 : return rv;
287 : }
288 :
289 0 : return NS_OK;
290 : }
291 :
292 : nsresult
293 0 : nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType)
294 : {
295 0 : nsISupports *context = aRequest->mElement.get()
296 0 : ? static_cast<nsISupports *>(aRequest->mElement.get())
297 0 : : static_cast<nsISupports *>(mDocument);
298 0 : nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType);
299 0 : if (NS_FAILED(rv)) {
300 0 : return rv;
301 : }
302 :
303 0 : nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
304 :
305 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->GetScriptGlobalObject()));
306 0 : if (!window) {
307 0 : return NS_ERROR_NULL_POINTER;
308 : }
309 :
310 0 : nsIDocShell *docshell = window->GetDocShell();
311 :
312 0 : nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
313 :
314 : // check for a Content Security Policy to pass down to the channel
315 : // that will be created to load the script
316 0 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
317 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
318 0 : rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
319 0 : NS_ENSURE_SUCCESS(rv, rv);
320 0 : if (csp) {
321 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
322 0 : channelPolicy->SetContentSecurityPolicy(csp);
323 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
324 : }
325 :
326 0 : nsCOMPtr<nsIChannel> channel;
327 0 : rv = NS_NewChannel(getter_AddRefs(channel),
328 : aRequest->mURI, nsnull, loadGroup, prompter,
329 : nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
330 0 : channelPolicy);
331 0 : NS_ENSURE_SUCCESS(rv, rv);
332 :
333 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
334 0 : if (httpChannel) {
335 : // HTTP content negotation has little value in this context.
336 0 : httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
337 0 : NS_LITERAL_CSTRING("*/*"),
338 0 : false);
339 0 : httpChannel->SetReferrer(mDocument->GetDocumentURI());
340 : }
341 :
342 0 : nsCOMPtr<nsIStreamLoader> loader;
343 0 : rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
344 0 : NS_ENSURE_SUCCESS(rv, rv);
345 :
346 0 : nsCOMPtr<nsIStreamListener> listener = loader.get();
347 :
348 0 : if (aRequest->mCORSMode != CORS_NONE) {
349 0 : bool withCredentials = (aRequest->mCORSMode == CORS_USE_CREDENTIALS);
350 : listener =
351 0 : new nsCORSListenerProxy(listener, mDocument->NodePrincipal(), channel,
352 0 : withCredentials, &rv);
353 0 : NS_ENSURE_SUCCESS(rv, rv);
354 : }
355 :
356 0 : rv = channel->AsyncOpen(listener, aRequest);
357 0 : NS_ENSURE_SUCCESS(rv, rv);
358 :
359 0 : return NS_OK;
360 : }
361 :
362 : bool
363 0 : nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi,
364 : nsIURI * const &aURI) const
365 : {
366 : bool same;
367 0 : return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) &&
368 0 : same;
369 : }
370 :
371 : class nsScriptRequestProcessor : public nsRunnable
372 0 : {
373 : private:
374 : nsRefPtr<nsScriptLoader> mLoader;
375 : nsRefPtr<nsScriptLoadRequest> mRequest;
376 : public:
377 0 : nsScriptRequestProcessor(nsScriptLoader* aLoader,
378 : nsScriptLoadRequest* aRequest)
379 : : mLoader(aLoader)
380 0 : , mRequest(aRequest)
381 0 : {}
382 0 : NS_IMETHODIMP Run()
383 : {
384 0 : return mLoader->ProcessRequest(mRequest);
385 : }
386 : };
387 :
388 : bool
389 0 : nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
390 : {
391 : // We need a document to evaluate scripts.
392 0 : NS_ENSURE_TRUE(mDocument, false);
393 :
394 : // Check to see if scripts has been turned off.
395 0 : if (!mEnabled || !mDocument->IsScriptEnabled()) {
396 0 : return false;
397 : }
398 :
399 0 : NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");
400 :
401 0 : nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
402 :
403 : // Step 12. Check that the script is not an eventhandler
404 0 : if (IsScriptEventHandler(scriptContent)) {
405 0 : return false;
406 : }
407 :
408 : // Script evaluation can also be disabled in the current script
409 : // context even though it's enabled in the document.
410 : // XXX - still hard-coded for JS here, even though another language
411 : // may be specified. Should this check be made *after* we examine
412 : // the attributes to locate the script-type?
413 : // For now though, if JS is disabled we assume every language is
414 : // disabled.
415 : // XXX is this different from the mDocument->IsScriptEnabled() call?
416 0 : nsIScriptGlobalObject *globalObject = mDocument->GetScriptGlobalObject();
417 0 : if (!globalObject) {
418 0 : return false;
419 : }
420 :
421 : nsIScriptContext *context = globalObject->GetScriptContext(
422 0 : nsIProgrammingLanguage::JAVASCRIPT);
423 :
424 : // If scripts aren't enabled in the current context, there's no
425 : // point in going on.
426 0 : if (!context || !context->GetScriptsEnabled()) {
427 0 : return false;
428 : }
429 :
430 : // Default script language is whatever the root element specifies
431 : // (which may come from a header or http-meta tag), or if there
432 : // is no root element, from the script global object.
433 0 : Element* rootElement = mDocument->GetRootElement();
434 0 : PRUint32 typeID = rootElement ? rootElement->GetScriptTypeID() :
435 0 : context->GetScriptTypeID();
436 0 : PRUint32 version = 0;
437 0 : nsAutoString language, type, src;
438 0 : nsresult rv = NS_OK;
439 :
440 : // Check the type attribute to determine language and version.
441 : // If type exists, it trumps the deprecated 'language='
442 0 : aElement->GetScriptType(type);
443 0 : if (!type.IsEmpty()) {
444 0 : nsContentTypeParser parser(type);
445 :
446 0 : nsAutoString mimeType;
447 0 : rv = parser.GetType(mimeType);
448 0 : NS_ENSURE_SUCCESS(rv, false);
449 :
450 : // Javascript keeps the fast path, optimized for most-likely type
451 : // Table ordered from most to least likely JS MIME types.
452 : // See bug 62485, feel free to add <script type="..."> survey data to it,
453 : // or to a new bug once 62485 is closed.
454 : static const char *jsTypes[] = {
455 : "text/javascript",
456 : "text/ecmascript",
457 : "application/javascript",
458 : "application/ecmascript",
459 : "application/x-javascript",
460 : nsnull
461 : };
462 :
463 0 : bool isJavaScript = false;
464 0 : for (PRInt32 i = 0; jsTypes[i]; i++) {
465 0 : if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) {
466 0 : isJavaScript = true;
467 0 : break;
468 : }
469 : }
470 0 : if (isJavaScript)
471 0 : typeID = nsIProgrammingLanguage::JAVASCRIPT;
472 : else {
473 : // Use the object factory to locate a matching language.
474 0 : nsCOMPtr<nsIScriptRuntime> runtime;
475 0 : rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime));
476 0 : if (NS_FAILED(rv) || runtime == nsnull) {
477 : // Failed to get the explicitly specified language
478 0 : NS_WARNING("Failed to find a scripting language");
479 0 : typeID = nsIProgrammingLanguage::UNKNOWN;
480 : } else
481 0 : typeID = runtime->GetScriptTypeID();
482 : }
483 0 : if (typeID != nsIProgrammingLanguage::UNKNOWN) {
484 : // Get the version string, and ensure the language supports it.
485 0 : nsAutoString versionName;
486 0 : rv = parser.GetParameter("version", versionName);
487 0 : if (NS_FAILED(rv)) {
488 : // no version attribute - version remains 0.
489 0 : if (rv != NS_ERROR_INVALID_ARG)
490 0 : return false;
491 : } else {
492 0 : nsCOMPtr<nsIScriptRuntime> runtime;
493 0 : rv = NS_GetScriptRuntimeByID(typeID, getter_AddRefs(runtime));
494 0 : if (NS_FAILED(rv)) {
495 0 : NS_ERROR("Failed to locate the language with this ID");
496 0 : return false;
497 : }
498 0 : rv = runtime->ParseVersion(versionName, &version);
499 0 : if (NS_FAILED(rv)) {
500 0 : NS_WARNING("This script language version is not supported - ignored");
501 0 : typeID = nsIProgrammingLanguage::UNKNOWN;
502 : }
503 : }
504 : }
505 :
506 : // Some js specifics yet to be abstracted.
507 0 : if (typeID == nsIProgrammingLanguage::JAVASCRIPT) {
508 0 : nsAutoString value;
509 0 : rv = parser.GetParameter("e4x", value);
510 0 : if (NS_FAILED(rv)) {
511 0 : if (rv != NS_ERROR_INVALID_ARG)
512 0 : return false;
513 : } else {
514 0 : if (value.Length() == 1 && value[0] == '1')
515 : // This means that we need to set JSOPTION_XML in the JS options.
516 : // We re-use our knowledge of the implementation to reuse
517 : // JSVERSION_HAS_XML as a safe version flag.
518 : // If version has JSVERSION_UNKNOWN (-1), then this is still OK.
519 0 : version = js::VersionSetXML(JSVersion(version), true);
520 : }
521 : }
522 : } else {
523 : // no 'type=' element
524 : // "language" is a deprecated attribute of HTML, so we check it only for
525 : // HTML script elements.
526 0 : if (scriptContent->IsHTML()) {
527 0 : scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language);
528 0 : if (!language.IsEmpty()) {
529 0 : if (nsContentUtils::IsJavaScriptLanguage(language, &version))
530 0 : typeID = nsIProgrammingLanguage::JAVASCRIPT;
531 : else
532 0 : typeID = nsIProgrammingLanguage::UNKNOWN;
533 : // IE, Opera, etc. do not respect language version, so neither should
534 : // we at this late date in the browser wars saga. Note that this change
535 : // affects HTML but not XUL or SVG (but note also that XUL has its own
536 : // code to check nsContentUtils::IsJavaScriptLanguage -- that's probably
537 : // a separate bug, one we may not be able to fix short of XUL2). See
538 : // bug 255895 (https://bugzilla.mozilla.org/show_bug.cgi?id=255895).
539 : NS_ASSERTION(JSVERSION_DEFAULT == 0,
540 : "We rely on all languages having 0 as a version default");
541 0 : version = 0;
542 : }
543 : }
544 : }
545 :
546 : // If we don't know the language, we don't know how to evaluate
547 0 : if (typeID == nsIProgrammingLanguage::UNKNOWN) {
548 0 : return false;
549 : }
550 : // If not from a chrome document (which is always trusted), we need some way
551 : // of checking the language is "safe". Currently the only other language
552 : // impl is Python, and that is *not* safe in untrusted code - so fixing
553 : // this isn't a priority.!
554 : // See also similar code in nsXULContentSink.cpp
555 0 : if (typeID != nsIProgrammingLanguage::JAVASCRIPT &&
556 0 : !nsContentUtils::IsChromeDoc(mDocument)) {
557 0 : NS_WARNING("Untrusted language called from non-chrome - ignored");
558 0 : return false;
559 : }
560 :
561 0 : scriptContent->SetScriptTypeID(typeID);
562 :
563 : // Step 14. in the HTML5 spec
564 :
565 0 : nsRefPtr<nsScriptLoadRequest> request;
566 0 : if (aElement->GetScriptExternal()) {
567 : // external script
568 0 : nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
569 0 : if (!scriptURI) {
570 0 : return false;
571 : }
572 0 : CORSMode ourCORSMode = aElement->GetCORSMode();
573 : nsTArray<PreloadInfo>::index_type i =
574 0 : mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
575 0 : if (i != nsTArray<PreloadInfo>::NoIndex) {
576 : // preloaded
577 : // note that a script-inserted script can steal a preload!
578 0 : request = mPreloads[i].mRequest;
579 0 : request->mElement = aElement;
580 0 : nsString preloadCharset(mPreloads[i].mCharset);
581 0 : mPreloads.RemoveElementAt(i);
582 :
583 : // Double-check that the charset the preload used is the same as
584 : // the charset we have now.
585 0 : nsAutoString elementCharset;
586 0 : aElement->GetScriptCharset(elementCharset);
587 0 : if (elementCharset.Equals(preloadCharset) &&
588 0 : ourCORSMode == request->mCORSMode) {
589 0 : rv = CheckContentPolicy(mDocument, aElement, request->mURI, type);
590 0 : NS_ENSURE_SUCCESS(rv, false);
591 : } else {
592 : // Drop the preload
593 0 : request = nsnull;
594 : }
595 : }
596 :
597 0 : if (!request) {
598 : // no usable preload
599 0 : request = new nsScriptLoadRequest(aElement, version, ourCORSMode);
600 0 : request->mURI = scriptURI;
601 0 : request->mIsInline = false;
602 0 : request->mLoading = true;
603 0 : rv = StartLoad(request, type);
604 0 : NS_ENSURE_SUCCESS(rv, false);
605 : }
606 :
607 0 : request->mJSVersion = version;
608 :
609 0 : if (aElement->GetScriptAsync()) {
610 0 : mAsyncRequests.AppendElement(request);
611 0 : if (!request->mLoading) {
612 : // The script is available already. Run it ASAP when the event
613 : // loop gets a chance to spin.
614 0 : ProcessPendingRequestsAsync();
615 : }
616 0 : return false;
617 : }
618 0 : if (!aElement->GetParserCreated()) {
619 : // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
620 : // for RequireJS work with their Gecko-sniffed code path. See
621 : // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
622 0 : mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
623 0 : if (!request->mLoading) {
624 : // The script is available already. Run it ASAP when the event
625 : // loop gets a chance to spin.
626 0 : ProcessPendingRequestsAsync();
627 : }
628 0 : return false;
629 : }
630 : // we now have a parser-inserted request that may or may not be still
631 : // loading
632 0 : if (aElement->GetScriptDeferred()) {
633 : // We don't want to run this yet.
634 : // If we come here, the script is a parser-created script and it has
635 : // the defer attribute but not the async attribute. Since a
636 : // a parser-inserted script is being run, we came here by the parser
637 : // running the script, which means the parser is still alive and the
638 : // parse is ongoing.
639 0 : NS_ASSERTION(mDocument->GetCurrentContentSink() ||
640 : aElement->GetParserCreated() == FROM_PARSER_XSLT,
641 : "Non-XSLT Defer script on a document without an active parser; bug 592366.");
642 0 : mDeferRequests.AppendElement(request);
643 0 : return false;
644 : }
645 :
646 0 : if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
647 : // Need to maintain order for XSLT-inserted scripts
648 0 : NS_ASSERTION(!mParserBlockingRequest,
649 : "Parser-blocking scripts and XSLT scripts in the same doc!");
650 0 : mXSLTRequests.AppendElement(request);
651 0 : if (!request->mLoading) {
652 : // The script is available already. Run it ASAP when the event
653 : // loop gets a chance to spin.
654 0 : ProcessPendingRequestsAsync();
655 : }
656 0 : return true;
657 : }
658 0 : if (!request->mLoading && ReadyToExecuteScripts()) {
659 : // The request has already been loaded and there are no pending style
660 : // sheets. If the script comes from the network stream, cheat for
661 : // performance reasons and avoid a trip through the event loop.
662 0 : if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
663 0 : return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
664 : }
665 : // Otherwise, we've got a document.written script, make a trip through
666 : // the event loop to hide the preload effects from the scripts on the
667 : // Web page.
668 0 : NS_ASSERTION(!mParserBlockingRequest,
669 : "There can be only one parser-blocking script at a time");
670 0 : NS_ASSERTION(mXSLTRequests.IsEmpty(),
671 : "Parser-blocking scripts and XSLT scripts in the same doc!");
672 0 : mParserBlockingRequest = request;
673 0 : ProcessPendingRequestsAsync();
674 0 : return true;
675 : }
676 : // The script hasn't loaded yet or there's a style sheet blocking it.
677 : // The script will be run when it loads or the style sheet loads.
678 0 : NS_ASSERTION(!mParserBlockingRequest,
679 : "There can be only one parser-blocking script at a time");
680 0 : NS_ASSERTION(mXSLTRequests.IsEmpty(),
681 : "Parser-blocking scripts and XSLT scripts in the same doc!");
682 0 : mParserBlockingRequest = request;
683 0 : return true;
684 : }
685 :
686 : // inline script
687 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
688 0 : rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
689 0 : NS_ENSURE_SUCCESS(rv, false);
690 :
691 0 : if (csp) {
692 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
693 : bool inlineOK;
694 0 : rv = csp->GetAllowsInlineScript(&inlineOK);
695 0 : NS_ENSURE_SUCCESS(rv, false);
696 :
697 0 : if (!inlineOK) {
698 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
699 : // gather information to log with violation report
700 0 : nsIURI* uri = mDocument->GetDocumentURI();
701 0 : nsCAutoString asciiSpec;
702 0 : uri->GetAsciiSpec(asciiSpec);
703 0 : nsAutoString scriptText;
704 0 : aElement->GetScriptText(scriptText);
705 :
706 : // cap the length of the script sample at 40 chars
707 0 : if (scriptText.Length() > 40) {
708 0 : scriptText.Truncate(40);
709 0 : scriptText.Append(NS_LITERAL_STRING("..."));
710 : }
711 :
712 0 : csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
713 0 : NS_ConvertUTF8toUTF16(asciiSpec),
714 : scriptText,
715 0 : aElement->GetScriptLineNumber());
716 0 : return false;
717 : }
718 : }
719 :
720 : // Inline scripts ignore ther CORS mode and are always CORS_NONE
721 0 : request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
722 0 : request->mJSVersion = version;
723 0 : request->mLoading = false;
724 0 : request->mIsInline = true;
725 0 : request->mURI = mDocument->GetDocumentURI();
726 0 : request->mLineNo = aElement->GetScriptLineNumber();
727 :
728 0 : if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
729 0 : (!ReadyToExecuteScripts() || !mXSLTRequests.IsEmpty())) {
730 : // Need to maintain order for XSLT-inserted scripts
731 0 : NS_ASSERTION(!mParserBlockingRequest,
732 : "Parser-blocking scripts and XSLT scripts in the same doc!");
733 0 : mXSLTRequests.AppendElement(request);
734 0 : return true;
735 : }
736 0 : if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
737 0 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
738 : "A script-inserted script is inserted without an update batch?");
739 : nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this,
740 0 : request));
741 0 : return false;
742 : }
743 0 : if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
744 0 : !ReadyToExecuteScripts()) {
745 0 : NS_ASSERTION(!mParserBlockingRequest,
746 : "There can be only one parser-blocking script at a time");
747 0 : mParserBlockingRequest = request;
748 0 : NS_ASSERTION(mXSLTRequests.IsEmpty(),
749 : "Parser-blocking scripts and XSLT scripts in the same doc!");
750 0 : return true;
751 : }
752 : // We now have a document.written inline script or we have an inline script
753 : // from the network but there is no style sheet that is blocking scripts.
754 : // Don't check for style sheets blocking scripts in the document.write
755 : // case to avoid style sheet network activity affecting when
756 : // document.write returns. It's not really necessary to do this if
757 : // there's no document.write currently on the call stack. However,
758 : // this way matches IE more closely than checking if document.write
759 : // is on the call stack.
760 0 : NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
761 : "Not safe to run a parser-inserted script?");
762 0 : return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
763 : }
764 :
765 : nsresult
766 0 : nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
767 : {
768 0 : NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
769 : "Processing requests when running scripts is unsafe.");
770 :
771 0 : NS_ENSURE_ARG(aRequest);
772 : nsAFlatString* script;
773 0 : nsAutoString textData;
774 :
775 : NS_TIME_FUNCTION;
776 :
777 0 : nsCOMPtr<nsIDocument> doc;
778 :
779 0 : nsCOMPtr<nsINode> scriptElem = do_QueryInterface(aRequest->mElement);
780 :
781 : // If there's no script text, we try to get it from the element
782 0 : if (aRequest->mIsInline) {
783 : // XXX This is inefficient - GetText makes multiple
784 : // copies.
785 0 : aRequest->mElement->GetScriptText(textData);
786 :
787 0 : script = &textData;
788 : }
789 : else {
790 0 : script = &aRequest->mScriptText;
791 :
792 0 : doc = scriptElem->OwnerDoc();
793 : }
794 :
795 0 : nsCOMPtr<nsIScriptElement> oldParserInsertedScript;
796 0 : PRUint32 parserCreated = aRequest->mElement->GetParserCreated();
797 0 : if (parserCreated) {
798 0 : oldParserInsertedScript = mCurrentParserInsertedScript;
799 0 : mCurrentParserInsertedScript = aRequest->mElement;
800 : }
801 :
802 0 : FireScriptAvailable(NS_OK, aRequest);
803 :
804 0 : bool runScript = true;
805 : nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
806 : scriptElem,
807 0 : NS_LITERAL_STRING("beforescriptexecute"),
808 0 : true, true, &runScript);
809 :
810 0 : nsresult rv = NS_OK;
811 0 : if (runScript) {
812 0 : if (doc) {
813 0 : doc->BeginEvaluatingExternalScript();
814 : }
815 0 : aRequest->mElement->BeginEvaluating();
816 0 : rv = EvaluateScript(aRequest, *script);
817 0 : aRequest->mElement->EndEvaluating();
818 0 : if (doc) {
819 0 : doc->EndEvaluatingExternalScript();
820 : }
821 :
822 : nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
823 : scriptElem,
824 0 : NS_LITERAL_STRING("afterscriptexecute"),
825 0 : true, false);
826 : }
827 :
828 0 : FireScriptEvaluated(rv, aRequest);
829 :
830 0 : if (parserCreated) {
831 0 : mCurrentParserInsertedScript = oldParserInsertedScript;
832 : }
833 :
834 0 : return rv;
835 : }
836 :
837 : void
838 0 : nsScriptLoader::FireScriptAvailable(nsresult aResult,
839 : nsScriptLoadRequest* aRequest)
840 : {
841 0 : for (PRInt32 i = 0; i < mObservers.Count(); i++) {
842 0 : nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
843 0 : obs->ScriptAvailable(aResult, aRequest->mElement,
844 : aRequest->mIsInline, aRequest->mURI,
845 0 : aRequest->mLineNo);
846 : }
847 :
848 0 : aRequest->FireScriptAvailable(aResult);
849 0 : }
850 :
851 : void
852 0 : nsScriptLoader::FireScriptEvaluated(nsresult aResult,
853 : nsScriptLoadRequest* aRequest)
854 : {
855 0 : for (PRInt32 i = 0; i < mObservers.Count(); i++) {
856 0 : nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
857 0 : obs->ScriptEvaluated(aResult, aRequest->mElement,
858 0 : aRequest->mIsInline);
859 : }
860 :
861 0 : aRequest->FireScriptEvaluated(aResult);
862 0 : }
863 :
864 : nsresult
865 0 : nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
866 : const nsAFlatString& aScript)
867 : {
868 0 : nsresult rv = NS_OK;
869 :
870 : // We need a document to evaluate scripts.
871 0 : if (!mDocument) {
872 0 : return NS_ERROR_FAILURE;
873 : }
874 :
875 0 : nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
876 0 : nsIDocument* ownerDoc = scriptContent->OwnerDoc();
877 0 : if (ownerDoc != mDocument) {
878 : // Willful violation of HTML5 as of 2010-12-01
879 0 : return NS_ERROR_FAILURE;
880 : }
881 :
882 0 : nsPIDOMWindow *pwin = mDocument->GetInnerWindow();
883 0 : if (!pwin || !pwin->IsInnerWindow()) {
884 0 : return NS_ERROR_FAILURE;
885 : }
886 0 : nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
887 0 : NS_ASSERTION(globalObject, "windows must be global objects");
888 :
889 : // Get the script-type to be used by this element.
890 0 : NS_ASSERTION(scriptContent, "no content - what is default script-type?");
891 0 : PRUint32 stid = scriptContent ? scriptContent->GetScriptTypeID() :
892 0 : nsIProgrammingLanguage::JAVASCRIPT;
893 : // and make sure we are setup for this type of script.
894 0 : rv = globalObject->EnsureScriptEnvironment(stid);
895 0 : if (NS_FAILED(rv))
896 0 : return rv;
897 :
898 : // Make sure context is a strong reference since we access it after
899 : // we've executed a script, which may cause all other references to
900 : // the context to go away.
901 0 : nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext(stid);
902 0 : if (!context) {
903 0 : return NS_ERROR_FAILURE;
904 : }
905 :
906 0 : bool oldProcessingScriptTag = context->GetProcessingScriptTag();
907 0 : context->SetProcessingScriptTag(true);
908 :
909 : // Update our current script.
910 0 : nsCOMPtr<nsIScriptElement> oldCurrent = mCurrentScript;
911 0 : mCurrentScript = aRequest->mElement;
912 :
913 : // It's very important to use aRequest->mURI, not the final URI of the channel
914 : // aRequest ended up getting script data from, as the script filename.
915 0 : nsCAutoString url;
916 0 : nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI, url);
917 :
918 : bool isUndefined;
919 0 : rv = context->EvaluateString(aScript, globalObject->GetGlobalJSObject(),
920 : mDocument->NodePrincipal(),
921 : aRequest->mOriginPrincipal,
922 : url.get(), aRequest->mLineNo,
923 0 : aRequest->mJSVersion, nsnull, &isUndefined);
924 :
925 : // Put the old script back in case it wants to do anything else.
926 0 : mCurrentScript = oldCurrent;
927 :
928 0 : JSContext *cx = nsnull; // Initialize this to keep GCC happy.
929 0 : if (stid == nsIProgrammingLanguage::JAVASCRIPT) {
930 0 : cx = context->GetNativeContext();
931 0 : ::JS_BeginRequest(cx);
932 0 : NS_ASSERTION(!::JS_IsExceptionPending(cx),
933 : "JS_ReportPendingException wasn't called in EvaluateString");
934 : }
935 :
936 0 : context->SetProcessingScriptTag(oldProcessingScriptTag);
937 :
938 0 : if (stid == nsIProgrammingLanguage::JAVASCRIPT) {
939 0 : NS_ASSERTION(!::JS_IsExceptionPending(cx),
940 : "JS_ReportPendingException wasn't called");
941 0 : ::JS_EndRequest(cx);
942 : }
943 0 : return rv;
944 : }
945 :
946 : void
947 0 : nsScriptLoader::ProcessPendingRequestsAsync()
948 : {
949 0 : if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) {
950 : nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
951 0 : &nsScriptLoader::ProcessPendingRequests);
952 :
953 0 : NS_DispatchToCurrentThread(ev);
954 : }
955 0 : }
956 :
957 : void
958 1038 : nsScriptLoader::ProcessPendingRequests()
959 : {
960 2076 : nsRefPtr<nsScriptLoadRequest> request;
961 1038 : if (mParserBlockingRequest &&
962 1038 : !mParserBlockingRequest->mLoading &&
963 0 : ReadyToExecuteScripts()) {
964 0 : request.swap(mParserBlockingRequest);
965 0 : UnblockParser(request);
966 0 : ProcessRequest(request);
967 0 : ContinueParserAsync(request);
968 : }
969 :
970 2076 : while (ReadyToExecuteScripts() &&
971 0 : !mXSLTRequests.IsEmpty() &&
972 0 : !mXSLTRequests[0]->mLoading) {
973 0 : request.swap(mXSLTRequests[0]);
974 0 : mXSLTRequests.RemoveElementAt(0);
975 0 : ProcessRequest(request);
976 : }
977 :
978 1038 : PRUint32 i = 0;
979 2076 : while (mEnabled && i < mAsyncRequests.Length()) {
980 0 : if (!mAsyncRequests[i]->mLoading) {
981 0 : request.swap(mAsyncRequests[i]);
982 0 : mAsyncRequests.RemoveElementAt(i);
983 0 : ProcessRequest(request);
984 0 : continue;
985 : }
986 0 : ++i;
987 : }
988 :
989 2076 : while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
990 0 : !mNonAsyncExternalScriptInsertedRequests[0]->mLoading) {
991 : // Violate the HTML5 spec and execute these in the insertion order in
992 : // order to make LABjs and the "order" plug-in for RequireJS work with
993 : // their Gecko-sniffed code path. See
994 : // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
995 0 : request.swap(mNonAsyncExternalScriptInsertedRequests[0]);
996 0 : mNonAsyncExternalScriptInsertedRequests.RemoveElementAt(0);
997 0 : ProcessRequest(request);
998 : }
999 :
1000 1038 : if (mDocumentParsingDone && mXSLTRequests.IsEmpty()) {
1001 2076 : while (!mDeferRequests.IsEmpty() && !mDeferRequests[0]->mLoading) {
1002 0 : request.swap(mDeferRequests[0]);
1003 0 : mDeferRequests.RemoveElementAt(0);
1004 0 : ProcessRequest(request);
1005 : }
1006 : }
1007 :
1008 2076 : while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
1009 0 : nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
1010 0 : mPendingChildLoaders.RemoveElementAt(0);
1011 0 : child->RemoveExecuteBlocker();
1012 : }
1013 :
1014 6228 : if (mDocumentParsingDone && mDocument &&
1015 2076 : !mParserBlockingRequest && mAsyncRequests.IsEmpty() &&
1016 1038 : mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
1017 2076 : mXSLTRequests.IsEmpty() && mDeferRequests.IsEmpty()) {
1018 : // No more pending scripts; time to unblock onload.
1019 : // OK to unblock onload synchronously here, since callers must be
1020 : // prepared for the world changing anyway.
1021 1038 : mDocumentParsingDone = false;
1022 1038 : mDocument->UnblockOnload(true);
1023 : }
1024 1038 : }
1025 :
1026 : bool
1027 1038 : nsScriptLoader::ReadyToExecuteScripts()
1028 : {
1029 : // Make sure the SelfReadyToExecuteScripts check is first, so that
1030 : // we don't block twice on an ancestor.
1031 1038 : if (!SelfReadyToExecuteScripts()) {
1032 1038 : return false;
1033 : }
1034 :
1035 0 : for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
1036 0 : nsScriptLoader* ancestor = doc->ScriptLoader();
1037 0 : if (!ancestor->SelfReadyToExecuteScripts() &&
1038 0 : ancestor->AddPendingChildLoader(this)) {
1039 0 : AddExecuteBlocker();
1040 0 : return false;
1041 : }
1042 : }
1043 :
1044 0 : return true;
1045 : }
1046 :
1047 :
1048 : // This function was copied from nsParser.cpp. It was simplified a bit.
1049 : static bool
1050 0 : DetectByteOrderMark(const unsigned char* aBytes, PRInt32 aLen, nsCString& oCharset)
1051 : {
1052 0 : if (aLen < 2)
1053 0 : return false;
1054 :
1055 0 : switch(aBytes[0]) {
1056 : case 0xEF:
1057 0 : if (aLen >= 3 && 0xBB == aBytes[1] && 0xBF == aBytes[2]) {
1058 : // EF BB BF
1059 : // Win2K UTF-8 BOM
1060 0 : oCharset.Assign("UTF-8");
1061 : }
1062 0 : break;
1063 : case 0xFE:
1064 0 : if (0xFF == aBytes[1]) {
1065 : // FE FF
1066 : // UTF-16, big-endian
1067 0 : oCharset.Assign("UTF-16");
1068 : }
1069 0 : break;
1070 : case 0xFF:
1071 0 : if (0xFE == aBytes[1]) {
1072 : // FF FE
1073 : // UTF-16, little-endian
1074 0 : oCharset.Assign("UTF-16");
1075 : }
1076 0 : break;
1077 : }
1078 0 : return !oCharset.IsEmpty();
1079 : }
1080 :
1081 : /* static */ nsresult
1082 0 : nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const PRUint8* aData,
1083 : PRUint32 aLength, const nsAString& aHintCharset,
1084 : nsIDocument* aDocument, nsString& aString)
1085 : {
1086 0 : if (!aLength) {
1087 0 : aString.Truncate();
1088 0 : return NS_OK;
1089 : }
1090 :
1091 0 : nsCAutoString characterSet;
1092 :
1093 0 : nsresult rv = NS_OK;
1094 0 : if (aChannel) {
1095 0 : rv = aChannel->GetContentCharset(characterSet);
1096 : }
1097 :
1098 0 : if (!aHintCharset.IsEmpty() && (NS_FAILED(rv) || characterSet.IsEmpty())) {
1099 : // charset name is always ASCII.
1100 0 : LossyCopyUTF16toASCII(aHintCharset, characterSet);
1101 : }
1102 :
1103 0 : if (NS_FAILED(rv) || characterSet.IsEmpty()) {
1104 0 : DetectByteOrderMark(aData, aLength, characterSet);
1105 : }
1106 :
1107 0 : if (characterSet.IsEmpty() && aDocument) {
1108 : // charset from document default
1109 0 : characterSet = aDocument->GetDocumentCharacterSet();
1110 : }
1111 :
1112 0 : if (characterSet.IsEmpty()) {
1113 : // fall back to ISO-8859-1, see bug 118404
1114 0 : characterSet.AssignLiteral("ISO-8859-1");
1115 : }
1116 :
1117 : nsCOMPtr<nsICharsetConverterManager> charsetConv =
1118 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
1119 :
1120 0 : nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
1121 :
1122 0 : if (NS_SUCCEEDED(rv) && charsetConv) {
1123 0 : rv = charsetConv->GetUnicodeDecoder(characterSet.get(),
1124 0 : getter_AddRefs(unicodeDecoder));
1125 0 : if (NS_FAILED(rv)) {
1126 : // fall back to ISO-8859-1 if charset is not supported. (bug 230104)
1127 0 : rv = charsetConv->GetUnicodeDecoderRaw("ISO-8859-1",
1128 0 : getter_AddRefs(unicodeDecoder));
1129 : }
1130 : }
1131 :
1132 : // converts from the charset to unicode
1133 0 : if (NS_SUCCEEDED(rv)) {
1134 0 : PRInt32 unicodeLength = 0;
1135 :
1136 0 : rv = unicodeDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
1137 0 : aLength, &unicodeLength);
1138 0 : if (NS_SUCCEEDED(rv)) {
1139 0 : if (!EnsureStringLength(aString, unicodeLength))
1140 0 : return NS_ERROR_OUT_OF_MEMORY;
1141 :
1142 0 : PRUnichar *ustr = aString.BeginWriting();
1143 :
1144 0 : PRInt32 consumedLength = 0;
1145 0 : PRInt32 originalLength = aLength;
1146 0 : PRInt32 convertedLength = 0;
1147 0 : PRInt32 bufferLength = unicodeLength;
1148 0 : do {
1149 0 : rv = unicodeDecoder->Convert(reinterpret_cast<const char*>(aData),
1150 : (PRInt32 *) &aLength, ustr,
1151 0 : &unicodeLength);
1152 0 : if (NS_FAILED(rv)) {
1153 : // if we failed, we consume one byte, replace it with U+FFFD
1154 : // and try the conversion again.
1155 0 : ustr[unicodeLength++] = (PRUnichar)0xFFFD;
1156 0 : ustr += unicodeLength;
1157 :
1158 0 : unicodeDecoder->Reset();
1159 : }
1160 0 : aData += ++aLength;
1161 0 : consumedLength += aLength;
1162 0 : aLength = originalLength - consumedLength;
1163 0 : convertedLength += unicodeLength;
1164 0 : unicodeLength = bufferLength - convertedLength;
1165 0 : } while (NS_FAILED(rv) && (originalLength > consumedLength) && (bufferLength > convertedLength));
1166 0 : aString.SetLength(convertedLength);
1167 : }
1168 : }
1169 0 : return rv;
1170 : }
1171 :
1172 : NS_IMETHODIMP
1173 0 : nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
1174 : nsISupports* aContext,
1175 : nsresult aStatus,
1176 : PRUint32 aStringLen,
1177 : const PRUint8* aString)
1178 : {
1179 0 : nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
1180 0 : NS_ASSERTION(request, "null request in stream complete handler");
1181 0 : NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
1182 :
1183 : nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
1184 0 : aString);
1185 0 : if (NS_FAILED(rv)) {
1186 0 : if (mDeferRequests.RemoveElement(request) ||
1187 0 : mAsyncRequests.RemoveElement(request) ||
1188 0 : mNonAsyncExternalScriptInsertedRequests.RemoveElement(request) ||
1189 0 : mXSLTRequests.RemoveElement(request)) {
1190 0 : FireScriptAvailable(rv, request);
1191 0 : } else if (mParserBlockingRequest == request) {
1192 0 : mParserBlockingRequest = nsnull;
1193 0 : UnblockParser(request);
1194 0 : FireScriptAvailable(rv, request);
1195 0 : ContinueParserAsync(request);
1196 : } else {
1197 0 : mPreloads.RemoveElement(request, PreloadRequestComparator());
1198 : }
1199 : }
1200 :
1201 : // Process our request and/or any pending ones
1202 0 : ProcessPendingRequests();
1203 :
1204 0 : return NS_OK;
1205 : }
1206 :
1207 : void
1208 0 : nsScriptLoader::UnblockParser(nsScriptLoadRequest* aParserBlockingRequest)
1209 : {
1210 0 : aParserBlockingRequest->mElement->UnblockParser();
1211 0 : }
1212 :
1213 : void
1214 0 : nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest)
1215 : {
1216 0 : aParserBlockingRequest->mElement->ContinueParserAsync();
1217 0 : }
1218 :
1219 : nsresult
1220 0 : nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
1221 : nsIStreamLoader* aLoader,
1222 : nsresult aStatus,
1223 : PRUint32 aStringLen,
1224 : const PRUint8* aString)
1225 : {
1226 0 : if (NS_FAILED(aStatus)) {
1227 0 : return aStatus;
1228 : }
1229 :
1230 : // If we don't have a document, then we need to abort further
1231 : // evaluation.
1232 0 : if (!mDocument) {
1233 0 : return NS_ERROR_NOT_AVAILABLE;
1234 : }
1235 :
1236 : // If the load returned an error page, then we need to abort
1237 0 : nsCOMPtr<nsIRequest> req;
1238 0 : nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
1239 0 : NS_ASSERTION(req, "StreamLoader's request went away prematurely");
1240 0 : NS_ENSURE_SUCCESS(rv, rv);
1241 :
1242 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req);
1243 0 : if (httpChannel) {
1244 : bool requestSucceeded;
1245 0 : rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
1246 0 : if (NS_SUCCEEDED(rv) && !requestSucceeded) {
1247 0 : return NS_ERROR_NOT_AVAILABLE;
1248 : }
1249 : }
1250 :
1251 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
1252 : // If this load was subject to a CORS check; don't flag it with a
1253 : // separate origin principal, so that it will treat our document's
1254 : // principal as the origin principal
1255 0 : if (aRequest->mCORSMode == CORS_NONE) {
1256 0 : rv = nsContentUtils::GetSecurityManager()->
1257 0 : GetChannelPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal));
1258 0 : NS_ENSURE_SUCCESS(rv, rv);
1259 : }
1260 :
1261 0 : if (aStringLen) {
1262 : // Check the charset attribute to determine script charset.
1263 0 : nsAutoString hintCharset;
1264 0 : if (!aRequest->IsPreload()) {
1265 0 : aRequest->mElement->GetScriptCharset(hintCharset);
1266 : } else {
1267 : nsTArray<PreloadInfo>::index_type i =
1268 0 : mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator());
1269 0 : NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping");
1270 0 : hintCharset = mPreloads[i].mCharset;
1271 : }
1272 : rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
1273 0 : aRequest->mScriptText);
1274 :
1275 0 : NS_ENSURE_SUCCESS(rv, rv);
1276 :
1277 0 : if (!ShouldExecuteScript(mDocument, channel)) {
1278 0 : return NS_ERROR_NOT_AVAILABLE;
1279 : }
1280 : }
1281 :
1282 : // This assertion could fire errorously if we ran out of memory when
1283 : // inserting the request in the array. However it's an unlikely case
1284 : // so if you see this assertion it is likely something else that is
1285 : // wrong, especially if you see it more than once.
1286 0 : NS_ASSERTION(mDeferRequests.IndexOf(aRequest) >= 0 ||
1287 : mAsyncRequests.IndexOf(aRequest) >= 0 ||
1288 : mNonAsyncExternalScriptInsertedRequests.IndexOf(aRequest) >= 0 ||
1289 : mXSLTRequests.IndexOf(aRequest) >= 0 ||
1290 : mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
1291 : mParserBlockingRequest,
1292 : "aRequest should be pending!");
1293 :
1294 : // Mark this as loaded
1295 0 : aRequest->mLoading = false;
1296 :
1297 0 : return NS_OK;
1298 : }
1299 :
1300 : /* static */
1301 : bool
1302 0 : nsScriptLoader::ShouldExecuteScript(nsIDocument* aDocument,
1303 : nsIChannel* aChannel)
1304 : {
1305 0 : if (!aChannel) {
1306 0 : return false;
1307 : }
1308 :
1309 : bool hasCert;
1310 0 : nsIPrincipal* docPrincipal = aDocument->NodePrincipal();
1311 0 : docPrincipal->GetHasCertificate(&hasCert);
1312 0 : if (!hasCert) {
1313 0 : return true;
1314 : }
1315 :
1316 0 : nsCOMPtr<nsIPrincipal> channelPrincipal;
1317 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
1318 0 : GetChannelPrincipal(aChannel, getter_AddRefs(channelPrincipal));
1319 0 : NS_ENSURE_SUCCESS(rv, false);
1320 :
1321 0 : NS_ASSERTION(channelPrincipal, "Gotta have a principal here!");
1322 :
1323 : // If the channel principal isn't at least as powerful as the
1324 : // document principal, then we don't execute the script.
1325 : bool subsumes;
1326 0 : rv = channelPrincipal->Subsumes(docPrincipal, &subsumes);
1327 0 : return NS_SUCCEEDED(rv) && subsumes;
1328 : }
1329 :
1330 : void
1331 1038 : nsScriptLoader::ParsingComplete(bool aTerminated)
1332 : {
1333 1038 : if (mDeferEnabled) {
1334 : // Have to check because we apparently get ParsingComplete
1335 : // without BeginDeferringScripts in some cases
1336 1038 : mDocumentParsingDone = true;
1337 : }
1338 1038 : mDeferEnabled = false;
1339 1038 : if (aTerminated) {
1340 62 : mDeferRequests.Clear();
1341 62 : mAsyncRequests.Clear();
1342 62 : mNonAsyncExternalScriptInsertedRequests.Clear();
1343 62 : mXSLTRequests.Clear();
1344 62 : mParserBlockingRequest = nsnull;
1345 : }
1346 :
1347 : // Have to call this even if aTerminated so we'll correctly unblock
1348 : // onload and all.
1349 1038 : ProcessPendingRequests();
1350 1038 : }
1351 :
1352 : void
1353 0 : nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
1354 : const nsAString &aType,
1355 : const nsAString &aCrossOrigin)
1356 : {
1357 : // Check to see if scripts has been turned off.
1358 0 : if (!mEnabled || !mDocument->IsScriptEnabled()) {
1359 0 : return;
1360 : }
1361 :
1362 : nsRefPtr<nsScriptLoadRequest> request =
1363 : new nsScriptLoadRequest(nsnull, 0,
1364 0 : nsGenericElement::StringToCORSMode(aCrossOrigin));
1365 0 : request->mURI = aURI;
1366 0 : request->mIsInline = false;
1367 0 : request->mLoading = true;
1368 0 : nsresult rv = StartLoad(request, aType);
1369 0 : if (NS_FAILED(rv)) {
1370 : return;
1371 : }
1372 :
1373 0 : PreloadInfo *pi = mPreloads.AppendElement();
1374 0 : pi->mRequest = request;
1375 0 : pi->mCharset = aCharset;
1376 : }
|