content/base/src/nsScriptLoader.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // vim: ft=cpp tw=78 sw=2 et ts=2
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * A class that handles loading and evaluation of <script> elements.
michael@0 9 */
michael@0 10
michael@0 11 #include "nsScriptLoader.h"
michael@0 12
michael@0 13 #include "jsapi.h"
michael@0 14 #include "jsfriendapi.h"
michael@0 15 #include "nsIUnicodeDecoder.h"
michael@0 16 #include "nsIContent.h"
michael@0 17 #include "nsJSUtils.h"
michael@0 18 #include "mozilla/dom/ScriptSettings.h"
michael@0 19 #include "mozilla/dom/Element.h"
michael@0 20 #include "nsGkAtoms.h"
michael@0 21 #include "nsNetUtil.h"
michael@0 22 #include "nsIJSRuntimeService.h"
michael@0 23 #include "nsIScriptGlobalObject.h"
michael@0 24 #include "nsIScriptContext.h"
michael@0 25 #include "nsIScriptSecurityManager.h"
michael@0 26 #include "nsIPrincipal.h"
michael@0 27 #include "nsJSPrincipals.h"
michael@0 28 #include "nsContentPolicyUtils.h"
michael@0 29 #include "nsIHttpChannel.h"
michael@0 30 #include "nsIHttpChannelInternal.h"
michael@0 31 #include "nsITimedChannel.h"
michael@0 32 #include "nsIScriptElement.h"
michael@0 33 #include "nsIDOMHTMLScriptElement.h"
michael@0 34 #include "nsIDocShell.h"
michael@0 35 #include "nsContentUtils.h"
michael@0 36 #include "nsCxPusher.h"
michael@0 37 #include "nsUnicharUtils.h"
michael@0 38 #include "nsAutoPtr.h"
michael@0 39 #include "nsIXPConnect.h"
michael@0 40 #include "nsError.h"
michael@0 41 #include "nsThreadUtils.h"
michael@0 42 #include "nsDocShellCID.h"
michael@0 43 #include "nsIContentSecurityPolicy.h"
michael@0 44 #include "prlog.h"
michael@0 45 #include "nsIChannelPolicy.h"
michael@0 46 #include "nsChannelPolicy.h"
michael@0 47 #include "nsCRT.h"
michael@0 48 #include "nsContentCreatorFunctions.h"
michael@0 49 #include "nsCrossSiteListenerProxy.h"
michael@0 50 #include "nsSandboxFlags.h"
michael@0 51 #include "nsContentTypeParser.h"
michael@0 52 #include "nsINetworkSeer.h"
michael@0 53 #include "mozilla/dom/EncodingUtils.h"
michael@0 54
michael@0 55 #include "mozilla/CORSMode.h"
michael@0 56 #include "mozilla/Attributes.h"
michael@0 57 #include "mozilla/unused.h"
michael@0 58
michael@0 59 #ifdef PR_LOGGING
michael@0 60 static PRLogModuleInfo* gCspPRLog;
michael@0 61 #endif
michael@0 62
michael@0 63 using namespace mozilla;
michael@0 64 using namespace mozilla::dom;
michael@0 65
michael@0 66 //////////////////////////////////////////////////////////////
michael@0 67 // Per-request data structure
michael@0 68 //////////////////////////////////////////////////////////////
michael@0 69
michael@0 70 class nsScriptLoadRequest MOZ_FINAL : public nsISupports {
michael@0 71 public:
michael@0 72 nsScriptLoadRequest(nsIScriptElement* aElement,
michael@0 73 uint32_t aVersion,
michael@0 74 CORSMode aCORSMode)
michael@0 75 : mElement(aElement),
michael@0 76 mLoading(true),
michael@0 77 mIsInline(true),
michael@0 78 mHasSourceMapURL(false),
michael@0 79 mScriptTextBuf(nullptr),
michael@0 80 mScriptTextLength(0),
michael@0 81 mJSVersion(aVersion),
michael@0 82 mLineNo(1),
michael@0 83 mCORSMode(aCORSMode)
michael@0 84 {
michael@0 85 }
michael@0 86
michael@0 87 ~nsScriptLoadRequest()
michael@0 88 {
michael@0 89 if (mScriptTextBuf) {
michael@0 90 js_free(mScriptTextBuf);
michael@0 91 }
michael@0 92 }
michael@0 93
michael@0 94 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 95
michael@0 96 void FireScriptAvailable(nsresult aResult)
michael@0 97 {
michael@0 98 mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
michael@0 99 }
michael@0 100 void FireScriptEvaluated(nsresult aResult)
michael@0 101 {
michael@0 102 mElement->ScriptEvaluated(aResult, mElement, mIsInline);
michael@0 103 }
michael@0 104
michael@0 105 bool IsPreload()
michael@0 106 {
michael@0 107 return mElement == nullptr;
michael@0 108 }
michael@0 109
michael@0 110 nsCOMPtr<nsIScriptElement> mElement;
michael@0 111 bool mLoading; // Are we still waiting for a load to complete?
michael@0 112 bool mIsInline; // Is the script inline or loaded?
michael@0 113 bool mHasSourceMapURL; // Does the HTTP header have a source map url?
michael@0 114 nsString mSourceMapURL; // Holds source map url for loaded scripts
michael@0 115 jschar* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
michael@0 116 size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
michael@0 117 uint32_t mJSVersion;
michael@0 118 nsCOMPtr<nsIURI> mURI;
michael@0 119 nsCOMPtr<nsIPrincipal> mOriginPrincipal;
michael@0 120 nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing.
michael@0 121 int32_t mLineNo;
michael@0 122 const CORSMode mCORSMode;
michael@0 123 };
michael@0 124
michael@0 125 // The nsScriptLoadRequest is passed as the context to necko, and thus
michael@0 126 // it needs to be threadsafe. Necko won't do anything with this
michael@0 127 // context, but it will AddRef and Release it on other threads.
michael@0 128 NS_IMPL_ISUPPORTS0(nsScriptLoadRequest)
michael@0 129
michael@0 130 //////////////////////////////////////////////////////////////
michael@0 131 //
michael@0 132 //////////////////////////////////////////////////////////////
michael@0 133
michael@0 134 nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
michael@0 135 : mDocument(aDocument),
michael@0 136 mBlockerCount(0),
michael@0 137 mEnabled(true),
michael@0 138 mDeferEnabled(false),
michael@0 139 mDocumentParsingDone(false),
michael@0 140 mBlockingDOMContentLoaded(false)
michael@0 141 {
michael@0 142 // enable logging for CSP
michael@0 143 #ifdef PR_LOGGING
michael@0 144 if (!gCspPRLog)
michael@0 145 gCspPRLog = PR_NewLogModule("CSP");
michael@0 146 #endif
michael@0 147 }
michael@0 148
michael@0 149 nsScriptLoader::~nsScriptLoader()
michael@0 150 {
michael@0 151 mObservers.Clear();
michael@0 152
michael@0 153 if (mParserBlockingRequest) {
michael@0 154 mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
michael@0 155 }
michael@0 156
michael@0 157 for (uint32_t i = 0; i < mXSLTRequests.Length(); i++) {
michael@0 158 mXSLTRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
michael@0 159 }
michael@0 160
michael@0 161 for (uint32_t i = 0; i < mDeferRequests.Length(); i++) {
michael@0 162 mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
michael@0 163 }
michael@0 164
michael@0 165 for (uint32_t i = 0; i < mAsyncRequests.Length(); i++) {
michael@0 166 mAsyncRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
michael@0 167 }
michael@0 168
michael@0 169 for (uint32_t i = 0; i < mNonAsyncExternalScriptInsertedRequests.Length(); i++) {
michael@0 170 mNonAsyncExternalScriptInsertedRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
michael@0 171 }
michael@0 172
michael@0 173 // Unblock the kids, in case any of them moved to a different document
michael@0 174 // subtree in the meantime and therefore aren't actually going away.
michael@0 175 for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
michael@0 176 mPendingChildLoaders[j]->RemoveExecuteBlocker();
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 NS_IMPL_ISUPPORTS(nsScriptLoader, nsIStreamLoaderObserver)
michael@0 181
michael@0 182 // Helper method for checking if the script element is an event-handler
michael@0 183 // This means that it has both a for-attribute and a event-attribute.
michael@0 184 // Also, if the for-attribute has a value that matches "\s*window\s*",
michael@0 185 // and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
michael@0 186 // eventhandler. (both matches are case insensitive).
michael@0 187 // This is how IE seems to filter out a window's onload handler from a
michael@0 188 // <script for=... event=...> element.
michael@0 189
michael@0 190 static bool
michael@0 191 IsScriptEventHandler(nsIContent* aScriptElement)
michael@0 192 {
michael@0 193 if (!aScriptElement->IsHTML()) {
michael@0 194 return false;
michael@0 195 }
michael@0 196
michael@0 197 nsAutoString forAttr, eventAttr;
michael@0 198 if (!aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, forAttr) ||
michael@0 199 !aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, eventAttr)) {
michael@0 200 return false;
michael@0 201 }
michael@0 202
michael@0 203 const nsAString& for_str =
michael@0 204 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr);
michael@0 205 if (!for_str.LowerCaseEqualsLiteral("window")) {
michael@0 206 return true;
michael@0 207 }
michael@0 208
michael@0 209 // We found for="window", now check for event="onload".
michael@0 210 const nsAString& event_str =
michael@0 211 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr, false);
michael@0 212 if (!StringBeginsWith(event_str, NS_LITERAL_STRING("onload"),
michael@0 213 nsCaseInsensitiveStringComparator())) {
michael@0 214 // It ain't "onload.*".
michael@0 215
michael@0 216 return true;
michael@0 217 }
michael@0 218
michael@0 219 nsAutoString::const_iterator start, end;
michael@0 220 event_str.BeginReading(start);
michael@0 221 event_str.EndReading(end);
michael@0 222
michael@0 223 start.advance(6); // advance past "onload"
michael@0 224
michael@0 225 if (start != end && *start != '(' && *start != ' ') {
michael@0 226 // We got onload followed by something other than space or
michael@0 227 // '('. Not good enough.
michael@0 228
michael@0 229 return true;
michael@0 230 }
michael@0 231
michael@0 232 return false;
michael@0 233 }
michael@0 234
michael@0 235 nsresult
michael@0 236 nsScriptLoader::CheckContentPolicy(nsIDocument* aDocument,
michael@0 237 nsISupports *aContext,
michael@0 238 nsIURI *aURI,
michael@0 239 const nsAString &aType)
michael@0 240 {
michael@0 241 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
michael@0 242 nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
michael@0 243 aURI,
michael@0 244 aDocument->NodePrincipal(),
michael@0 245 aContext,
michael@0 246 NS_LossyConvertUTF16toASCII(aType),
michael@0 247 nullptr, //extra
michael@0 248 &shouldLoad,
michael@0 249 nsContentUtils::GetContentPolicy(),
michael@0 250 nsContentUtils::GetSecurityManager());
michael@0 251 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
michael@0 252 if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
michael@0 253 return NS_ERROR_CONTENT_BLOCKED;
michael@0 254 }
michael@0 255 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
michael@0 256 }
michael@0 257
michael@0 258 return NS_OK;
michael@0 259 }
michael@0 260
michael@0 261 nsresult
michael@0 262 nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
michael@0 263 nsISupports* aContext,
michael@0 264 nsIURI* aURI,
michael@0 265 const nsAString &aType)
michael@0 266 {
michael@0 267 // Check that the containing page is allowed to load this URI.
michael@0 268 nsresult rv = nsContentUtils::GetSecurityManager()->
michael@0 269 CheckLoadURIWithPrincipal(aDocument->NodePrincipal(), aURI,
michael@0 270 nsIScriptSecurityManager::ALLOW_CHROME);
michael@0 271
michael@0 272 NS_ENSURE_SUCCESS(rv, rv);
michael@0 273
michael@0 274 // After the security manager, the content-policy stuff gets a veto
michael@0 275 rv = CheckContentPolicy(aDocument, aContext, aURI, aType);
michael@0 276 if (NS_FAILED(rv)) {
michael@0 277 return rv;
michael@0 278 }
michael@0 279
michael@0 280 return NS_OK;
michael@0 281 }
michael@0 282
michael@0 283 nsresult
michael@0 284 nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
michael@0 285 bool aScriptFromHead)
michael@0 286 {
michael@0 287 nsISupports *context = aRequest->mElement.get()
michael@0 288 ? static_cast<nsISupports *>(aRequest->mElement.get())
michael@0 289 : static_cast<nsISupports *>(mDocument);
michael@0 290 nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType);
michael@0 291 if (NS_FAILED(rv)) {
michael@0 292 return rv;
michael@0 293 }
michael@0 294
michael@0 295 nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
michael@0 296
michael@0 297 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->GetWindow()));
michael@0 298 if (!window) {
michael@0 299 return NS_ERROR_NULL_POINTER;
michael@0 300 }
michael@0 301
michael@0 302 nsIDocShell *docshell = window->GetDocShell();
michael@0 303
michael@0 304 nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
michael@0 305
michael@0 306 // If this document is sandboxed without 'allow-scripts', abort.
michael@0 307 if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
michael@0 308 return NS_OK;
michael@0 309 }
michael@0 310
michael@0 311 // check for a Content Security Policy to pass down to the channel
michael@0 312 // that will be created to load the script
michael@0 313 nsCOMPtr<nsIChannelPolicy> channelPolicy;
michael@0 314 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 315 rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
michael@0 316 NS_ENSURE_SUCCESS(rv, rv);
michael@0 317 if (csp) {
michael@0 318 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
michael@0 319 channelPolicy->SetContentSecurityPolicy(csp);
michael@0 320 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
michael@0 321 }
michael@0 322
michael@0 323 nsCOMPtr<nsIChannel> channel;
michael@0 324 rv = NS_NewChannel(getter_AddRefs(channel),
michael@0 325 aRequest->mURI, nullptr, loadGroup, prompter,
michael@0 326 nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
michael@0 327 channelPolicy);
michael@0 328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 329
michael@0 330 nsIScriptElement *script = aRequest->mElement;
michael@0 331 if (aScriptFromHead &&
michael@0 332 !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
michael@0 333 nsCOMPtr<nsIHttpChannelInternal>
michael@0 334 internalHttpChannel(do_QueryInterface(channel));
michael@0 335 if (internalHttpChannel)
michael@0 336 internalHttpChannel->SetLoadAsBlocking(true);
michael@0 337 }
michael@0 338
michael@0 339 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
michael@0 340 if (httpChannel) {
michael@0 341 // HTTP content negotation has little value in this context.
michael@0 342 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
michael@0 343 NS_LITERAL_CSTRING("*/*"),
michael@0 344 false);
michael@0 345 httpChannel->SetReferrer(mDocument->GetDocumentURI());
michael@0 346 }
michael@0 347
michael@0 348 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell));
michael@0 349 mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(),
michael@0 350 nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext);
michael@0 351
michael@0 352 // Set the initiator type
michael@0 353 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
michael@0 354 if (timedChannel) {
michael@0 355 timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
michael@0 356 }
michael@0 357
michael@0 358 nsCOMPtr<nsIStreamLoader> loader;
michael@0 359 rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
michael@0 360 NS_ENSURE_SUCCESS(rv, rv);
michael@0 361
michael@0 362 nsCOMPtr<nsIStreamListener> listener = loader.get();
michael@0 363
michael@0 364 if (aRequest->mCORSMode != CORS_NONE) {
michael@0 365 bool withCredentials = (aRequest->mCORSMode == CORS_USE_CREDENTIALS);
michael@0 366 nsRefPtr<nsCORSListenerProxy> corsListener =
michael@0 367 new nsCORSListenerProxy(listener, mDocument->NodePrincipal(),
michael@0 368 withCredentials);
michael@0 369 rv = corsListener->Init(channel);
michael@0 370 NS_ENSURE_SUCCESS(rv, rv);
michael@0 371 listener = corsListener;
michael@0 372 }
michael@0 373
michael@0 374 rv = channel->AsyncOpen(listener, aRequest);
michael@0 375 NS_ENSURE_SUCCESS(rv, rv);
michael@0 376
michael@0 377 return NS_OK;
michael@0 378 }
michael@0 379
michael@0 380 bool
michael@0 381 nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi,
michael@0 382 nsIURI * const &aURI) const
michael@0 383 {
michael@0 384 bool same;
michael@0 385 return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) &&
michael@0 386 same;
michael@0 387 }
michael@0 388
michael@0 389 class nsScriptRequestProcessor : public nsRunnable
michael@0 390 {
michael@0 391 private:
michael@0 392 nsRefPtr<nsScriptLoader> mLoader;
michael@0 393 nsRefPtr<nsScriptLoadRequest> mRequest;
michael@0 394 public:
michael@0 395 nsScriptRequestProcessor(nsScriptLoader* aLoader,
michael@0 396 nsScriptLoadRequest* aRequest)
michael@0 397 : mLoader(aLoader)
michael@0 398 , mRequest(aRequest)
michael@0 399 {}
michael@0 400 NS_IMETHODIMP Run()
michael@0 401 {
michael@0 402 return mLoader->ProcessRequest(mRequest);
michael@0 403 }
michael@0 404 };
michael@0 405
michael@0 406 static inline bool
michael@0 407 ParseTypeAttribute(const nsAString& aType, JSVersion* aVersion)
michael@0 408 {
michael@0 409 MOZ_ASSERT(!aType.IsEmpty());
michael@0 410 MOZ_ASSERT(aVersion);
michael@0 411 MOZ_ASSERT(*aVersion == JSVERSION_DEFAULT);
michael@0 412
michael@0 413 nsContentTypeParser parser(aType);
michael@0 414
michael@0 415 nsAutoString mimeType;
michael@0 416 nsresult rv = parser.GetType(mimeType);
michael@0 417 NS_ENSURE_SUCCESS(rv, false);
michael@0 418
michael@0 419 if (!nsContentUtils::IsJavascriptMIMEType(mimeType)) {
michael@0 420 return false;
michael@0 421 }
michael@0 422
michael@0 423 // Get the version string, and ensure the language supports it.
michael@0 424 nsAutoString versionName;
michael@0 425 rv = parser.GetParameter("version", versionName);
michael@0 426
michael@0 427 if (NS_SUCCEEDED(rv)) {
michael@0 428 *aVersion = nsContentUtils::ParseJavascriptVersion(versionName);
michael@0 429 } else if (rv != NS_ERROR_INVALID_ARG) {
michael@0 430 return false;
michael@0 431 }
michael@0 432
michael@0 433 return true;
michael@0 434 }
michael@0 435
michael@0 436 bool
michael@0 437 CSPAllowsInlineScript(nsIScriptElement *aElement, nsIDocument *aDocument)
michael@0 438 {
michael@0 439 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 440 nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
michael@0 441 NS_ENSURE_SUCCESS(rv, false);
michael@0 442
michael@0 443 if (!csp) {
michael@0 444 // no CSP --> allow
michael@0 445 return true;
michael@0 446 }
michael@0 447
michael@0 448 // An inline script can be allowed because all inline scripts are allowed,
michael@0 449 // or else because it is whitelisted by a nonce-source or hash-source. This
michael@0 450 // is a logical OR between whitelisting methods, so the allowInlineScript
michael@0 451 // outparam can be reused for each check as long as we stop checking as soon
michael@0 452 // as it is set to true. This also optimizes performance by avoiding the
michael@0 453 // overhead of unnecessary checks.
michael@0 454 bool allowInlineScript = true;
michael@0 455 nsAutoTArray<unsigned short, 3> violations;
michael@0 456
michael@0 457 bool reportInlineViolation = false;
michael@0 458 rv = csp->GetAllowsInlineScript(&reportInlineViolation, &allowInlineScript);
michael@0 459 NS_ENSURE_SUCCESS(rv, false);
michael@0 460 if (reportInlineViolation) {
michael@0 461 violations.AppendElement(static_cast<unsigned short>(
michael@0 462 nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT));
michael@0 463 }
michael@0 464
michael@0 465 nsAutoString nonce;
michael@0 466 if (!allowInlineScript) {
michael@0 467 nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
michael@0 468 bool foundNonce = scriptContent->GetAttr(kNameSpaceID_None,
michael@0 469 nsGkAtoms::nonce, nonce);
michael@0 470 if (foundNonce) {
michael@0 471 bool reportNonceViolation;
michael@0 472 rv = csp->GetAllowsNonce(nonce, nsIContentPolicy::TYPE_SCRIPT,
michael@0 473 &reportNonceViolation, &allowInlineScript);
michael@0 474 NS_ENSURE_SUCCESS(rv, false);
michael@0 475 if (reportNonceViolation) {
michael@0 476 violations.AppendElement(static_cast<unsigned short>(
michael@0 477 nsIContentSecurityPolicy::VIOLATION_TYPE_NONCE_SCRIPT));
michael@0 478 }
michael@0 479 }
michael@0 480 }
michael@0 481
michael@0 482 if (!allowInlineScript) {
michael@0 483 bool reportHashViolation;
michael@0 484 nsAutoString scriptText;
michael@0 485 aElement->GetScriptText(scriptText);
michael@0 486 rv = csp->GetAllowsHash(scriptText, nsIContentPolicy::TYPE_SCRIPT,
michael@0 487 &reportHashViolation, &allowInlineScript);
michael@0 488 NS_ENSURE_SUCCESS(rv, false);
michael@0 489 if (reportHashViolation) {
michael@0 490 violations.AppendElement(static_cast<unsigned short>(
michael@0 491 nsIContentSecurityPolicy::VIOLATION_TYPE_HASH_SCRIPT));
michael@0 492 }
michael@0 493 }
michael@0 494
michael@0 495 // What violation(s) should be reported?
michael@0 496 //
michael@0 497 // 1. If the script tag has a nonce attribute, and the nonce does not match
michael@0 498 // the policy, report VIOLATION_TYPE_NONCE_SCRIPT.
michael@0 499 // 2. If the policy has at least one hash-source, and the hashed contents of
michael@0 500 // the script tag did not match any of them, report VIOLATION_TYPE_HASH_SCRIPT
michael@0 501 // 3. Otherwise, report VIOLATION_TYPE_INLINE_SCRIPT if appropriate.
michael@0 502 //
michael@0 503 // 1 and 2 may occur together, 3 should only occur by itself. Naturally,
michael@0 504 // every VIOLATION_TYPE_NONCE_SCRIPT and VIOLATION_TYPE_HASH_SCRIPT are also
michael@0 505 // VIOLATION_TYPE_INLINE_SCRIPT, but reporting the
michael@0 506 // VIOLATION_TYPE_INLINE_SCRIPT is redundant and does not help the developer.
michael@0 507 if (!violations.IsEmpty()) {
michael@0 508 MOZ_ASSERT(violations[0] == nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
michael@0 509 "How did we get any violations without an initial inline script violation?");
michael@0 510 // gather information to log with violation report
michael@0 511 nsIURI* uri = aDocument->GetDocumentURI();
michael@0 512 nsAutoCString asciiSpec;
michael@0 513 uri->GetAsciiSpec(asciiSpec);
michael@0 514 nsAutoString scriptText;
michael@0 515 aElement->GetScriptText(scriptText);
michael@0 516 nsAutoString scriptSample(scriptText);
michael@0 517
michael@0 518 // cap the length of the script sample at 40 chars
michael@0 519 if (scriptSample.Length() > 40) {
michael@0 520 scriptSample.Truncate(40);
michael@0 521 scriptSample.AppendLiteral("...");
michael@0 522 }
michael@0 523
michael@0 524 for (uint32_t i = 0; i < violations.Length(); i++) {
michael@0 525 // Skip reporting the redundant inline script violation if there are
michael@0 526 // other (nonce and/or hash violations) as well.
michael@0 527 if (i > 0 || violations.Length() == 1) {
michael@0 528 csp->LogViolationDetails(violations[i], NS_ConvertUTF8toUTF16(asciiSpec),
michael@0 529 scriptSample, aElement->GetScriptLineNumber(),
michael@0 530 nonce, scriptText);
michael@0 531 }
michael@0 532 }
michael@0 533 }
michael@0 534
michael@0 535 if (!allowInlineScript) {
michael@0 536 NS_ASSERTION(!violations.IsEmpty(),
michael@0 537 "CSP blocked inline script but is not reporting a violation");
michael@0 538 return false;
michael@0 539 }
michael@0 540 return true;
michael@0 541 }
michael@0 542
michael@0 543 bool
michael@0 544 nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
michael@0 545 {
michael@0 546 // We need a document to evaluate scripts.
michael@0 547 NS_ENSURE_TRUE(mDocument, false);
michael@0 548
michael@0 549 // Check to see if scripts has been turned off.
michael@0 550 if (!mEnabled || !mDocument->IsScriptEnabled()) {
michael@0 551 return false;
michael@0 552 }
michael@0 553
michael@0 554 NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");
michael@0 555
michael@0 556 nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
michael@0 557
michael@0 558 // Step 12. Check that the script is not an eventhandler
michael@0 559 if (IsScriptEventHandler(scriptContent)) {
michael@0 560 return false;
michael@0 561 }
michael@0 562
michael@0 563 JSVersion version = JSVERSION_DEFAULT;
michael@0 564
michael@0 565 // Check the type attribute to determine language and version.
michael@0 566 // If type exists, it trumps the deprecated 'language='
michael@0 567 nsAutoString type;
michael@0 568 aElement->GetScriptType(type);
michael@0 569 if (!type.IsEmpty()) {
michael@0 570 NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false);
michael@0 571 } else {
michael@0 572 // no 'type=' element
michael@0 573 // "language" is a deprecated attribute of HTML, so we check it only for
michael@0 574 // HTML script elements.
michael@0 575 if (scriptContent->IsHTML()) {
michael@0 576 nsAutoString language;
michael@0 577 scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language);
michael@0 578 if (!language.IsEmpty()) {
michael@0 579 if (!nsContentUtils::IsJavaScriptLanguage(language)) {
michael@0 580 return false;
michael@0 581 }
michael@0 582 }
michael@0 583 }
michael@0 584 }
michael@0 585
michael@0 586 // Step 14. in the HTML5 spec
michael@0 587 nsresult rv = NS_OK;
michael@0 588 nsRefPtr<nsScriptLoadRequest> request;
michael@0 589 if (aElement->GetScriptExternal()) {
michael@0 590 // external script
michael@0 591 nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
michael@0 592 if (!scriptURI) {
michael@0 593 // Asynchronously report the failure to create a URI object
michael@0 594 NS_DispatchToCurrentThread(
michael@0 595 NS_NewRunnableMethod(aElement,
michael@0 596 &nsIScriptElement::FireErrorEvent));
michael@0 597 return false;
michael@0 598 }
michael@0 599 CORSMode ourCORSMode = aElement->GetCORSMode();
michael@0 600 nsTArray<PreloadInfo>::index_type i =
michael@0 601 mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
michael@0 602 if (i != nsTArray<PreloadInfo>::NoIndex) {
michael@0 603 // preloaded
michael@0 604 // note that a script-inserted script can steal a preload!
michael@0 605 request = mPreloads[i].mRequest;
michael@0 606 request->mElement = aElement;
michael@0 607 nsString preloadCharset(mPreloads[i].mCharset);
michael@0 608 mPreloads.RemoveElementAt(i);
michael@0 609
michael@0 610 // Double-check that the charset the preload used is the same as
michael@0 611 // the charset we have now.
michael@0 612 nsAutoString elementCharset;
michael@0 613 aElement->GetScriptCharset(elementCharset);
michael@0 614 if (elementCharset.Equals(preloadCharset) &&
michael@0 615 ourCORSMode == request->mCORSMode) {
michael@0 616 rv = CheckContentPolicy(mDocument, aElement, request->mURI, type);
michael@0 617 NS_ENSURE_SUCCESS(rv, false);
michael@0 618 } else {
michael@0 619 // Drop the preload
michael@0 620 request = nullptr;
michael@0 621 }
michael@0 622 }
michael@0 623
michael@0 624 if (!request) {
michael@0 625 // no usable preload
michael@0 626 request = new nsScriptLoadRequest(aElement, version, ourCORSMode);
michael@0 627 request->mURI = scriptURI;
michael@0 628 request->mIsInline = false;
michael@0 629 request->mLoading = true;
michael@0 630
michael@0 631 // set aScriptFromHead to false so we don't treat non preloaded scripts as
michael@0 632 // blockers for full page load. See bug 792438.
michael@0 633 rv = StartLoad(request, type, false);
michael@0 634 if (NS_FAILED(rv)) {
michael@0 635 // Asynchronously report the load failure
michael@0 636 NS_DispatchToCurrentThread(
michael@0 637 NS_NewRunnableMethod(aElement,
michael@0 638 &nsIScriptElement::FireErrorEvent));
michael@0 639 return false;
michael@0 640 }
michael@0 641 }
michael@0 642
michael@0 643 request->mJSVersion = version;
michael@0 644
michael@0 645 if (aElement->GetScriptAsync()) {
michael@0 646 mAsyncRequests.AppendElement(request);
michael@0 647 if (!request->mLoading) {
michael@0 648 // The script is available already. Run it ASAP when the event
michael@0 649 // loop gets a chance to spin.
michael@0 650 ProcessPendingRequestsAsync();
michael@0 651 }
michael@0 652 return false;
michael@0 653 }
michael@0 654 if (!aElement->GetParserCreated()) {
michael@0 655 // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
michael@0 656 // for RequireJS work with their Gecko-sniffed code path. See
michael@0 657 // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
michael@0 658 mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
michael@0 659 if (!request->mLoading) {
michael@0 660 // The script is available already. Run it ASAP when the event
michael@0 661 // loop gets a chance to spin.
michael@0 662 ProcessPendingRequestsAsync();
michael@0 663 }
michael@0 664 return false;
michael@0 665 }
michael@0 666 // we now have a parser-inserted request that may or may not be still
michael@0 667 // loading
michael@0 668 if (aElement->GetScriptDeferred()) {
michael@0 669 // We don't want to run this yet.
michael@0 670 // If we come here, the script is a parser-created script and it has
michael@0 671 // the defer attribute but not the async attribute. Since a
michael@0 672 // a parser-inserted script is being run, we came here by the parser
michael@0 673 // running the script, which means the parser is still alive and the
michael@0 674 // parse is ongoing.
michael@0 675 NS_ASSERTION(mDocument->GetCurrentContentSink() ||
michael@0 676 aElement->GetParserCreated() == FROM_PARSER_XSLT,
michael@0 677 "Non-XSLT Defer script on a document without an active parser; bug 592366.");
michael@0 678 AddDeferRequest(request);
michael@0 679 return false;
michael@0 680 }
michael@0 681
michael@0 682 if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
michael@0 683 // Need to maintain order for XSLT-inserted scripts
michael@0 684 NS_ASSERTION(!mParserBlockingRequest,
michael@0 685 "Parser-blocking scripts and XSLT scripts in the same doc!");
michael@0 686 mXSLTRequests.AppendElement(request);
michael@0 687 if (!request->mLoading) {
michael@0 688 // The script is available already. Run it ASAP when the event
michael@0 689 // loop gets a chance to spin.
michael@0 690 ProcessPendingRequestsAsync();
michael@0 691 }
michael@0 692 return true;
michael@0 693 }
michael@0 694 if (!request->mLoading && ReadyToExecuteScripts()) {
michael@0 695 // The request has already been loaded and there are no pending style
michael@0 696 // sheets. If the script comes from the network stream, cheat for
michael@0 697 // performance reasons and avoid a trip through the event loop.
michael@0 698 if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
michael@0 699 return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
michael@0 700 }
michael@0 701 // Otherwise, we've got a document.written script, make a trip through
michael@0 702 // the event loop to hide the preload effects from the scripts on the
michael@0 703 // Web page.
michael@0 704 NS_ASSERTION(!mParserBlockingRequest,
michael@0 705 "There can be only one parser-blocking script at a time");
michael@0 706 NS_ASSERTION(mXSLTRequests.IsEmpty(),
michael@0 707 "Parser-blocking scripts and XSLT scripts in the same doc!");
michael@0 708 mParserBlockingRequest = request;
michael@0 709 ProcessPendingRequestsAsync();
michael@0 710 return true;
michael@0 711 }
michael@0 712 // The script hasn't loaded yet or there's a style sheet blocking it.
michael@0 713 // The script will be run when it loads or the style sheet loads.
michael@0 714 NS_ASSERTION(!mParserBlockingRequest,
michael@0 715 "There can be only one parser-blocking script at a time");
michael@0 716 NS_ASSERTION(mXSLTRequests.IsEmpty(),
michael@0 717 "Parser-blocking scripts and XSLT scripts in the same doc!");
michael@0 718 mParserBlockingRequest = request;
michael@0 719 return true;
michael@0 720 }
michael@0 721
michael@0 722 // inline script
michael@0 723 // Is this document sandboxed without 'allow-scripts'?
michael@0 724 if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
michael@0 725 return false;
michael@0 726 }
michael@0 727
michael@0 728 // Does CSP allow this inline script to run?
michael@0 729 if (!CSPAllowsInlineScript(aElement, mDocument)) {
michael@0 730 return false;
michael@0 731 }
michael@0 732
michael@0 733 // Inline scripts ignore ther CORS mode and are always CORS_NONE
michael@0 734 request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
michael@0 735 request->mJSVersion = version;
michael@0 736 request->mLoading = false;
michael@0 737 request->mIsInline = true;
michael@0 738 request->mURI = mDocument->GetDocumentURI();
michael@0 739 request->mLineNo = aElement->GetScriptLineNumber();
michael@0 740
michael@0 741 if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
michael@0 742 (!ReadyToExecuteScripts() || !mXSLTRequests.IsEmpty())) {
michael@0 743 // Need to maintain order for XSLT-inserted scripts
michael@0 744 NS_ASSERTION(!mParserBlockingRequest,
michael@0 745 "Parser-blocking scripts and XSLT scripts in the same doc!");
michael@0 746 mXSLTRequests.AppendElement(request);
michael@0 747 return true;
michael@0 748 }
michael@0 749 if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
michael@0 750 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
michael@0 751 "A script-inserted script is inserted without an update batch?");
michael@0 752 nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this,
michael@0 753 request));
michael@0 754 return false;
michael@0 755 }
michael@0 756 if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
michael@0 757 !ReadyToExecuteScripts()) {
michael@0 758 NS_ASSERTION(!mParserBlockingRequest,
michael@0 759 "There can be only one parser-blocking script at a time");
michael@0 760 mParserBlockingRequest = request;
michael@0 761 NS_ASSERTION(mXSLTRequests.IsEmpty(),
michael@0 762 "Parser-blocking scripts and XSLT scripts in the same doc!");
michael@0 763 return true;
michael@0 764 }
michael@0 765 // We now have a document.written inline script or we have an inline script
michael@0 766 // from the network but there is no style sheet that is blocking scripts.
michael@0 767 // Don't check for style sheets blocking scripts in the document.write
michael@0 768 // case to avoid style sheet network activity affecting when
michael@0 769 // document.write returns. It's not really necessary to do this if
michael@0 770 // there's no document.write currently on the call stack. However,
michael@0 771 // this way matches IE more closely than checking if document.write
michael@0 772 // is on the call stack.
michael@0 773 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
michael@0 774 "Not safe to run a parser-inserted script?");
michael@0 775 return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
michael@0 776 }
michael@0 777
michael@0 778 namespace {
michael@0 779
michael@0 780 class NotifyOffThreadScriptLoadCompletedRunnable : public nsRunnable
michael@0 781 {
michael@0 782 nsRefPtr<nsScriptLoadRequest> mRequest;
michael@0 783 nsRefPtr<nsScriptLoader> mLoader;
michael@0 784 void *mToken;
michael@0 785
michael@0 786 public:
michael@0 787 NotifyOffThreadScriptLoadCompletedRunnable(nsScriptLoadRequest* aRequest,
michael@0 788 nsScriptLoader* aLoader)
michael@0 789 : mRequest(aRequest), mLoader(aLoader), mToken(nullptr)
michael@0 790 {}
michael@0 791
michael@0 792 void SetToken(void* aToken) {
michael@0 793 MOZ_ASSERT(aToken && !mToken);
michael@0 794 mToken = aToken;
michael@0 795 }
michael@0 796
michael@0 797 NS_DECL_NSIRUNNABLE
michael@0 798 };
michael@0 799
michael@0 800 } /* anonymous namespace */
michael@0 801
michael@0 802 nsresult
michael@0 803 nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest, void **aOffThreadToken)
michael@0 804 {
michael@0 805 nsresult rv = ProcessRequest(aRequest, aOffThreadToken);
michael@0 806 mDocument->UnblockOnload(false);
michael@0 807 return rv;
michael@0 808 }
michael@0 809
michael@0 810 NS_IMETHODIMP
michael@0 811 NotifyOffThreadScriptLoadCompletedRunnable::Run()
michael@0 812 {
michael@0 813 MOZ_ASSERT(NS_IsMainThread());
michael@0 814
michael@0 815 nsresult rv = mLoader->ProcessOffThreadRequest(mRequest, &mToken);
michael@0 816
michael@0 817 if (mToken) {
michael@0 818 // The result of the off thread parse was not actually needed to process
michael@0 819 // the request (disappearing window, some other error, ...). Finish the
michael@0 820 // request to avoid leaks in the JS engine.
michael@0 821 nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
michael@0 822 NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
michael@0 823 JSRuntime *rt;
michael@0 824 svc->GetRuntime(&rt);
michael@0 825 NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE);
michael@0 826 JS::FinishOffThreadScript(nullptr, rt, mToken);
michael@0 827 }
michael@0 828
michael@0 829 return rv;
michael@0 830 }
michael@0 831
michael@0 832 static void
michael@0 833 OffThreadScriptLoaderCallback(void *aToken, void *aCallbackData)
michael@0 834 {
michael@0 835 nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable =
michael@0 836 dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
michael@0 837 aRunnable->SetToken(aToken);
michael@0 838 NS_DispatchToMainThread(aRunnable);
michael@0 839 }
michael@0 840
michael@0 841 nsresult
michael@0 842 nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
michael@0 843 {
michael@0 844 if (!aRequest->mElement->GetScriptAsync() || aRequest->mIsInline) {
michael@0 845 return NS_ERROR_FAILURE;
michael@0 846 }
michael@0 847
michael@0 848 nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
michael@0 849 if (!globalObject) {
michael@0 850 return NS_ERROR_FAILURE;
michael@0 851 }
michael@0 852
michael@0 853 nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
michael@0 854 if (!context) {
michael@0 855 return NS_ERROR_FAILURE;
michael@0 856 }
michael@0 857
michael@0 858 AutoPushJSContext cx(context->GetNativeContext());
michael@0 859 JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
michael@0 860
michael@0 861 JS::CompileOptions options(cx);
michael@0 862 FillCompileOptionsForRequest(aRequest, global, &options);
michael@0 863
michael@0 864 if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
michael@0 865 return NS_ERROR_FAILURE;
michael@0 866 }
michael@0 867
michael@0 868 nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
michael@0 869 new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
michael@0 870
michael@0 871 if (!JS::CompileOffThread(cx, options,
michael@0 872 aRequest->mScriptTextBuf, aRequest->mScriptTextLength,
michael@0 873 OffThreadScriptLoaderCallback,
michael@0 874 static_cast<void*>(runnable))) {
michael@0 875 return NS_ERROR_OUT_OF_MEMORY;
michael@0 876 }
michael@0 877
michael@0 878 mDocument->BlockOnload();
michael@0 879
michael@0 880 unused << runnable.forget();
michael@0 881 return NS_OK;
michael@0 882 }
michael@0 883
michael@0 884 nsresult
michael@0 885 nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest, void **aOffThreadToken)
michael@0 886 {
michael@0 887 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
michael@0 888 "Processing requests when running scripts is unsafe.");
michael@0 889
michael@0 890 if (!aOffThreadToken) {
michael@0 891 nsresult rv = AttemptAsyncScriptParse(aRequest);
michael@0 892 if (rv != NS_ERROR_FAILURE)
michael@0 893 return rv;
michael@0 894 }
michael@0 895
michael@0 896 NS_ENSURE_ARG(aRequest);
michael@0 897 nsAutoString textData;
michael@0 898 const jschar* scriptBuf = nullptr;
michael@0 899 size_t scriptLength = 0;
michael@0 900 JS::SourceBufferHolder::Ownership giveScriptOwnership =
michael@0 901 JS::SourceBufferHolder::NoOwnership;
michael@0 902
michael@0 903 nsCOMPtr<nsIDocument> doc;
michael@0 904
michael@0 905 nsCOMPtr<nsINode> scriptElem = do_QueryInterface(aRequest->mElement);
michael@0 906
michael@0 907 // If there's no script text, we try to get it from the element
michael@0 908 if (aRequest->mIsInline) {
michael@0 909 // XXX This is inefficient - GetText makes multiple
michael@0 910 // copies.
michael@0 911 aRequest->mElement->GetScriptText(textData);
michael@0 912
michael@0 913 scriptBuf = textData.get();
michael@0 914 scriptLength = textData.Length();
michael@0 915 giveScriptOwnership = JS::SourceBufferHolder::NoOwnership;
michael@0 916 }
michael@0 917 else {
michael@0 918 scriptBuf = aRequest->mScriptTextBuf;
michael@0 919 scriptLength = aRequest->mScriptTextLength;
michael@0 920
michael@0 921 giveScriptOwnership = JS::SourceBufferHolder::GiveOwnership;
michael@0 922 aRequest->mScriptTextBuf = nullptr;
michael@0 923 aRequest->mScriptTextLength = 0;
michael@0 924
michael@0 925 doc = scriptElem->OwnerDoc();
michael@0 926 }
michael@0 927
michael@0 928 JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, giveScriptOwnership);
michael@0 929
michael@0 930 nsCOMPtr<nsIScriptElement> oldParserInsertedScript;
michael@0 931 uint32_t parserCreated = aRequest->mElement->GetParserCreated();
michael@0 932 if (parserCreated) {
michael@0 933 oldParserInsertedScript = mCurrentParserInsertedScript;
michael@0 934 mCurrentParserInsertedScript = aRequest->mElement;
michael@0 935 }
michael@0 936
michael@0 937 FireScriptAvailable(NS_OK, aRequest);
michael@0 938
michael@0 939 // The window may have gone away by this point, in which case there's no point
michael@0 940 // in trying to run the script.
michael@0 941 nsPIDOMWindow *pwin = mDocument->GetInnerWindow();
michael@0 942 bool runScript = !!pwin;
michael@0 943 if (runScript) {
michael@0 944 nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
michael@0 945 scriptElem,
michael@0 946 NS_LITERAL_STRING("beforescriptexecute"),
michael@0 947 true, true, &runScript);
michael@0 948 }
michael@0 949
michael@0 950 // Inner window could have gone away after firing beforescriptexecute
michael@0 951 pwin = mDocument->GetInnerWindow();
michael@0 952 if (!pwin) {
michael@0 953 runScript = false;
michael@0 954 }
michael@0 955
michael@0 956 nsresult rv = NS_OK;
michael@0 957 if (runScript) {
michael@0 958 if (doc) {
michael@0 959 doc->BeginEvaluatingExternalScript();
michael@0 960 }
michael@0 961 aRequest->mElement->BeginEvaluating();
michael@0 962 rv = EvaluateScript(aRequest, srcBuf, aOffThreadToken);
michael@0 963 aRequest->mElement->EndEvaluating();
michael@0 964 if (doc) {
michael@0 965 doc->EndEvaluatingExternalScript();
michael@0 966 }
michael@0 967
michael@0 968 nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
michael@0 969 scriptElem,
michael@0 970 NS_LITERAL_STRING("afterscriptexecute"),
michael@0 971 true, false);
michael@0 972 }
michael@0 973
michael@0 974 FireScriptEvaluated(rv, aRequest);
michael@0 975
michael@0 976 if (parserCreated) {
michael@0 977 mCurrentParserInsertedScript = oldParserInsertedScript;
michael@0 978 }
michael@0 979
michael@0 980 return rv;
michael@0 981 }
michael@0 982
michael@0 983 void
michael@0 984 nsScriptLoader::FireScriptAvailable(nsresult aResult,
michael@0 985 nsScriptLoadRequest* aRequest)
michael@0 986 {
michael@0 987 for (int32_t i = 0; i < mObservers.Count(); i++) {
michael@0 988 nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
michael@0 989 obs->ScriptAvailable(aResult, aRequest->mElement,
michael@0 990 aRequest->mIsInline, aRequest->mURI,
michael@0 991 aRequest->mLineNo);
michael@0 992 }
michael@0 993
michael@0 994 aRequest->FireScriptAvailable(aResult);
michael@0 995 }
michael@0 996
michael@0 997 void
michael@0 998 nsScriptLoader::FireScriptEvaluated(nsresult aResult,
michael@0 999 nsScriptLoadRequest* aRequest)
michael@0 1000 {
michael@0 1001 for (int32_t i = 0; i < mObservers.Count(); i++) {
michael@0 1002 nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
michael@0 1003 obs->ScriptEvaluated(aResult, aRequest->mElement,
michael@0 1004 aRequest->mIsInline);
michael@0 1005 }
michael@0 1006
michael@0 1007 aRequest->FireScriptEvaluated(aResult);
michael@0 1008 }
michael@0 1009
michael@0 1010 already_AddRefed<nsIScriptGlobalObject>
michael@0 1011 nsScriptLoader::GetScriptGlobalObject()
michael@0 1012 {
michael@0 1013 nsPIDOMWindow *pwin = mDocument->GetInnerWindow();
michael@0 1014 if (!pwin) {
michael@0 1015 return nullptr;
michael@0 1016 }
michael@0 1017
michael@0 1018 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
michael@0 1019 NS_ASSERTION(globalObject, "windows must be global objects");
michael@0 1020
michael@0 1021 // and make sure we are setup for this type of script.
michael@0 1022 nsresult rv = globalObject->EnsureScriptEnvironment();
michael@0 1023 if (NS_FAILED(rv)) {
michael@0 1024 return nullptr;
michael@0 1025 }
michael@0 1026
michael@0 1027 return globalObject.forget();
michael@0 1028 }
michael@0 1029
michael@0 1030 void
michael@0 1031 nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
michael@0 1032 JS::Handle<JSObject *> aScopeChain,
michael@0 1033 JS::CompileOptions *aOptions)
michael@0 1034 {
michael@0 1035 // It's very important to use aRequest->mURI, not the final URI of the channel
michael@0 1036 // aRequest ended up getting script data from, as the script filename.
michael@0 1037 nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI, aRequest->mURL);
michael@0 1038
michael@0 1039 aOptions->setIntroductionType("scriptElement");
michael@0 1040 aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo);
michael@0 1041 aOptions->setVersion(JSVersion(aRequest->mJSVersion));
michael@0 1042 aOptions->setCompileAndGo(JS_IsGlobalObject(aScopeChain));
michael@0 1043 if (aRequest->mHasSourceMapURL) {
michael@0 1044 aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());
michael@0 1045 }
michael@0 1046 if (aRequest->mOriginPrincipal) {
michael@0 1047 aOptions->setOriginPrincipals(nsJSPrincipals::get(aRequest->mOriginPrincipal));
michael@0 1048 }
michael@0 1049
michael@0 1050 AutoJSContext cx;
michael@0 1051 JS::Rooted<JS::Value> elementVal(cx);
michael@0 1052 MOZ_ASSERT(aRequest->mElement);
michael@0 1053 // XXXbz this is super-fragile, because the code that _uses_ the
michael@0 1054 // JS::CompileOptions is nowhere near us, but we have to coordinate
michael@0 1055 // compartments with it... and in particular, it will compile in the
michael@0 1056 // compartment of aScopeChain, so we want to wrap into that compartment as
michael@0 1057 // well.
michael@0 1058 JSAutoCompartment ac(cx, aScopeChain);
michael@0 1059 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aRequest->mElement,
michael@0 1060 &elementVal,
michael@0 1061 /* aAllowWrapping = */ true))) {
michael@0 1062 MOZ_ASSERT(elementVal.isObject());
michael@0 1063 aOptions->setElement(&elementVal.toObject());
michael@0 1064 }
michael@0 1065 }
michael@0 1066
michael@0 1067 nsresult
michael@0 1068 nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
michael@0 1069 JS::SourceBufferHolder& aSrcBuf,
michael@0 1070 void** aOffThreadToken)
michael@0 1071 {
michael@0 1072 // We need a document to evaluate scripts.
michael@0 1073 if (!mDocument) {
michael@0 1074 return NS_ERROR_FAILURE;
michael@0 1075 }
michael@0 1076
michael@0 1077 nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
michael@0 1078 nsIDocument* ownerDoc = scriptContent->OwnerDoc();
michael@0 1079 if (ownerDoc != mDocument) {
michael@0 1080 // Willful violation of HTML5 as of 2010-12-01
michael@0 1081 return NS_ERROR_FAILURE;
michael@0 1082 }
michael@0 1083
michael@0 1084 // Get the script-type to be used by this element.
michael@0 1085 NS_ASSERTION(scriptContent, "no content - what is default script-type?");
michael@0 1086
michael@0 1087 nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
michael@0 1088 if (!globalObject) {
michael@0 1089 return NS_ERROR_FAILURE;
michael@0 1090 }
michael@0 1091
michael@0 1092 // Make sure context is a strong reference since we access it after
michael@0 1093 // we've executed a script, which may cause all other references to
michael@0 1094 // the context to go away.
michael@0 1095 nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
michael@0 1096 if (!context) {
michael@0 1097 return NS_ERROR_FAILURE;
michael@0 1098 }
michael@0 1099
michael@0 1100 JSVersion version = JSVersion(aRequest->mJSVersion);
michael@0 1101 if (version == JSVERSION_UNKNOWN) {
michael@0 1102 return NS_OK;
michael@0 1103 }
michael@0 1104
michael@0 1105 // New script entry point required, due to the "Create a script" sub-step of
michael@0 1106 // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
michael@0 1107 AutoEntryScript entryScript(globalObject, true, context->GetNativeContext());
michael@0 1108 JS::Rooted<JSObject*> global(entryScript.cx(),
michael@0 1109 globalObject->GetGlobalJSObject());
michael@0 1110
michael@0 1111 bool oldProcessingScriptTag = context->GetProcessingScriptTag();
michael@0 1112 context->SetProcessingScriptTag(true);
michael@0 1113
michael@0 1114 // Update our current script.
michael@0 1115 nsCOMPtr<nsIScriptElement> oldCurrent = mCurrentScript;
michael@0 1116 mCurrentScript = aRequest->mElement;
michael@0 1117
michael@0 1118 JS::CompileOptions options(entryScript.cx());
michael@0 1119 FillCompileOptionsForRequest(aRequest, global, &options);
michael@0 1120 nsresult rv = nsJSUtils::EvaluateString(entryScript.cx(), aSrcBuf, global, options,
michael@0 1121 aOffThreadToken);
michael@0 1122
michael@0 1123 // Put the old script back in case it wants to do anything else.
michael@0 1124 mCurrentScript = oldCurrent;
michael@0 1125
michael@0 1126 context->SetProcessingScriptTag(oldProcessingScriptTag);
michael@0 1127 return rv;
michael@0 1128 }
michael@0 1129
michael@0 1130 void
michael@0 1131 nsScriptLoader::ProcessPendingRequestsAsync()
michael@0 1132 {
michael@0 1133 if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) {
michael@0 1134 nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
michael@0 1135 &nsScriptLoader::ProcessPendingRequests);
michael@0 1136
michael@0 1137 NS_DispatchToCurrentThread(ev);
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 void
michael@0 1142 nsScriptLoader::ProcessPendingRequests()
michael@0 1143 {
michael@0 1144 nsRefPtr<nsScriptLoadRequest> request;
michael@0 1145 if (mParserBlockingRequest &&
michael@0 1146 !mParserBlockingRequest->mLoading &&
michael@0 1147 ReadyToExecuteScripts()) {
michael@0 1148 request.swap(mParserBlockingRequest);
michael@0 1149 UnblockParser(request);
michael@0 1150 ProcessRequest(request);
michael@0 1151 ContinueParserAsync(request);
michael@0 1152 }
michael@0 1153
michael@0 1154 while (ReadyToExecuteScripts() &&
michael@0 1155 !mXSLTRequests.IsEmpty() &&
michael@0 1156 !mXSLTRequests[0]->mLoading) {
michael@0 1157 request.swap(mXSLTRequests[0]);
michael@0 1158 mXSLTRequests.RemoveElementAt(0);
michael@0 1159 ProcessRequest(request);
michael@0 1160 }
michael@0 1161
michael@0 1162 uint32_t i = 0;
michael@0 1163 while (mEnabled && i < mAsyncRequests.Length()) {
michael@0 1164 if (!mAsyncRequests[i]->mLoading) {
michael@0 1165 request.swap(mAsyncRequests[i]);
michael@0 1166 mAsyncRequests.RemoveElementAt(i);
michael@0 1167 ProcessRequest(request);
michael@0 1168 continue;
michael@0 1169 }
michael@0 1170 ++i;
michael@0 1171 }
michael@0 1172
michael@0 1173 while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
michael@0 1174 !mNonAsyncExternalScriptInsertedRequests[0]->mLoading) {
michael@0 1175 // Violate the HTML5 spec and execute these in the insertion order in
michael@0 1176 // order to make LABjs and the "order" plug-in for RequireJS work with
michael@0 1177 // their Gecko-sniffed code path. See
michael@0 1178 // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
michael@0 1179 request.swap(mNonAsyncExternalScriptInsertedRequests[0]);
michael@0 1180 mNonAsyncExternalScriptInsertedRequests.RemoveElementAt(0);
michael@0 1181 ProcessRequest(request);
michael@0 1182 }
michael@0 1183
michael@0 1184 if (mDocumentParsingDone && mXSLTRequests.IsEmpty()) {
michael@0 1185 while (!mDeferRequests.IsEmpty() && !mDeferRequests[0]->mLoading) {
michael@0 1186 request.swap(mDeferRequests[0]);
michael@0 1187 mDeferRequests.RemoveElementAt(0);
michael@0 1188 ProcessRequest(request);
michael@0 1189 }
michael@0 1190 }
michael@0 1191
michael@0 1192 while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
michael@0 1193 nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
michael@0 1194 mPendingChildLoaders.RemoveElementAt(0);
michael@0 1195 child->RemoveExecuteBlocker();
michael@0 1196 }
michael@0 1197
michael@0 1198 if (mDocumentParsingDone && mDocument &&
michael@0 1199 !mParserBlockingRequest && mAsyncRequests.IsEmpty() &&
michael@0 1200 mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
michael@0 1201 mXSLTRequests.IsEmpty() && mDeferRequests.IsEmpty()) {
michael@0 1202 if (MaybeRemovedDeferRequests()) {
michael@0 1203 return ProcessPendingRequests();
michael@0 1204 }
michael@0 1205 // No more pending scripts; time to unblock onload.
michael@0 1206 // OK to unblock onload synchronously here, since callers must be
michael@0 1207 // prepared for the world changing anyway.
michael@0 1208 mDocumentParsingDone = false;
michael@0 1209 mDocument->UnblockOnload(true);
michael@0 1210 }
michael@0 1211 }
michael@0 1212
michael@0 1213 bool
michael@0 1214 nsScriptLoader::ReadyToExecuteScripts()
michael@0 1215 {
michael@0 1216 // Make sure the SelfReadyToExecuteScripts check is first, so that
michael@0 1217 // we don't block twice on an ancestor.
michael@0 1218 if (!SelfReadyToExecuteScripts()) {
michael@0 1219 return false;
michael@0 1220 }
michael@0 1221
michael@0 1222 for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
michael@0 1223 nsScriptLoader* ancestor = doc->ScriptLoader();
michael@0 1224 if (!ancestor->SelfReadyToExecuteScripts() &&
michael@0 1225 ancestor->AddPendingChildLoader(this)) {
michael@0 1226 AddExecuteBlocker();
michael@0 1227 return false;
michael@0 1228 }
michael@0 1229 }
michael@0 1230
michael@0 1231 return true;
michael@0 1232 }
michael@0 1233
michael@0 1234
michael@0 1235 // This function was copied from nsParser.cpp. It was simplified a bit.
michael@0 1236 static bool
michael@0 1237 DetectByteOrderMark(const unsigned char* aBytes, int32_t aLen, nsCString& oCharset)
michael@0 1238 {
michael@0 1239 if (aLen < 2)
michael@0 1240 return false;
michael@0 1241
michael@0 1242 switch(aBytes[0]) {
michael@0 1243 case 0xEF:
michael@0 1244 if (aLen >= 3 && 0xBB == aBytes[1] && 0xBF == aBytes[2]) {
michael@0 1245 // EF BB BF
michael@0 1246 // Win2K UTF-8 BOM
michael@0 1247 oCharset.Assign("UTF-8");
michael@0 1248 }
michael@0 1249 break;
michael@0 1250 case 0xFE:
michael@0 1251 if (0xFF == aBytes[1]) {
michael@0 1252 // FE FF
michael@0 1253 // UTF-16, big-endian
michael@0 1254 oCharset.Assign("UTF-16BE");
michael@0 1255 }
michael@0 1256 break;
michael@0 1257 case 0xFF:
michael@0 1258 if (0xFE == aBytes[1]) {
michael@0 1259 // FF FE
michael@0 1260 // UTF-16, little-endian
michael@0 1261 oCharset.Assign("UTF-16LE");
michael@0 1262 }
michael@0 1263 break;
michael@0 1264 }
michael@0 1265 return !oCharset.IsEmpty();
michael@0 1266 }
michael@0 1267
michael@0 1268 /* static */ nsresult
michael@0 1269 nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
michael@0 1270 uint32_t aLength, const nsAString& aHintCharset,
michael@0 1271 nsIDocument* aDocument,
michael@0 1272 jschar*& aBufOut, size_t& aLengthOut)
michael@0 1273 {
michael@0 1274 if (!aLength) {
michael@0 1275 aBufOut = nullptr;
michael@0 1276 aLengthOut = 0;
michael@0 1277 return NS_OK;
michael@0 1278 }
michael@0 1279
michael@0 1280 // The encoding info precedence is as follows from high to low:
michael@0 1281 // The BOM
michael@0 1282 // HTTP Content-Type (if name recognized)
michael@0 1283 // charset attribute (if name recognized)
michael@0 1284 // The encoding of the document
michael@0 1285
michael@0 1286 nsAutoCString charset;
michael@0 1287
michael@0 1288 nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
michael@0 1289
michael@0 1290 if (DetectByteOrderMark(aData, aLength, charset)) {
michael@0 1291 // charset is now "UTF-8" or "UTF-16". The UTF-16 decoder will re-sniff
michael@0 1292 // the BOM for endianness. Both the UTF-16 and the UTF-8 decoder will
michael@0 1293 // take care of swallowing the BOM.
michael@0 1294 unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
michael@0 1295 }
michael@0 1296
michael@0 1297 if (!unicodeDecoder &&
michael@0 1298 aChannel &&
michael@0 1299 NS_SUCCEEDED(aChannel->GetContentCharset(charset)) &&
michael@0 1300 EncodingUtils::FindEncodingForLabel(charset, charset)) {
michael@0 1301 unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
michael@0 1302 }
michael@0 1303
michael@0 1304 if (!unicodeDecoder &&
michael@0 1305 EncodingUtils::FindEncodingForLabel(aHintCharset, charset)) {
michael@0 1306 unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
michael@0 1307 }
michael@0 1308
michael@0 1309 if (!unicodeDecoder && aDocument) {
michael@0 1310 charset = aDocument->GetDocumentCharacterSet();
michael@0 1311 unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
michael@0 1312 }
michael@0 1313
michael@0 1314 if (!unicodeDecoder) {
michael@0 1315 // Curiously, there are various callers that don't pass aDocument. The
michael@0 1316 // fallback in the old code was ISO-8859-1, which behaved like
michael@0 1317 // windows-1252. Saying windows-1252 for clarity and for compliance
michael@0 1318 // with the Encoding Standard.
michael@0 1319 unicodeDecoder = EncodingUtils::DecoderForEncoding("windows-1252");
michael@0 1320 }
michael@0 1321
michael@0 1322 int32_t unicodeLength = 0;
michael@0 1323
michael@0 1324 nsresult rv =
michael@0 1325 unicodeDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
michael@0 1326 aLength, &unicodeLength);
michael@0 1327 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1328
michael@0 1329 aBufOut = static_cast<jschar*>(js_malloc(unicodeLength * sizeof(jschar)));
michael@0 1330 if (!aBufOut) {
michael@0 1331 aLengthOut = 0;
michael@0 1332 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1333 }
michael@0 1334 aLengthOut = unicodeLength;
michael@0 1335
michael@0 1336 rv = unicodeDecoder->Convert(reinterpret_cast<const char*>(aData),
michael@0 1337 (int32_t *) &aLength, aBufOut,
michael@0 1338 &unicodeLength);
michael@0 1339 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 1340 aLengthOut = unicodeLength;
michael@0 1341 if (NS_FAILED(rv)) {
michael@0 1342 js_free(aBufOut);
michael@0 1343 aBufOut = nullptr;
michael@0 1344 aLengthOut = 0;
michael@0 1345 }
michael@0 1346 return rv;
michael@0 1347 }
michael@0 1348
michael@0 1349 NS_IMETHODIMP
michael@0 1350 nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
michael@0 1351 nsISupports* aContext,
michael@0 1352 nsresult aStatus,
michael@0 1353 uint32_t aStringLen,
michael@0 1354 const uint8_t* aString)
michael@0 1355 {
michael@0 1356 nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
michael@0 1357 NS_ASSERTION(request, "null request in stream complete handler");
michael@0 1358 NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
michael@0 1359
michael@0 1360 nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
michael@0 1361 aString);
michael@0 1362 if (NS_FAILED(rv)) {
michael@0 1363 if (mDeferRequests.RemoveElement(request) ||
michael@0 1364 mAsyncRequests.RemoveElement(request) ||
michael@0 1365 mNonAsyncExternalScriptInsertedRequests.RemoveElement(request) ||
michael@0 1366 mXSLTRequests.RemoveElement(request)) {
michael@0 1367 FireScriptAvailable(rv, request);
michael@0 1368 } else if (mParserBlockingRequest == request) {
michael@0 1369 mParserBlockingRequest = nullptr;
michael@0 1370 UnblockParser(request);
michael@0 1371 FireScriptAvailable(rv, request);
michael@0 1372 ContinueParserAsync(request);
michael@0 1373 } else {
michael@0 1374 mPreloads.RemoveElement(request, PreloadRequestComparator());
michael@0 1375 }
michael@0 1376 rv = NS_OK;
michael@0 1377 } else {
michael@0 1378 NS_Free(const_cast<uint8_t *>(aString));
michael@0 1379 rv = NS_SUCCESS_ADOPTED_DATA;
michael@0 1380 }
michael@0 1381
michael@0 1382 // Process our request and/or any pending ones
michael@0 1383 ProcessPendingRequests();
michael@0 1384
michael@0 1385 return rv;
michael@0 1386 }
michael@0 1387
michael@0 1388 void
michael@0 1389 nsScriptLoader::UnblockParser(nsScriptLoadRequest* aParserBlockingRequest)
michael@0 1390 {
michael@0 1391 aParserBlockingRequest->mElement->UnblockParser();
michael@0 1392 }
michael@0 1393
michael@0 1394 void
michael@0 1395 nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest)
michael@0 1396 {
michael@0 1397 aParserBlockingRequest->mElement->ContinueParserAsync();
michael@0 1398 }
michael@0 1399
michael@0 1400 nsresult
michael@0 1401 nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
michael@0 1402 nsIStreamLoader* aLoader,
michael@0 1403 nsresult aStatus,
michael@0 1404 uint32_t aStringLen,
michael@0 1405 const uint8_t* aString)
michael@0 1406 {
michael@0 1407 if (NS_FAILED(aStatus)) {
michael@0 1408 return aStatus;
michael@0 1409 }
michael@0 1410
michael@0 1411 // If we don't have a document, then we need to abort further
michael@0 1412 // evaluation.
michael@0 1413 if (!mDocument) {
michael@0 1414 return NS_ERROR_NOT_AVAILABLE;
michael@0 1415 }
michael@0 1416
michael@0 1417 // If the load returned an error page, then we need to abort
michael@0 1418 nsCOMPtr<nsIRequest> req;
michael@0 1419 nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
michael@0 1420 NS_ASSERTION(req, "StreamLoader's request went away prematurely");
michael@0 1421 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1422
michael@0 1423 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req);
michael@0 1424 if (httpChannel) {
michael@0 1425 bool requestSucceeded;
michael@0 1426 rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
michael@0 1427 if (NS_SUCCEEDED(rv) && !requestSucceeded) {
michael@0 1428 return NS_ERROR_NOT_AVAILABLE;
michael@0 1429 }
michael@0 1430
michael@0 1431 nsAutoCString sourceMapURL;
michael@0 1432 httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL);
michael@0 1433 aRequest->mHasSourceMapURL = true;
michael@0 1434 aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL);
michael@0 1435 }
michael@0 1436
michael@0 1437 nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
michael@0 1438 // If this load was subject to a CORS check; don't flag it with a
michael@0 1439 // separate origin principal, so that it will treat our document's
michael@0 1440 // principal as the origin principal
michael@0 1441 if (aRequest->mCORSMode == CORS_NONE) {
michael@0 1442 rv = nsContentUtils::GetSecurityManager()->
michael@0 1443 GetChannelPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal));
michael@0 1444 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1445 }
michael@0 1446
michael@0 1447 if (aStringLen) {
michael@0 1448 // Check the charset attribute to determine script charset.
michael@0 1449 nsAutoString hintCharset;
michael@0 1450 if (!aRequest->IsPreload()) {
michael@0 1451 aRequest->mElement->GetScriptCharset(hintCharset);
michael@0 1452 } else {
michael@0 1453 nsTArray<PreloadInfo>::index_type i =
michael@0 1454 mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator());
michael@0 1455 NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping");
michael@0 1456 hintCharset = mPreloads[i].mCharset;
michael@0 1457 }
michael@0 1458 rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
michael@0 1459 aRequest->mScriptTextBuf, aRequest->mScriptTextLength);
michael@0 1460
michael@0 1461 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1462 }
michael@0 1463
michael@0 1464 // This assertion could fire errorously if we ran out of memory when
michael@0 1465 // inserting the request in the array. However it's an unlikely case
michael@0 1466 // so if you see this assertion it is likely something else that is
michael@0 1467 // wrong, especially if you see it more than once.
michael@0 1468 NS_ASSERTION(mDeferRequests.Contains(aRequest) ||
michael@0 1469 mAsyncRequests.Contains(aRequest) ||
michael@0 1470 mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
michael@0 1471 mXSLTRequests.Contains(aRequest) ||
michael@0 1472 mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
michael@0 1473 mParserBlockingRequest,
michael@0 1474 "aRequest should be pending!");
michael@0 1475
michael@0 1476 // Mark this as loaded
michael@0 1477 aRequest->mLoading = false;
michael@0 1478
michael@0 1479 return NS_OK;
michael@0 1480 }
michael@0 1481
michael@0 1482 void
michael@0 1483 nsScriptLoader::ParsingComplete(bool aTerminated)
michael@0 1484 {
michael@0 1485 if (mDeferEnabled) {
michael@0 1486 // Have to check because we apparently get ParsingComplete
michael@0 1487 // without BeginDeferringScripts in some cases
michael@0 1488 mDocumentParsingDone = true;
michael@0 1489 }
michael@0 1490 mDeferEnabled = false;
michael@0 1491 if (aTerminated) {
michael@0 1492 mDeferRequests.Clear();
michael@0 1493 mAsyncRequests.Clear();
michael@0 1494 mNonAsyncExternalScriptInsertedRequests.Clear();
michael@0 1495 mXSLTRequests.Clear();
michael@0 1496 mParserBlockingRequest = nullptr;
michael@0 1497 }
michael@0 1498
michael@0 1499 // Have to call this even if aTerminated so we'll correctly unblock
michael@0 1500 // onload and all.
michael@0 1501 ProcessPendingRequests();
michael@0 1502 }
michael@0 1503
michael@0 1504 void
michael@0 1505 nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
michael@0 1506 const nsAString &aType,
michael@0 1507 const nsAString &aCrossOrigin,
michael@0 1508 bool aScriptFromHead)
michael@0 1509 {
michael@0 1510 // Check to see if scripts has been turned off.
michael@0 1511 if (!mEnabled || !mDocument->IsScriptEnabled()) {
michael@0 1512 return;
michael@0 1513 }
michael@0 1514
michael@0 1515 nsRefPtr<nsScriptLoadRequest> request =
michael@0 1516 new nsScriptLoadRequest(nullptr, 0,
michael@0 1517 Element::StringToCORSMode(aCrossOrigin));
michael@0 1518 request->mURI = aURI;
michael@0 1519 request->mIsInline = false;
michael@0 1520 request->mLoading = true;
michael@0 1521 nsresult rv = StartLoad(request, aType, aScriptFromHead);
michael@0 1522 if (NS_FAILED(rv)) {
michael@0 1523 return;
michael@0 1524 }
michael@0 1525
michael@0 1526 PreloadInfo *pi = mPreloads.AppendElement();
michael@0 1527 pi->mRequest = request;
michael@0 1528 pi->mCharset = aCharset;
michael@0 1529 }
michael@0 1530
michael@0 1531 void
michael@0 1532 nsScriptLoader::AddDeferRequest(nsScriptLoadRequest* aRequest)
michael@0 1533 {
michael@0 1534 mDeferRequests.AppendElement(aRequest);
michael@0 1535 if (mDeferEnabled && mDeferRequests.Length() == 1 && mDocument &&
michael@0 1536 !mBlockingDOMContentLoaded) {
michael@0 1537 MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING);
michael@0 1538 mBlockingDOMContentLoaded = true;
michael@0 1539 mDocument->BlockDOMContentLoaded();
michael@0 1540 }
michael@0 1541 }
michael@0 1542
michael@0 1543 bool
michael@0 1544 nsScriptLoader::MaybeRemovedDeferRequests()
michael@0 1545 {
michael@0 1546 if (mDeferRequests.Length() == 0 && mDocument &&
michael@0 1547 mBlockingDOMContentLoaded) {
michael@0 1548 mBlockingDOMContentLoaded = false;
michael@0 1549 mDocument->UnblockDOMContentLoaded();
michael@0 1550 return true;
michael@0 1551 }
michael@0 1552 return false;
michael@0 1553 }

mercurial