tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }