nsJSProtocolHandler.cpp (46396B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=4 sw=2 et tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsJSProtocolHandler.h" 8 9 #include "DefaultURI.h" 10 #include "js/CompilationAndEvaluation.h" 11 #include "js/Wrapper.h" 12 #include "jsapi.h" 13 #include "mozilla/BasePrincipal.h" 14 #include "mozilla/CycleCollectedJSContext.h" 15 #include "mozilla/ErrorResult.h" 16 #include "mozilla/LoadInfo.h" 17 #include "mozilla/Maybe.h" 18 #include "mozilla/SourceLocation.h" 19 #include "mozilla/TextUtils.h" 20 #include "mozilla/dom/AutoEntryScript.h" 21 #include "mozilla/dom/DOMSecurityMonitor.h" 22 #include "mozilla/dom/Document.h" 23 #include "mozilla/dom/JSExecutionUtils.h" // mozilla::dom::Compile, mozilla::dom::EvaluationExceptionToNSResult 24 #include "mozilla/dom/PolicyContainer.h" 25 #include "mozilla/dom/PopupBlocker.h" 26 #include "mozilla/dom/ScriptSettings.h" 27 #include "mozilla/ipc/URIUtils.h" 28 #include "nsCOMPtr.h" 29 #include "nsCRT.h" 30 #include "nsContentSecurityManager.h" 31 #include "nsContentUtils.h" 32 #include "nsError.h" 33 #include "nsEscape.h" 34 #include "nsGlobalWindowInner.h" 35 #include "nsIClassInfoImpl.h" 36 #include "nsIContentSecurityPolicy.h" 37 #include "nsIDocShell.h" 38 #include "nsIDocumentViewer.h" 39 #include "nsIInterfaceRequestor.h" 40 #include "nsIInterfaceRequestorUtils.h" 41 #include "nsIObjectInputStream.h" 42 #include "nsIObjectOutputStream.h" 43 #include "nsIPrincipal.h" 44 #include "nsIScriptChannel.h" 45 #include "nsIScriptContext.h" 46 #include "nsIScriptGlobalObject.h" 47 #include "nsIStreamListener.h" 48 #include "nsIURI.h" 49 #include "nsIWebNavigation.h" 50 #include "nsIWritablePropertyBag2.h" 51 #include "nsJSUtils.h" 52 #include "nsNetUtil.h" 53 #include "nsPIDOMWindow.h" 54 #include "nsReadableUtils.h" 55 #include "nsSandboxFlags.h" 56 #include "nsString.h" 57 #include "nsStringStream.h" 58 #include "nsTextToSubURI.h" 59 #include "nsThreadUtils.h" 60 61 using mozilla::IsAscii; 62 using mozilla::dom::AutoEntryScript; 63 64 static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID); 65 66 // A stream class used to handle javascript: URLs. 67 class JSURLInputStream : public nsIInputStream { 68 public: 69 JSURLInputStream(); 70 71 NS_DECL_THREADSAFE_ISUPPORTS 72 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream) 73 74 nsresult Init(nsIURI* uri); 75 76 // @param aExecutionPolicy `nsIScriptChannel::NO_EXECUTION` or 77 // `nsIScriptChannel::EXECUTE_NORMAL`. 78 nsresult EvaluateScript( 79 nsIChannel* aChannel, 80 mozilla::dom::PopupBlocker::PopupControlState aPopupState, 81 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow, 82 const mozilla::JSCallingLocation& aJSCallingLocation); 83 84 protected: 85 virtual ~JSURLInputStream(); 86 87 nsCOMPtr<nsIInputStream> mInnerStream; 88 nsCString mScript; 89 nsCString mURL; 90 }; 91 92 // 93 // nsISupports implementation... 94 // 95 NS_IMPL_ISUPPORTS(JSURLInputStream, nsIInputStream) 96 97 JSURLInputStream::JSURLInputStream() = default; 98 99 JSURLInputStream::~JSURLInputStream() = default; 100 101 nsresult JSURLInputStream::Init(nsIURI* uri) { 102 NS_ENSURE_ARG_POINTER(uri); 103 104 // Get the script string to evaluate... 105 nsresult rv = uri->GetPathQueryRef(mScript); 106 if (NS_FAILED(rv)) return rv; 107 108 // Get the url. 109 rv = uri->GetSpec(mURL); 110 if (NS_FAILED(rv)) return rv; 111 112 return NS_OK; 113 } 114 115 static bool IsISO88591(const nsString& aString) { 116 for (nsString::const_char_iterator c = aString.BeginReading(), 117 c_end = aString.EndReading(); 118 c < c_end; ++c) { 119 if (*c > 255) return false; 120 } 121 return true; 122 } 123 124 static nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel) { 125 // Get the global object owner from the channel 126 nsCOMPtr<nsIDocShell> docShell; 127 NS_QueryNotificationCallbacks(aChannel, docShell); 128 if (!docShell) { 129 NS_WARNING("Unable to get a docShell from the channel!"); 130 return nullptr; 131 } 132 133 // So far so good: get the script global from its docshell 134 nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject(); 135 136 NS_ASSERTION(global, 137 "Unable to get an nsIScriptGlobalObject from the " 138 "docShell!"); 139 return global; 140 } 141 142 // https://w3c.github.io/webappsec-csp/#should-block-navigation-request 143 // specialized for type "navigation" requests with "javascript: URLs". 144 // Excluding step 2. 145 static bool AllowedByCSP(nsIContentSecurityPolicy* aCSP, 146 const nsACString& aJavaScriptURL, 147 const mozilla::JSCallingLocation& aJSCallingLocation) { 148 if (!aCSP) { 149 return true; 150 } 151 152 // Step 3. If result is "Allowed", and if navigation request’s current URL’s 153 // scheme is javascript: 154 // 155 // Step 3.1.1.2 If directive’s inline check returns "Allowed" when executed 156 // upon null, "navigation" and navigation request’s current URL, skip to the 157 // next directive. 158 // 159 // This means /type/ is "navigation" and /source string/ is the 160 // "navigation request’s current URL". 161 // 162 // Per 163 // https://w3c.github.io/webappsec-csp/#effective-directive-for-inline-check 164 // type "navigation" maps to the effective directive script-src-elem. 165 bool allowsInlineScript = true; 166 167 nsresult rv = 168 aCSP->GetAllowsInline(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE, 169 true, // aHasUnsafeHash 170 u""_ns, // aNonce 171 true, // aParserCreated 172 nullptr, // aElement, 173 nullptr, // nsICSPEventListener 174 NS_ConvertASCIItoUTF16(aJavaScriptURL), // aContent 175 aJSCallingLocation.mLine, // aLineNumber 176 aJSCallingLocation.mColumn, // aColumnNumber 177 &allowsInlineScript); 178 179 return (NS_SUCCEEDED(rv) && allowsInlineScript); 180 } 181 182 // https://html.spec.whatwg.org/#evaluate-a-javascript:-url 183 // Steps 7-10. 184 // 185 // If the execution result is a string, |aRv| is set to success, and 186 // |aRetValue| is set to the string. 187 // 188 // If the execution result is not a string, |aRv| is set to success, and 189 // |aRetValue| is set to undefined. 190 // 191 // In case of a evaluation failure during the execution, |aRv| is set to the 192 // corresponding result code and |aRetValue| remains unchanged. 193 static void ExecScriptAndGetString(JSContext* aCx, 194 JS::Handle<JSScript*> aScript, 195 JS::MutableHandle<JS::Value> aRetValue, 196 mozilla::ErrorResult& aRv) { 197 MOZ_ASSERT(aScript); 198 199 // Step 7. Let evaluationStatus be the result of running the classic script 200 // script. 201 if (!JS_ExecuteScript(aCx, aScript, aRetValue)) { 202 aRv.NoteJSContextException(aCx); 203 return; 204 } 205 206 // Step 8. Let result be null. 207 // Step 9. If evaluationStatus is a normal completion, and 208 // evaluationStatus.[[Value]] is a String, then set result to 209 // evaluationStatus.[[Value]]. 210 if (aRetValue.isString()) { 211 return; 212 } 213 214 // Step 10. Otherwise, return null. 215 // 216 // NOTE: The `null` here is the return value of the entire algorithm. 217 // This function returns `undefined` for all cases and let the caller 218 // handle it. 219 aRetValue.setUndefined(); 220 } 221 222 nsresult JSURLInputStream::EvaluateScript( 223 nsIChannel* aChannel, 224 mozilla::dom::PopupBlocker::PopupControlState aPopupState, 225 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow, 226 const mozilla::JSCallingLocation& aJSCallingLocation) { 227 if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) { 228 // Nothing to do here. 229 return NS_ERROR_DOM_RETVAL_UNDEFINED; 230 } 231 232 NS_ENSURE_ARG_POINTER(aChannel); 233 MOZ_ASSERT(aOriginalInnerWindow, 234 "We should not have gotten here if this was null!"); 235 236 // Set the channel's resultPrincipalURI to the active document's URI. This 237 // corresponds to treating that URI as the URI of our channel's response. In 238 // the spec we're supposed to use the URL of the active document, but since 239 // we bail out of here if the inner window has changed, and GetDocumentURI() 240 // on the inner window returns the URL of the active document if the inner 241 // window is current, this is equivalent to the spec behavior. 242 nsCOMPtr<nsIURI> docURI = aOriginalInnerWindow->GetDocumentURI(); 243 if (!docURI) { 244 // We're not going to be able to have a sane URL, so just don't run the 245 // script at all. 246 return NS_ERROR_DOM_RETVAL_UNDEFINED; 247 } 248 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 249 loadInfo->SetResultPrincipalURI(docURI); 250 251 #ifdef DEBUG 252 DOMSecurityMonitor::AuditUseOfJavaScriptURI(aChannel); 253 #endif 254 255 // Get principal of code for execution 256 nsCOMPtr<nsISupports> owner; 257 aChannel->GetOwner(getter_AddRefs(owner)); 258 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner); 259 if (!principal) { 260 if (loadInfo->GetForceInheritPrincipal()) { 261 principal = loadInfo->FindPrincipalToInherit(aChannel); 262 } else { 263 // No execution without a principal! 264 NS_ASSERTION(!owner, "Non-principal owner?"); 265 NS_WARNING("No principal to execute JS with"); 266 return NS_ERROR_DOM_RETVAL_UNDEFINED; 267 } 268 } 269 270 nsresult rv; 271 272 // CSP check: javascript: URIs are disabled unless "inline" scripts 273 // are allowed by both the CSP of the thing that started the load 274 // (which is the CSPToInherit of the loadinfo) and the CSP of the 275 // target document. The target document check is performed below, 276 // once we have determined the target document. 277 nsCOMPtr<nsIPolicyContainer> policyContainer = 278 loadInfo->GetPolicyContainerToInherit(); 279 nsCOMPtr<nsIContentSecurityPolicy> csp = 280 PolicyContainer::GetCSP(policyContainer); 281 282 if (!AllowedByCSP(csp, mURL, aJSCallingLocation)) { 283 return NS_ERROR_DOM_RETVAL_UNDEFINED; 284 } 285 286 // Get the global object we should be running on. 287 nsIScriptGlobalObject* global = GetGlobalObject(aChannel); 288 if (!global) { 289 return NS_ERROR_FAILURE; 290 } 291 292 // Make sure we still have the same inner window as we used to. 293 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(global); 294 nsPIDOMWindowInner* innerWin = win->GetCurrentInnerWindow(); 295 296 if (innerWin != aOriginalInnerWindow) { 297 return NS_ERROR_UNEXPECTED; 298 } 299 300 mozilla::dom::Document* targetDoc = innerWin->GetExtantDoc(); 301 302 // Sandboxed document check: javascript: URI execution is disabled in a 303 // sandboxed document unless 'allow-scripts' was specified. 304 if ((targetDoc && !targetDoc->IsScriptEnabled()) || 305 (loadInfo->GetTriggeringSandboxFlags() & SANDBOXED_SCRIPTS)) { 306 if (nsCOMPtr<nsIObserverService> obs = 307 mozilla::services::GetObserverService()) { 308 obs->NotifyWhenScriptSafe(ToSupports(innerWin), 309 "javascript-uri-blocked-by-sandbox"); 310 } 311 return NS_ERROR_DOM_RETVAL_UNDEFINED; 312 } 313 314 if (targetDoc) { 315 // Perform a Security check against the CSP of the document we are 316 // running against. javascript: URIs are disabled unless "inline" 317 // scripts are allowed. We only do that if targetDoc->NodePrincipal() 318 // subsumes loadInfo->TriggeringPrincipal(). If it doesn't, then 319 // someone more-privileged (our UI or an extension) started the 320 // load and hence the load should not be subject to the target 321 // document's CSP. 322 // 323 // The "more privileged" assumption is safe, because if the triggering 324 // principal does not subsume targetDoc->NodePrincipal() we won't run the 325 // script at all. More precisely, we test that "principal" subsumes the 326 // target's principal, but "principal" should never be higher-privilege 327 // than the triggering principal here: it's either the triggering 328 // principal, or the principal of the document we started the load 329 // against if the triggering principal is system. 330 if (targetDoc->NodePrincipal()->Subsumes(loadInfo->TriggeringPrincipal())) { 331 nsCOMPtr<nsIContentSecurityPolicy> targetCSP = 332 PolicyContainer::GetCSP(targetDoc->GetPolicyContainer()); 333 if (!AllowedByCSP(targetCSP, mURL, aJSCallingLocation)) { 334 return NS_ERROR_DOM_RETVAL_UNDEFINED; 335 } 336 } 337 } 338 339 // Push our popup control state 340 AutoPopupStatePusher popupStatePusher(aPopupState); 341 342 nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin); 343 344 // So far so good: get the script context from its owner. 345 nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext(); 346 if (!scriptContext) return NS_ERROR_FAILURE; 347 348 // New script entry point required, due to the "Create a script" step of 349 // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol 350 mozilla::nsAutoMicroTask mt; 351 AutoEntryScript aes(innerGlobal, "javascript: URI", true); 352 JSContext* cx = aes.cx(); 353 JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject()); 354 NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED); 355 356 //-- Don't execute unless the script principal subsumes the 357 // principal of the context. 358 nsIPrincipal* objectPrincipal = 359 nsContentUtils::ObjectPrincipal(globalJSObject); 360 361 bool subsumes; 362 rv = principal->Subsumes(objectPrincipal, &subsumes); 363 if (NS_FAILED(rv)) return rv; 364 365 if (!subsumes) { 366 return NS_ERROR_DOM_RETVAL_UNDEFINED; 367 } 368 369 // Fail if someone tries to execute in a global with system principal. 370 if (objectPrincipal->IsSystemPrincipal()) { 371 return NS_ERROR_DOM_SECURITY_ERR; 372 } 373 374 nsAutoCString script(mScript); 375 // Unescape the script 376 NS_UnescapeURL(script); 377 378 JS::Rooted<JS::Value> v(cx, JS::UndefinedValue()); 379 // Finally, we have everything needed to evaluate the expression. 380 JS::CompileOptions options(cx); 381 options.setFileAndLine(mURL.get(), 1); 382 options.setIntroductionType("javascriptURL"); 383 { 384 mozilla::ErrorResult erv; 385 if (MOZ_LIKELY(xpc::Scriptability::Get(globalJSObject).Allowed())) { 386 mozilla::AutoProfilerLabel autoProfilerLabel( 387 "JSExecutionContext", 388 /* dynamicStr */ nullptr, JS::ProfilingCategoryPair::JS); 389 JSAutoRealm autoRealm(cx, globalJSObject); 390 RefPtr<JS::Stencil> stencil; 391 JS::Rooted<JSScript*> compiledScript(cx); 392 mozilla::dom::Compile(cx, options, NS_ConvertUTF8toUTF16(script), stencil, 393 erv); 394 if (stencil) { 395 JS::InstantiateOptions instantiateOptions(options); 396 MOZ_ASSERT(!instantiateOptions.deferDebugMetadata); 397 compiledScript.set(JS::InstantiateGlobalStencil( 398 aes.cx(), instantiateOptions, stencil, /* storage */ nullptr)); 399 if (!compiledScript) { 400 erv.NoteJSContextException(aes.cx()); 401 } 402 } 403 404 if (!erv.Failed()) { 405 MOZ_ASSERT(!options.noScriptRval); 406 ExecScriptAndGetString(cx, compiledScript, &v, erv); 407 } 408 } 409 rv = mozilla::dom::EvaluationExceptionToNSResult(erv); 410 } 411 412 js::AssertSameCompartment(cx, v); 413 MOZ_ASSERT(v.isString() || v.isUndefined()); 414 415 if (NS_FAILED(rv)) { 416 return NS_ERROR_MALFORMED_URI; 417 } 418 if (v.isUndefined()) { 419 return NS_ERROR_DOM_RETVAL_UNDEFINED; 420 } 421 MOZ_ASSERT(rv != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, 422 "How did we get a non-undefined return value?"); 423 nsAutoJSString result; 424 if (!result.init(cx, v)) { 425 return NS_ERROR_OUT_OF_MEMORY; 426 } 427 428 char* bytes; 429 uint32_t bytesLen; 430 constexpr auto isoCharset = "windows-1252"_ns; 431 constexpr auto utf8Charset = "UTF-8"_ns; 432 const nsLiteralCString* charset; 433 if (IsISO88591(result)) { 434 // For compatibility, if the result is ISO-8859-1, we use 435 // windows-1252, so that people can compatibly create images 436 // using javascript: URLs. 437 bytes = ToNewCString(result, mozilla::fallible); 438 bytesLen = result.Length(); 439 charset = &isoCharset; 440 } else { 441 bytes = ToNewUTF8String(result, &bytesLen); 442 charset = &utf8Charset; 443 } 444 aChannel->SetContentCharset(*charset); 445 if (bytes) { 446 rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream), 447 mozilla::Span(bytes, bytesLen), 448 NS_ASSIGNMENT_ADOPT); 449 } else { 450 rv = NS_ERROR_OUT_OF_MEMORY; 451 } 452 453 return rv; 454 } 455 456 //////////////////////////////////////////////////////////////////////////////// 457 458 class nsJSChannel : public nsIChannel, 459 public nsIStreamListener, 460 public nsIScriptChannel, 461 public nsIPropertyBag2 { 462 public: 463 nsJSChannel(); 464 465 NS_DECL_ISUPPORTS 466 NS_DECL_NSIREQUEST 467 NS_DECL_NSICHANNEL 468 NS_DECL_NSIREQUESTOBSERVER 469 NS_DECL_NSISTREAMLISTENER 470 NS_DECL_NSISCRIPTCHANNEL 471 NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag) 472 NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag) 473 474 nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo); 475 476 // Actually evaluate the script. 477 void EvaluateScript(); 478 479 protected: 480 virtual ~nsJSChannel(); 481 482 nsresult StopAll(); 483 484 void NotifyListener(); 485 486 void CleanupStrongRefs(); 487 488 nsCOMPtr<nsIChannel> mStreamChannel; 489 nsCOMPtr<nsIPropertyBag2> mPropertyBag; 490 nsCOMPtr<nsIStreamListener> mListener; // Our final listener 491 nsCOMPtr<nsPIDOMWindowInner> mOriginalInnerWindow; // The inner window our 492 // load started against. 493 // If we blocked onload on a document in AsyncOpen, this is the document we 494 // did it on. 495 RefPtr<mozilla::dom::Document> mDocumentOnloadBlockedOn; 496 497 nsresult mStatus; // Our status 498 499 nsLoadFlags mLoadFlags; 500 nsLoadFlags mActualLoadFlags; // See AsyncOpen 501 502 RefPtr<JSURLInputStream> mJSURIStream; 503 mozilla::dom::PopupBlocker::PopupControlState mPopupState; 504 mozilla::JSCallingLocation mJSCallingLocation; 505 uint32_t mExecutionPolicy; 506 bool mIsAsync; 507 bool mIsActive; 508 bool mOpenedStreamChannel; 509 }; 510 511 nsJSChannel::nsJSChannel() 512 : mStatus(NS_OK), 513 mLoadFlags(LOAD_NORMAL), 514 mActualLoadFlags(LOAD_NORMAL), 515 mPopupState(mozilla::dom::PopupBlocker::openOverridden), 516 mExecutionPolicy(NO_EXECUTION), 517 mIsAsync(true), 518 mIsActive(false), 519 mOpenedStreamChannel(false) {} 520 521 nsJSChannel::~nsJSChannel() = default; 522 523 nsresult nsJSChannel::StopAll() { 524 nsresult rv = NS_ERROR_UNEXPECTED; 525 nsCOMPtr<nsIWebNavigation> webNav; 526 NS_QueryNotificationCallbacks(mStreamChannel, webNav); 527 528 NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!"); 529 if (webNav) { 530 rv = webNav->Stop(nsIWebNavigation::STOP_ALL); 531 } 532 533 return rv; 534 } 535 536 nsresult nsJSChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) { 537 RefPtr<nsJSURI> jsURI; 538 nsresult rv = aURI->QueryInterface(kJSURICID, getter_AddRefs(jsURI)); 539 NS_ENSURE_SUCCESS(rv, rv); 540 541 // Create the nsIStreamIO layer used by the nsIStreamIOChannel. 542 mJSURIStream = new JSURLInputStream(); 543 544 // Create a stock input stream channel... 545 // Remember, until AsyncOpen is called, the script will not be evaluated 546 // and the underlying Input Stream will not be created... 547 nsCOMPtr<nsIChannel> channel; 548 RefPtr<JSURLInputStream> jsURIStream = mJSURIStream; 549 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI, 550 jsURIStream.forget(), "text/html"_ns, 551 ""_ns, aLoadInfo); 552 NS_ENSURE_SUCCESS(rv, rv); 553 554 rv = mJSURIStream->Init(aURI); 555 if (NS_SUCCEEDED(rv)) { 556 mStreamChannel = channel; 557 mPropertyBag = do_QueryInterface(channel); 558 nsCOMPtr<nsIWritablePropertyBag2> writableBag = do_QueryInterface(channel); 559 if (writableBag && jsURI->GetBaseURI()) { 560 writableBag->SetPropertyAsInterface(u"baseURI"_ns, jsURI->GetBaseURI()); 561 } 562 } 563 564 return rv; 565 } 566 567 NS_IMETHODIMP 568 nsJSChannel::GetIsDocument(bool* aIsDocument) { 569 return NS_GetIsDocumentChannel(this, aIsDocument); 570 } 571 572 // 573 // nsISupports implementation... 574 // 575 576 NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver, 577 nsIStreamListener, nsIScriptChannel, nsIPropertyBag, 578 nsIPropertyBag2) 579 580 // 581 // nsIRequest implementation... 582 // 583 584 NS_IMETHODIMP 585 nsJSChannel::GetName(nsACString& aResult) { 586 return mStreamChannel->GetName(aResult); 587 } 588 589 NS_IMETHODIMP 590 nsJSChannel::IsPending(bool* aResult) { 591 *aResult = mIsActive; 592 return NS_OK; 593 } 594 595 NS_IMETHODIMP 596 nsJSChannel::GetStatus(nsresult* aResult) { 597 if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) { 598 return mStreamChannel->GetStatus(aResult); 599 } 600 601 *aResult = mStatus; 602 603 return NS_OK; 604 } 605 606 NS_IMETHODIMP nsJSChannel::SetCanceledReason(const nsACString& aReason) { 607 return SetCanceledReasonImpl(aReason); 608 } 609 610 NS_IMETHODIMP nsJSChannel::GetCanceledReason(nsACString& aReason) { 611 return GetCanceledReasonImpl(aReason); 612 } 613 614 NS_IMETHODIMP nsJSChannel::CancelWithReason(nsresult aStatus, 615 const nsACString& aReason) { 616 return CancelWithReasonImpl(aStatus, aReason); 617 } 618 619 NS_IMETHODIMP 620 nsJSChannel::Cancel(nsresult aStatus) { 621 mStatus = aStatus; 622 623 if (mOpenedStreamChannel) { 624 mStreamChannel->Cancel(aStatus); 625 } 626 627 return NS_OK; 628 } 629 630 NS_IMETHODIMP 631 nsJSChannel::GetCanceled(bool* aCanceled) { 632 nsresult status = NS_ERROR_FAILURE; 633 GetStatus(&status); 634 *aCanceled = NS_FAILED(status); 635 return NS_OK; 636 } 637 638 NS_IMETHODIMP 639 nsJSChannel::Suspend() { return mStreamChannel->Suspend(); } 640 641 NS_IMETHODIMP 642 nsJSChannel::Resume() { return mStreamChannel->Resume(); } 643 644 // 645 // nsIChannel implementation 646 // 647 648 NS_IMETHODIMP 649 nsJSChannel::GetOriginalURI(nsIURI** aURI) { 650 return mStreamChannel->GetOriginalURI(aURI); 651 } 652 653 NS_IMETHODIMP 654 nsJSChannel::SetOriginalURI(nsIURI* aURI) { 655 return mStreamChannel->SetOriginalURI(aURI); 656 } 657 658 NS_IMETHODIMP 659 nsJSChannel::GetURI(nsIURI** aURI) { return mStreamChannel->GetURI(aURI); } 660 661 NS_IMETHODIMP 662 nsJSChannel::Open(nsIInputStream** aStream) { 663 nsCOMPtr<nsIStreamListener> listener; 664 nsresult rv = 665 nsContentSecurityManager::doContentSecurityCheck(this, listener); 666 NS_ENSURE_SUCCESS(rv, rv); 667 668 mJSCallingLocation = mozilla::JSCallingLocation::Get(); 669 rv = mJSURIStream->EvaluateScript(mStreamChannel, mPopupState, 670 mExecutionPolicy, mOriginalInnerWindow, 671 mJSCallingLocation); 672 NS_ENSURE_SUCCESS(rv, rv); 673 674 return mStreamChannel->Open(aStream); 675 } 676 677 NS_IMETHODIMP 678 nsJSChannel::AsyncOpen(nsIStreamListener* aListener) { 679 NS_ENSURE_ARG(aListener); 680 681 nsCOMPtr<nsIStreamListener> listener = aListener; 682 nsresult rv = 683 nsContentSecurityManager::doContentSecurityCheck(this, listener); 684 NS_ENSURE_SUCCESS(rv, rv); 685 686 #ifdef DEBUG 687 { 688 nsCOMPtr<nsILoadInfo> loadInfo = nsIChannel::LoadInfo(); 689 MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 || 690 loadInfo->GetInitialSecurityCheckDone(), 691 "security flags in loadInfo but asyncOpen() not called"); 692 } 693 #endif 694 695 // First make sure that we have a usable inner window; we'll want to make 696 // sure that we execute against that inner and no other. 697 nsIScriptGlobalObject* global = GetGlobalObject(this); 698 if (!global) { 699 return NS_ERROR_NOT_AVAILABLE; 700 } 701 702 nsCOMPtr<nsPIDOMWindowOuter> win(do_QueryInterface(global)); 703 NS_ASSERTION(win, "Our global is not a window??"); 704 705 // Make sure we create a new inner window if one doesn't already exist (see 706 // bug 306630). 707 mOriginalInnerWindow = win->EnsureInnerWindow(); 708 if (!mOriginalInnerWindow) { 709 return NS_ERROR_NOT_AVAILABLE; 710 } 711 712 mListener = aListener; 713 714 mIsActive = true; 715 716 // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer 717 // notifications (and hence nsIWebProgressListener notifications) from 718 // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI, 719 // which means that the DocLoader would not generate document start and 720 // stop notifications (see bug 257875). 721 mActualLoadFlags = mLoadFlags; 722 mLoadFlags |= LOAD_BACKGROUND; 723 724 // Add the javascript channel to its loadgroup so that we know if 725 // network loads were canceled or not... 726 nsCOMPtr<nsILoadGroup> loadGroup; 727 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 728 if (loadGroup) { 729 rv = loadGroup->AddRequest(this, nullptr); 730 if (NS_FAILED(rv)) { 731 mIsActive = false; 732 CleanupStrongRefs(); 733 return rv; 734 } 735 } 736 737 mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc(); 738 if (mDocumentOnloadBlockedOn) { 739 // If we're a document channel, we need to actually block onload on our 740 // _parent_ document. This is because we don't actually set our 741 // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the 742 // document channel will claim to not be busy, and our parent's onload 743 // could fire too early. 744 nsLoadFlags loadFlags; 745 mStreamChannel->GetLoadFlags(&loadFlags); 746 if (loadFlags & LOAD_DOCUMENT_URI) { 747 mDocumentOnloadBlockedOn = 748 mDocumentOnloadBlockedOn->GetInProcessParentDocument(); 749 } 750 } 751 if (mDocumentOnloadBlockedOn) { 752 mDocumentOnloadBlockedOn->BlockOnload(); 753 } 754 755 mPopupState = mozilla::dom::PopupBlocker::GetPopupControlState(); 756 757 void (nsJSChannel::*method)(); 758 const char* name; 759 760 // The calling location is unavailable in the runnable, hence getting it here. 761 mJSCallingLocation = mozilla::JSCallingLocation::Get(); 762 763 if (mIsAsync) { 764 // post an event to do the rest 765 method = &nsJSChannel::EvaluateScript; 766 name = "nsJSChannel::EvaluateScript"; 767 } else { 768 EvaluateScript(); 769 if (mOpenedStreamChannel) { 770 // That will handle notifying things 771 return NS_OK; 772 } 773 774 NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_"); 775 776 // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't 777 // have any content resulting from the execution and NS_BINDING_ABORTED 778 // if something we did causes our own load to be stopped. Return 779 // success in those cases, and error out in all others. 780 if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED && 781 mStatus != NS_BINDING_ABORTED) { 782 // Note that calling EvaluateScript() handled removing us from the 783 // loadgroup and marking us as not active anymore. 784 CleanupStrongRefs(); 785 return mStatus; 786 } 787 788 // We're returning success from asyncOpen(), but we didn't open a 789 // stream channel. We'll have to notify ourselves, but make sure to do 790 // it asynchronously. 791 method = &nsJSChannel::NotifyListener; 792 name = "nsJSChannel::NotifyListener"; 793 } 794 795 nsCOMPtr<nsIRunnable> runnable = 796 mozilla::NewRunnableMethod(name, this, method); 797 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(mOriginalInnerWindow); 798 rv = window->Dispatch(runnable.forget()); 799 800 if (NS_FAILED(rv)) { 801 loadGroup->RemoveRequest(this, nullptr, rv); 802 mIsActive = false; 803 CleanupStrongRefs(); 804 } 805 return rv; 806 } 807 808 void nsJSChannel::EvaluateScript() { 809 // Synchronously execute the script... 810 // mIsActive is used to indicate the the request is 'busy' during the 811 // the script evaluation phase. This means that IsPending() will 812 // indicate the the request is busy while the script is executing... 813 814 // Note that we want to be in the loadgroup and pending while we evaluate 815 // the script, so that we find out if the loadgroup gets canceled by the 816 // script execution (in which case we shouldn't pump out data even if the 817 // script returns it). 818 819 if (NS_SUCCEEDED(mStatus)) { 820 nsresult rv = mJSURIStream->EvaluateScript( 821 mStreamChannel, mPopupState, mExecutionPolicy, mOriginalInnerWindow, 822 mJSCallingLocation); 823 824 // Note that evaluation may have canceled us, so recheck mStatus again 825 if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) { 826 mStatus = rv; 827 } 828 } 829 830 // Remove the javascript channel from its loadgroup... 831 nsCOMPtr<nsILoadGroup> loadGroup; 832 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 833 if (loadGroup) { 834 loadGroup->RemoveRequest(this, nullptr, mStatus); 835 } 836 837 // Reset load flags to their original value... 838 mLoadFlags = mActualLoadFlags; 839 840 // We're no longer active, it's now up to the stream channel to do 841 // the loading, if needed. 842 mIsActive = false; 843 844 if (NS_FAILED(mStatus)) { 845 if (mIsAsync) { 846 NotifyListener(); 847 } 848 return; 849 } 850 851 // EvaluateScript() succeeded, and we were not canceled, that 852 // means there's data to parse as a result of evaluating the 853 // script. 854 855 // Get the stream channels load flags (!= mLoadFlags). 856 nsLoadFlags loadFlags; 857 mStreamChannel->GetLoadFlags(&loadFlags); 858 859 uint32_t disposition; 860 if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition))) 861 disposition = nsIChannel::DISPOSITION_INLINE; 862 if (loadFlags & LOAD_DOCUMENT_URI && 863 disposition != nsIChannel::DISPOSITION_ATTACHMENT) { 864 // We're loaded as the document channel and not expecting to download 865 // the result. If we go on, we'll blow away the current document. Make 866 // sure that's ok. If so, stop all pending network loads. 867 868 nsCOMPtr<nsIDocShell> docShell; 869 NS_QueryNotificationCallbacks(mStreamChannel, docShell); 870 if (docShell) { 871 nsCOMPtr<nsIDocumentViewer> viewer; 872 docShell->GetDocViewer(getter_AddRefs(viewer)); 873 874 if (viewer) { 875 bool okToUnload; 876 877 if (NS_SUCCEEDED(viewer->PermitUnload(&okToUnload)) && !okToUnload) { 878 // The user didn't want to unload the current 879 // page, translate this into an undefined 880 // return from the javascript: URL... 881 mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED; 882 } 883 // Note: `docShell` may have been destroyed in `PermitUnload`, so don't 884 // add uses of `docShell` later in this method! 885 } 886 } 887 888 if (NS_SUCCEEDED(mStatus)) { 889 mStatus = StopAll(); 890 } 891 } 892 893 if (NS_FAILED(mStatus)) { 894 if (mIsAsync) { 895 NotifyListener(); 896 } 897 return; 898 } 899 900 mStatus = mStreamChannel->AsyncOpen(this); 901 if (NS_SUCCEEDED(mStatus)) { 902 // mStreamChannel will call OnStartRequest and OnStopRequest on 903 // us, so we'll be sure to call them on our listener. 904 mOpenedStreamChannel = true; 905 906 // Now readd ourselves to the loadgroup so we can receive 907 // cancellation notifications. 908 mIsActive = true; 909 if (loadGroup) { 910 mStatus = loadGroup->AddRequest(this, nullptr); 911 912 // If AddRequest failed, that's OK. The key is to make sure we get 913 // cancelled if needed, and that call just canceled us if it 914 // failed. We'll still get notified by the stream channel when it 915 // finishes. 916 } 917 918 } else if (mIsAsync) { 919 NotifyListener(); 920 } 921 } 922 923 void nsJSChannel::NotifyListener() { 924 mListener->OnStartRequest(this); 925 mListener->OnStopRequest(this, mStatus); 926 927 CleanupStrongRefs(); 928 } 929 930 void nsJSChannel::CleanupStrongRefs() { 931 mListener = nullptr; 932 mOriginalInnerWindow = nullptr; 933 if (mDocumentOnloadBlockedOn) { 934 mDocumentOnloadBlockedOn->UnblockOnload(false); 935 mDocumentOnloadBlockedOn = nullptr; 936 } 937 } 938 939 NS_IMETHODIMP 940 nsJSChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 941 *aLoadFlags = mLoadFlags; 942 943 return NS_OK; 944 } 945 946 NS_IMETHODIMP 947 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 948 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is 949 // actually right. 950 bool bogusLoadBackground = false; 951 if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) && 952 (aLoadFlags & LOAD_BACKGROUND)) { 953 // We're getting a LOAD_BACKGROUND, but it's probably just our own fake 954 // flag being mirrored to us. The one exception is if our loadgroup is 955 // LOAD_BACKGROUND. 956 bool loadGroupIsBackground = false; 957 nsCOMPtr<nsILoadGroup> loadGroup; 958 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 959 if (loadGroup) { 960 nsLoadFlags loadGroupFlags; 961 loadGroup->GetLoadFlags(&loadGroupFlags); 962 loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0); 963 } 964 bogusLoadBackground = !loadGroupIsBackground; 965 } 966 967 // Since the javascript channel is never the actual channel that 968 // any data is loaded through, don't ever set the 969 // LOAD_DOCUMENT_URI flag on it, since that could lead to two 970 // 'document channels' in the loadgroup if a javascript: URL is 971 // loaded while a document is being loaded in the same window. 972 973 // XXXbz this, and a whole lot of other hackery, could go away if we'd just 974 // cancel the current document load on javascript: load start like IE does. 975 976 mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI; 977 978 if (bogusLoadBackground) { 979 aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND; 980 } 981 982 mActualLoadFlags = aLoadFlags; 983 984 // ... but the underlying stream channel should get this bit, if 985 // set, since that'll be the real document channel if the 986 // javascript: URL generated data. 987 988 return mStreamChannel->SetLoadFlags(aLoadFlags); 989 } 990 991 NS_IMETHODIMP 992 nsJSChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 993 return GetTRRModeImpl(aTRRMode); 994 } 995 996 NS_IMETHODIMP 997 nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 998 return SetTRRModeImpl(aTRRMode); 999 } 1000 1001 NS_IMETHODIMP 1002 nsJSChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 1003 return mStreamChannel->GetLoadGroup(aLoadGroup); 1004 } 1005 1006 NS_IMETHODIMP 1007 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 1008 if (aLoadGroup) { 1009 bool streamPending; 1010 nsresult rv = mStreamChannel->IsPending(&streamPending); 1011 NS_ENSURE_SUCCESS(rv, rv); 1012 1013 if (streamPending) { 1014 nsCOMPtr<nsILoadGroup> curLoadGroup; 1015 mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup)); 1016 1017 if (aLoadGroup != curLoadGroup) { 1018 // Move the stream channel to our new loadgroup. Make sure to 1019 // add it before removing it, so that we don't trigger onload 1020 // by accident. 1021 aLoadGroup->AddRequest(mStreamChannel, nullptr); 1022 if (curLoadGroup) { 1023 curLoadGroup->RemoveRequest(mStreamChannel, nullptr, 1024 NS_BINDING_RETARGETED); 1025 } 1026 } 1027 } 1028 } 1029 1030 return mStreamChannel->SetLoadGroup(aLoadGroup); 1031 } 1032 1033 NS_IMETHODIMP 1034 nsJSChannel::GetOwner(nsISupports** aOwner) { 1035 return mStreamChannel->GetOwner(aOwner); 1036 } 1037 1038 NS_IMETHODIMP 1039 nsJSChannel::SetOwner(nsISupports* aOwner) { 1040 return mStreamChannel->SetOwner(aOwner); 1041 } 1042 1043 NS_IMETHODIMP 1044 nsJSChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { 1045 return mStreamChannel->GetLoadInfo(aLoadInfo); 1046 } 1047 1048 NS_IMETHODIMP 1049 nsJSChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) { 1050 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null"); 1051 return mStreamChannel->SetLoadInfo(aLoadInfo); 1052 } 1053 1054 NS_IMETHODIMP 1055 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 1056 return mStreamChannel->GetNotificationCallbacks(aCallbacks); 1057 } 1058 1059 NS_IMETHODIMP 1060 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 1061 return mStreamChannel->SetNotificationCallbacks(aCallbacks); 1062 } 1063 1064 NS_IMETHODIMP 1065 nsJSChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) { 1066 return mStreamChannel->GetSecurityInfo(aSecurityInfo); 1067 } 1068 1069 NS_IMETHODIMP 1070 nsJSChannel::GetContentType(nsACString& aContentType) { 1071 return mStreamChannel->GetContentType(aContentType); 1072 } 1073 1074 NS_IMETHODIMP 1075 nsJSChannel::SetContentType(const nsACString& aContentType) { 1076 return mStreamChannel->SetContentType(aContentType); 1077 } 1078 1079 NS_IMETHODIMP 1080 nsJSChannel::GetContentCharset(nsACString& aContentCharset) { 1081 return mStreamChannel->GetContentCharset(aContentCharset); 1082 } 1083 1084 NS_IMETHODIMP 1085 nsJSChannel::SetContentCharset(const nsACString& aContentCharset) { 1086 return mStreamChannel->SetContentCharset(aContentCharset); 1087 } 1088 1089 NS_IMETHODIMP 1090 nsJSChannel::GetContentDisposition(uint32_t* aContentDisposition) { 1091 return mStreamChannel->GetContentDisposition(aContentDisposition); 1092 } 1093 1094 NS_IMETHODIMP 1095 nsJSChannel::SetContentDisposition(uint32_t aContentDisposition) { 1096 return mStreamChannel->SetContentDisposition(aContentDisposition); 1097 } 1098 1099 NS_IMETHODIMP 1100 nsJSChannel::GetContentDispositionFilename( 1101 nsAString& aContentDispositionFilename) { 1102 return mStreamChannel->GetContentDispositionFilename( 1103 aContentDispositionFilename); 1104 } 1105 1106 NS_IMETHODIMP 1107 nsJSChannel::SetContentDispositionFilename( 1108 const nsAString& aContentDispositionFilename) { 1109 return mStreamChannel->SetContentDispositionFilename( 1110 aContentDispositionFilename); 1111 } 1112 1113 NS_IMETHODIMP 1114 nsJSChannel::GetContentDispositionHeader( 1115 nsACString& aContentDispositionHeader) { 1116 return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader); 1117 } 1118 1119 NS_IMETHODIMP 1120 nsJSChannel::GetContentLength(int64_t* aContentLength) { 1121 return mStreamChannel->GetContentLength(aContentLength); 1122 } 1123 1124 NS_IMETHODIMP 1125 nsJSChannel::SetContentLength(int64_t aContentLength) { 1126 return mStreamChannel->SetContentLength(aContentLength); 1127 } 1128 1129 NS_IMETHODIMP 1130 nsJSChannel::OnStartRequest(nsIRequest* aRequest) { 1131 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); 1132 1133 return mListener->OnStartRequest(this); 1134 } 1135 1136 NS_IMETHODIMP 1137 nsJSChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, 1138 uint64_t aOffset, uint32_t aCount) { 1139 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); 1140 1141 return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount); 1142 } 1143 1144 NS_IMETHODIMP 1145 nsJSChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { 1146 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); 1147 1148 nsCOMPtr<nsIStreamListener> listener = mListener; 1149 1150 CleanupStrongRefs(); 1151 1152 // Make sure aStatus matches what GetStatus() returns 1153 if (NS_FAILED(mStatus)) { 1154 aStatus = mStatus; 1155 } 1156 1157 nsresult rv = listener->OnStopRequest(this, aStatus); 1158 1159 nsCOMPtr<nsILoadGroup> loadGroup; 1160 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1161 if (loadGroup) { 1162 loadGroup->RemoveRequest(this, nullptr, mStatus); 1163 } 1164 1165 mIsActive = false; 1166 1167 return rv; 1168 } 1169 1170 NS_IMETHODIMP 1171 nsJSChannel::SetExecutionPolicy(uint32_t aPolicy) { 1172 NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL); 1173 1174 mExecutionPolicy = aPolicy; 1175 return NS_OK; 1176 } 1177 1178 NS_IMETHODIMP 1179 nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy) { 1180 *aPolicy = mExecutionPolicy; 1181 return NS_OK; 1182 } 1183 1184 NS_IMETHODIMP 1185 nsJSChannel::SetExecuteAsync(bool aIsAsync) { 1186 if (!mIsActive) { 1187 mIsAsync = aIsAsync; 1188 } 1189 // else ignore this call 1190 NS_WARNING_ASSERTION(!mIsActive, 1191 "Calling SetExecuteAsync on active channel?"); 1192 1193 return NS_OK; 1194 } 1195 1196 NS_IMETHODIMP 1197 nsJSChannel::GetExecuteAsync(bool* aIsAsync) { 1198 *aIsAsync = mIsAsync; 1199 return NS_OK; 1200 } 1201 1202 bool nsJSChannel::GetIsDocumentLoad() { 1203 // Our LOAD_DOCUMENT_URI flag, if any, lives on our stream channel. 1204 nsLoadFlags flags; 1205 mStreamChannel->GetLoadFlags(&flags); 1206 return flags & LOAD_DOCUMENT_URI; 1207 } 1208 1209 //////////////////////////////////////////////////////////////////////////////// 1210 1211 nsJSProtocolHandler::nsJSProtocolHandler() = default; 1212 1213 nsJSProtocolHandler::~nsJSProtocolHandler() = default; 1214 1215 NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler) 1216 1217 /* static */ nsresult nsJSProtocolHandler::EnsureUTF8Spec( 1218 const nsCString& aSpec, const char* aCharset, nsACString& aUTF8Spec) { 1219 aUTF8Spec.Truncate(); 1220 1221 nsAutoString uStr; 1222 nsresult rv = nsTextToSubURI::UnEscapeNonAsciiURI( 1223 nsDependentCString(aCharset), aSpec, uStr); 1224 NS_ENSURE_SUCCESS(rv, rv); 1225 1226 if (!IsAscii(uStr)) { 1227 rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr), 1228 esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec, 1229 mozilla::fallible); 1230 NS_ENSURE_SUCCESS(rv, rv); 1231 } 1232 1233 return NS_OK; 1234 } 1235 1236 //////////////////////////////////////////////////////////////////////////////// 1237 // nsIProtocolHandler methods: 1238 1239 NS_IMETHODIMP 1240 nsJSProtocolHandler::GetScheme(nsACString& result) { 1241 result = "javascript"; 1242 return NS_OK; 1243 } 1244 1245 /* static */ nsresult nsJSProtocolHandler::CreateNewURI(const nsACString& aSpec, 1246 const char* aCharset, 1247 nsIURI* aBaseURI, 1248 nsIURI** result) { 1249 nsresult rv = NS_OK; 1250 1251 // javascript: URLs (currently) have no additional structure beyond that 1252 // provided by standard URLs, so there is no "outer" object given to 1253 // CreateInstance. 1254 1255 NS_MutateURI mutator(new nsJSURI::Mutator()); 1256 nsCOMPtr<nsIURI> base(aBaseURI); 1257 mutator.Apply(&nsIJSURIMutator::SetBase, base); 1258 if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) { 1259 mutator.SetSpec(aSpec); 1260 } else { 1261 nsAutoCString utf8Spec; 1262 rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec); 1263 if (NS_FAILED(rv)) { 1264 return rv; 1265 } 1266 if (utf8Spec.IsEmpty()) { 1267 mutator.SetSpec(aSpec); 1268 } else { 1269 mutator.SetSpec(utf8Spec); 1270 } 1271 } 1272 1273 nsCOMPtr<nsIURI> url; 1274 rv = mutator.Finalize(url); 1275 if (NS_FAILED(rv)) { 1276 return rv; 1277 } 1278 1279 // use DefaultURI to check for validity when we have possible hostnames 1280 // since nsSimpleURI doesn't know about hostnames 1281 auto pos = aSpec.Find("javascript:"); 1282 if (pos != kNotFound) { 1283 nsDependentCSubstring rest(aSpec, pos + sizeof("javascript:") - 1, -1); 1284 if (StringBeginsWith(rest, "//"_ns)) { 1285 nsCOMPtr<nsIURI> uriWithHost; 1286 rv = NS_MutateURI(new mozilla::net::DefaultURI::Mutator()) 1287 .SetSpec(aSpec) 1288 .Finalize(uriWithHost); 1289 NS_ENSURE_SUCCESS(rv, rv); 1290 } 1291 } 1292 1293 url.forget(result); 1294 return rv; 1295 } 1296 1297 NS_IMETHODIMP 1298 nsJSProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, 1299 nsIChannel** result) { 1300 nsresult rv; 1301 1302 NS_ENSURE_ARG_POINTER(uri); 1303 RefPtr<nsJSChannel> channel = new nsJSChannel(); 1304 if (!channel) { 1305 return NS_ERROR_OUT_OF_MEMORY; 1306 } 1307 1308 rv = channel->Init(uri, aLoadInfo); 1309 NS_ENSURE_SUCCESS(rv, rv); 1310 1311 if (NS_SUCCEEDED(rv)) { 1312 channel.forget(result); 1313 } 1314 return rv; 1315 } 1316 1317 NS_IMETHODIMP 1318 nsJSProtocolHandler::AllowPort(int32_t port, const char* scheme, 1319 bool* _retval) { 1320 // don't override anything. 1321 *_retval = false; 1322 return NS_OK; 1323 } 1324 1325 //////////////////////////////////////////////////////////// 1326 // nsJSURI implementation 1327 static NS_DEFINE_CID(kThisSimpleURIImplementationCID, 1328 NS_THIS_SIMPLEURI_IMPLEMENTATION_CID); 1329 1330 NS_IMPL_ADDREF_INHERITED(nsJSURI, mozilla::net::nsSimpleURI) 1331 NS_IMPL_RELEASE_INHERITED(nsJSURI, mozilla::net::nsSimpleURI) 1332 1333 NS_IMPL_CLASSINFO(nsJSURI, nullptr, nsIClassInfo::THREADSAFE, NS_JSURI_CID); 1334 // Empty CI getter. We only need nsIClassInfo for Serialization 1335 NS_IMPL_CI_INTERFACE_GETTER0(nsJSURI) 1336 1337 NS_INTERFACE_MAP_BEGIN(nsJSURI) 1338 if (aIID.Equals(kJSURICID)) 1339 foundInterface = static_cast<nsIURI*>(this); 1340 else if (aIID.Equals(kThisSimpleURIImplementationCID)) { 1341 // Need to return explicitly here, because if we just set foundInterface 1342 // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into 1343 // nsSimplURI::QueryInterface and finding something for this CID. 1344 *aInstancePtr = nullptr; 1345 return NS_NOINTERFACE; 1346 } else 1347 NS_IMPL_QUERY_CLASSINFO(nsJSURI) 1348 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI) 1349 1350 // nsISerializable methods: 1351 1352 NS_IMETHODIMP 1353 nsJSURI::Read(nsIObjectInputStream* aStream) { 1354 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); 1355 return NS_ERROR_NOT_IMPLEMENTED; 1356 } 1357 1358 nsresult nsJSURI::ReadPrivate(nsIObjectInputStream* aStream) { 1359 nsresult rv = mozilla::net::nsSimpleURI::ReadPrivate(aStream); 1360 if (NS_FAILED(rv)) return rv; 1361 1362 bool haveBase; 1363 rv = aStream->ReadBoolean(&haveBase); 1364 if (NS_FAILED(rv)) return rv; 1365 1366 if (haveBase) { 1367 nsCOMPtr<nsISupports> supports; 1368 rv = aStream->ReadObject(true, getter_AddRefs(supports)); 1369 if (NS_FAILED(rv)) return rv; 1370 mBaseURI = do_QueryInterface(supports); 1371 } 1372 1373 return NS_OK; 1374 } 1375 1376 NS_IMETHODIMP 1377 nsJSURI::Write(nsIObjectOutputStream* aStream) { 1378 nsresult rv = mozilla::net::nsSimpleURI::Write(aStream); 1379 if (NS_FAILED(rv)) return rv; 1380 1381 rv = aStream->WriteBoolean(mBaseURI != nullptr); 1382 if (NS_FAILED(rv)) return rv; 1383 1384 if (mBaseURI) { 1385 rv = aStream->WriteObject(mBaseURI, true); 1386 if (NS_FAILED(rv)) return rv; 1387 } 1388 1389 return NS_OK; 1390 } 1391 1392 NS_IMETHODIMP_(void) nsJSURI::Serialize(mozilla::ipc::URIParams& aParams) { 1393 using namespace mozilla::ipc; 1394 1395 JSURIParams jsParams; 1396 URIParams simpleParams; 1397 1398 mozilla::net::nsSimpleURI::Serialize(simpleParams); 1399 1400 jsParams.simpleParams() = simpleParams; 1401 if (mBaseURI) { 1402 SerializeURI(mBaseURI, jsParams.baseURI()); 1403 } else { 1404 jsParams.baseURI() = mozilla::Nothing(); 1405 } 1406 1407 aParams = jsParams; 1408 } 1409 1410 bool nsJSURI::Deserialize(const mozilla::ipc::URIParams& aParams) { 1411 using namespace mozilla::ipc; 1412 1413 if (aParams.type() != URIParams::TJSURIParams) { 1414 NS_ERROR("Received unknown parameters from the other process!"); 1415 return false; 1416 } 1417 1418 const JSURIParams& jsParams = aParams.get_JSURIParams(); 1419 mozilla::net::nsSimpleURI::Deserialize(jsParams.simpleParams()); 1420 1421 if (jsParams.baseURI().isSome()) { 1422 mBaseURI = DeserializeURI(jsParams.baseURI().ref()); 1423 } else { 1424 mBaseURI = nullptr; 1425 } 1426 return true; 1427 } 1428 1429 // nsSimpleURI methods: 1430 /* virtual */ already_AddRefed<mozilla::net::nsSimpleURI> 1431 nsJSURI::StartClone() { 1432 RefPtr<nsJSURI> url = new nsJSURI(mBaseURI); 1433 return url.forget(); 1434 } 1435 1436 // Queries this list of interfaces. If none match, it queries mURI. 1437 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJSURI::Mutator, nsIURISetters, nsIURIMutator, 1438 nsISerializable, nsIJSURIMutator) 1439 1440 NS_IMETHODIMP 1441 nsJSURI::Mutate(nsIURIMutator** aMutator) { 1442 RefPtr<nsJSURI::Mutator> mutator = new nsJSURI::Mutator(); 1443 nsresult rv = mutator->InitFromURI(this); 1444 if (NS_FAILED(rv)) { 1445 return rv; 1446 } 1447 mutator.forget(aMutator); 1448 return NS_OK; 1449 } 1450 1451 /* virtual */ 1452 nsresult nsJSURI::EqualsInternal( 1453 nsIURI* aOther, mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode, 1454 bool* aResult) { 1455 NS_ENSURE_ARG_POINTER(aOther); 1456 MOZ_ASSERT(aResult, "null pointer for outparam"); 1457 1458 RefPtr<nsJSURI> otherJSURI; 1459 nsresult rv = aOther->QueryInterface(kJSURICID, getter_AddRefs(otherJSURI)); 1460 if (NS_FAILED(rv)) { 1461 *aResult = false; // aOther is not a nsJSURI --> not equal. 1462 return NS_OK; 1463 } 1464 1465 // Compare the member data that our base class knows about. 1466 if (!mozilla::net::nsSimpleURI::EqualsInternal(otherJSURI, 1467 aRefHandlingMode)) { 1468 *aResult = false; 1469 return NS_OK; 1470 } 1471 1472 // Compare the piece of additional member data that we add to base class. 1473 nsIURI* otherBaseURI = otherJSURI->GetBaseURI(); 1474 1475 if (mBaseURI) { 1476 // (As noted in StartClone, we always honor refs on mBaseURI) 1477 return mBaseURI->Equals(otherBaseURI, aResult); 1478 } 1479 1480 *aResult = !otherBaseURI; 1481 return NS_OK; 1482 }