Backed out 3 changesets (bug 1879975) for causing bustage while fetching windows...
[gecko.git] / dom / jsurl / nsJSProtocolHandler.cpp
blob8dd0bc4ff5c5efd414768e3c67bba69692cbb33f
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/. */
7 #include "nsCOMPtr.h"
8 #include "jsapi.h"
9 #include "js/Wrapper.h"
10 #include "nsCRT.h"
11 #include "nsError.h"
12 #include "nsString.h"
13 #include "nsGlobalWindowInner.h"
14 #include "nsReadableUtils.h"
15 #include "nsJSProtocolHandler.h"
16 #include "nsStringStream.h"
17 #include "nsNetUtil.h"
19 #include "nsIClassInfoImpl.h"
20 #include "nsIStreamListener.h"
21 #include "nsIURI.h"
22 #include "nsIScriptContext.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsIPrincipal.h"
25 #include "nsIInterfaceRequestor.h"
26 #include "nsIInterfaceRequestorUtils.h"
27 #include "nsPIDOMWindow.h"
28 #include "nsEscape.h"
29 #include "nsIWebNavigation.h"
30 #include "nsIDocShell.h"
31 #include "nsIDocumentViewer.h"
32 #include "nsContentUtils.h"
33 #include "nsJSUtils.h"
34 #include "nsThreadUtils.h"
35 #include "nsIScriptChannel.h"
36 #include "mozilla/dom/Document.h"
37 #include "nsIObjectInputStream.h"
38 #include "nsIObjectOutputStream.h"
39 #include "nsIWritablePropertyBag2.h"
40 #include "nsIContentSecurityPolicy.h"
41 #include "nsSandboxFlags.h"
42 #include "nsTextToSubURI.h"
43 #include "mozilla/BasePrincipal.h"
44 #include "mozilla/CycleCollectedJSContext.h"
45 #include "mozilla/dom/AutoEntryScript.h"
46 #include "mozilla/dom/DOMSecurityMonitor.h"
47 #include "mozilla/dom/JSExecutionContext.h"
48 #include "mozilla/dom/ScriptSettings.h"
49 #include "mozilla/dom/PopupBlocker.h"
50 #include "nsContentSecurityManager.h"
51 #include "DefaultURI.h"
53 #include "mozilla/LoadInfo.h"
54 #include "mozilla/Maybe.h"
55 #include "mozilla/TextUtils.h"
56 #include "mozilla/ipc/URIUtils.h"
58 using mozilla::IsAscii;
59 using mozilla::dom::AutoEntryScript;
60 using mozilla::dom::JSExecutionContext;
62 static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID);
64 class nsJSThunk : public nsIInputStream {
65 public:
66 nsJSThunk();
68 NS_DECL_THREADSAFE_ISUPPORTS
69 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream)
71 nsresult Init(nsIURI* uri);
72 nsresult EvaluateScript(
73 nsIChannel* aChannel,
74 mozilla::dom::PopupBlocker::PopupControlState aPopupState,
75 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow);
77 protected:
78 virtual ~nsJSThunk();
80 nsCOMPtr<nsIInputStream> mInnerStream;
81 nsCString mScript;
82 nsCString mURL;
86 // nsISupports implementation...
88 NS_IMPL_ISUPPORTS(nsJSThunk, nsIInputStream)
90 nsJSThunk::nsJSThunk() = default;
92 nsJSThunk::~nsJSThunk() = default;
94 nsresult nsJSThunk::Init(nsIURI* uri) {
95 NS_ENSURE_ARG_POINTER(uri);
97 // Get the script string to evaluate...
98 nsresult rv = uri->GetPathQueryRef(mScript);
99 if (NS_FAILED(rv)) return rv;
101 // Get the url.
102 rv = uri->GetSpec(mURL);
103 if (NS_FAILED(rv)) return rv;
105 return NS_OK;
108 static bool IsISO88591(const nsString& aString) {
109 for (nsString::const_char_iterator c = aString.BeginReading(),
110 c_end = aString.EndReading();
111 c < c_end; ++c) {
112 if (*c > 255) return false;
114 return true;
117 static nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel) {
118 // Get the global object owner from the channel
119 nsCOMPtr<nsIDocShell> docShell;
120 NS_QueryNotificationCallbacks(aChannel, docShell);
121 if (!docShell) {
122 NS_WARNING("Unable to get a docShell from the channel!");
123 return nullptr;
126 // So far so good: get the script global from its docshell
127 nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject();
129 NS_ASSERTION(global,
130 "Unable to get an nsIScriptGlobalObject from the "
131 "docShell!");
132 return global;
135 static bool AllowedByCSP(nsIContentSecurityPolicy* aCSP,
136 const nsACString& aJavaScriptURL) {
137 if (!aCSP) {
138 return true;
141 // https://w3c.github.io/webappsec-csp/#should-block-navigation-request
142 // Step 3. If result is "Allowed", and if navigation request’s current URL’s
143 // scheme is javascript:
145 // Step 3.1.1.2 If directive’s inline check returns "Allowed" when executed
146 // upon null, "navigation" and navigation request’s current URL, skip to the
147 // next directive.
149 // This means /type/ is "navigation" and /source string/ is the
150 // "navigation request’s current URL".
152 // Per
153 // https://w3c.github.io/webappsec-csp/#effective-directive-for-inline-check
154 // type "navigation" maps to the effective directive script-src-elem.
155 bool allowsInlineScript = true;
156 nsresult rv =
157 aCSP->GetAllowsInline(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE,
158 true, // aHasUnsafeHash
159 u""_ns, // aNonce
160 true, // aParserCreated
161 nullptr, // aElement,
162 nullptr, // nsICSPEventListener
163 NS_ConvertASCIItoUTF16(aJavaScriptURL), // aContent
164 0, // aLineNumber
165 1, // aColumnNumber
166 &allowsInlineScript);
168 return (NS_SUCCEEDED(rv) && allowsInlineScript);
171 nsresult nsJSThunk::EvaluateScript(
172 nsIChannel* aChannel,
173 mozilla::dom::PopupBlocker::PopupControlState aPopupState,
174 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow) {
175 if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) {
176 // Nothing to do here.
177 return NS_ERROR_DOM_RETVAL_UNDEFINED;
180 NS_ENSURE_ARG_POINTER(aChannel);
181 MOZ_ASSERT(aOriginalInnerWindow,
182 "We should not have gotten here if this was null!");
184 // Set the channel's resultPrincipalURI to the active document's URI. This
185 // corresponds to treating that URI as the URI of our channel's response. In
186 // the spec we're supposed to use the URL of the active document, but since
187 // we bail out of here if the inner window has changed, and GetDocumentURI()
188 // on the inner window returns the URL of the active document if the inner
189 // window is current, this is equivalent to the spec behavior.
190 nsCOMPtr<nsIURI> docURI = aOriginalInnerWindow->GetDocumentURI();
191 if (!docURI) {
192 // We're not going to be able to have a sane URL, so just don't run the
193 // script at all.
194 return NS_ERROR_DOM_RETVAL_UNDEFINED;
196 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
197 loadInfo->SetResultPrincipalURI(docURI);
199 #ifdef DEBUG
200 DOMSecurityMonitor::AuditUseOfJavaScriptURI(aChannel);
201 #endif
203 // Get principal of code for execution
204 nsCOMPtr<nsISupports> owner;
205 aChannel->GetOwner(getter_AddRefs(owner));
206 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
207 if (!principal) {
208 if (loadInfo->GetForceInheritPrincipal()) {
209 principal = loadInfo->FindPrincipalToInherit(aChannel);
210 } else {
211 // No execution without a principal!
212 NS_ASSERTION(!owner, "Non-principal owner?");
213 NS_WARNING("No principal to execute JS with");
214 return NS_ERROR_DOM_RETVAL_UNDEFINED;
218 nsresult rv;
220 // CSP check: javascript: URIs are disabled unless "inline" scripts
221 // are allowed by both the CSP of the thing that started the load
222 // (which is the CSPToInherit of the loadinfo) and the CSP of the
223 // target document. The target document check is performed below,
224 // once we have determined the target document.
225 nsCOMPtr<nsIContentSecurityPolicy> csp = loadInfo->GetCspToInherit();
227 if (!AllowedByCSP(csp, mURL)) {
228 return NS_ERROR_DOM_RETVAL_UNDEFINED;
231 // Get the global object we should be running on.
232 nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
233 if (!global) {
234 return NS_ERROR_FAILURE;
237 // Make sure we still have the same inner window as we used to.
238 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(global);
239 nsPIDOMWindowInner* innerWin = win->GetCurrentInnerWindow();
241 if (innerWin != aOriginalInnerWindow) {
242 return NS_ERROR_UNEXPECTED;
245 mozilla::dom::Document* targetDoc = innerWin->GetExtantDoc();
247 // Sandboxed document check: javascript: URI execution is disabled in a
248 // sandboxed document unless 'allow-scripts' was specified.
249 if ((targetDoc && !targetDoc->IsScriptEnabled()) ||
250 (loadInfo->GetTriggeringSandboxFlags() & SANDBOXED_SCRIPTS)) {
251 if (nsCOMPtr<nsIObserverService> obs =
252 mozilla::services::GetObserverService()) {
253 obs->NotifyWhenScriptSafe(ToSupports(innerWin),
254 "javascript-uri-blocked-by-sandbox");
256 return NS_ERROR_DOM_RETVAL_UNDEFINED;
259 if (targetDoc) {
260 // Perform a Security check against the CSP of the document we are
261 // running against. javascript: URIs are disabled unless "inline"
262 // scripts are allowed. We only do that if targetDoc->NodePrincipal()
263 // subsumes loadInfo->TriggeringPrincipal(). If it doesn't, then
264 // someone more-privileged (our UI or an extension) started the
265 // load and hence the load should not be subject to the target
266 // document's CSP.
268 // The "more privileged" assumption is safe, because if the triggering
269 // principal does not subsume targetDoc->NodePrincipal() we won't run the
270 // script at all. More precisely, we test that "principal" subsumes the
271 // target's principal, but "principal" should never be higher-privilege
272 // than the triggering principal here: it's either the triggering
273 // principal, or the principal of the document we started the load
274 // against if the triggering principal is system.
275 if (targetDoc->NodePrincipal()->Subsumes(loadInfo->TriggeringPrincipal())) {
276 nsCOMPtr<nsIContentSecurityPolicy> targetCSP = targetDoc->GetCsp();
277 if (!AllowedByCSP(targetCSP, mURL)) {
278 return NS_ERROR_DOM_RETVAL_UNDEFINED;
283 // Push our popup control state
284 AutoPopupStatePusher popupStatePusher(aPopupState);
286 nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin);
288 // So far so good: get the script context from its owner.
289 nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext();
290 if (!scriptContext) return NS_ERROR_FAILURE;
292 // New script entry point required, due to the "Create a script" step of
293 // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
294 mozilla::nsAutoMicroTask mt;
295 AutoEntryScript aes(innerGlobal, "javascript: URI", true);
296 JSContext* cx = aes.cx();
297 JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
298 NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
300 //-- Don't execute unless the script principal subsumes the
301 // principal of the context.
302 nsIPrincipal* objectPrincipal =
303 nsContentUtils::ObjectPrincipal(globalJSObject);
305 bool subsumes;
306 rv = principal->Subsumes(objectPrincipal, &subsumes);
307 if (NS_FAILED(rv)) return rv;
309 if (!subsumes) {
310 return NS_ERROR_DOM_RETVAL_UNDEFINED;
313 // Fail if someone tries to execute in a global with system principal.
314 if (objectPrincipal->IsSystemPrincipal()) {
315 return NS_ERROR_DOM_SECURITY_ERR;
318 nsAutoCString script(mScript);
319 // Unescape the script
320 NS_UnescapeURL(script);
322 JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
323 // Finally, we have everything needed to evaluate the expression.
324 JS::CompileOptions options(cx);
325 options.setFileAndLine(mURL.get(), 1);
326 options.setIntroductionType("javascriptURL");
328 JSExecutionContext exec(cx, globalJSObject, options);
329 exec.SetCoerceToString(true);
330 exec.Compile(NS_ConvertUTF8toUTF16(script));
331 rv = exec.ExecScript(&v);
334 js::AssertSameCompartment(cx, v);
336 if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
337 return NS_ERROR_MALFORMED_URI;
339 if (v.isUndefined()) {
340 return NS_ERROR_DOM_RETVAL_UNDEFINED;
342 MOZ_ASSERT(rv != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW,
343 "How did we get a non-undefined return value?");
344 nsAutoJSString result;
345 if (!result.init(cx, v)) {
346 return NS_ERROR_OUT_OF_MEMORY;
349 char* bytes;
350 uint32_t bytesLen;
351 constexpr auto isoCharset = "windows-1252"_ns;
352 constexpr auto utf8Charset = "UTF-8"_ns;
353 const nsLiteralCString* charset;
354 if (IsISO88591(result)) {
355 // For compatibility, if the result is ISO-8859-1, we use
356 // windows-1252, so that people can compatibly create images
357 // using javascript: URLs.
358 bytes = ToNewCString(result, mozilla::fallible);
359 bytesLen = result.Length();
360 charset = &isoCharset;
361 } else {
362 bytes = ToNewUTF8String(result, &bytesLen);
363 charset = &utf8Charset;
365 aChannel->SetContentCharset(*charset);
366 if (bytes) {
367 rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream),
368 mozilla::Span(bytes, bytesLen),
369 NS_ASSIGNMENT_ADOPT);
370 } else {
371 rv = NS_ERROR_OUT_OF_MEMORY;
374 return rv;
377 ////////////////////////////////////////////////////////////////////////////////
379 class nsJSChannel : public nsIChannel,
380 public nsIStreamListener,
381 public nsIScriptChannel,
382 public nsIPropertyBag2 {
383 public:
384 nsJSChannel();
386 NS_DECL_ISUPPORTS
387 NS_DECL_NSIREQUEST
388 NS_DECL_NSICHANNEL
389 NS_DECL_NSIREQUESTOBSERVER
390 NS_DECL_NSISTREAMLISTENER
391 NS_DECL_NSISCRIPTCHANNEL
392 NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag)
393 NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag)
395 nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo);
397 // Actually evaluate the script.
398 void EvaluateScript();
400 protected:
401 virtual ~nsJSChannel();
403 nsresult StopAll();
405 void NotifyListener();
407 void CleanupStrongRefs();
409 nsCOMPtr<nsIChannel> mStreamChannel;
410 nsCOMPtr<nsIPropertyBag2> mPropertyBag;
411 nsCOMPtr<nsIStreamListener> mListener; // Our final listener
412 nsCOMPtr<nsPIDOMWindowInner> mOriginalInnerWindow; // The inner window our
413 // load started against.
414 // If we blocked onload on a document in AsyncOpen, this is the document we
415 // did it on.
416 RefPtr<mozilla::dom::Document> mDocumentOnloadBlockedOn;
418 nsresult mStatus; // Our status
420 nsLoadFlags mLoadFlags;
421 nsLoadFlags mActualLoadFlags; // See AsyncOpen
423 RefPtr<nsJSThunk> mIOThunk;
424 mozilla::dom::PopupBlocker::PopupControlState mPopupState;
425 uint32_t mExecutionPolicy;
426 bool mIsAsync;
427 bool mIsActive;
428 bool mOpenedStreamChannel;
431 nsJSChannel::nsJSChannel()
432 : mStatus(NS_OK),
433 mLoadFlags(LOAD_NORMAL),
434 mActualLoadFlags(LOAD_NORMAL),
435 mPopupState(mozilla::dom::PopupBlocker::openOverridden),
436 mExecutionPolicy(NO_EXECUTION),
437 mIsAsync(true),
438 mIsActive(false),
439 mOpenedStreamChannel(false) {}
441 nsJSChannel::~nsJSChannel() = default;
443 nsresult nsJSChannel::StopAll() {
444 nsresult rv = NS_ERROR_UNEXPECTED;
445 nsCOMPtr<nsIWebNavigation> webNav;
446 NS_QueryNotificationCallbacks(mStreamChannel, webNav);
448 NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!");
449 if (webNav) {
450 rv = webNav->Stop(nsIWebNavigation::STOP_ALL);
453 return rv;
456 nsresult nsJSChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
457 RefPtr<nsJSURI> jsURI;
458 nsresult rv = aURI->QueryInterface(kJSURICID, getter_AddRefs(jsURI));
459 NS_ENSURE_SUCCESS(rv, rv);
461 // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
462 mIOThunk = new nsJSThunk();
464 // Create a stock input stream channel...
465 // Remember, until AsyncOpen is called, the script will not be evaluated
466 // and the underlying Input Stream will not be created...
467 nsCOMPtr<nsIChannel> channel;
468 RefPtr<nsJSThunk> thunk = mIOThunk;
469 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
470 thunk.forget(), "text/html"_ns, ""_ns,
471 aLoadInfo);
472 NS_ENSURE_SUCCESS(rv, rv);
474 rv = mIOThunk->Init(aURI);
475 if (NS_SUCCEEDED(rv)) {
476 mStreamChannel = channel;
477 mPropertyBag = do_QueryInterface(channel);
478 nsCOMPtr<nsIWritablePropertyBag2> writableBag = do_QueryInterface(channel);
479 if (writableBag && jsURI->GetBaseURI()) {
480 writableBag->SetPropertyAsInterface(u"baseURI"_ns, jsURI->GetBaseURI());
484 return rv;
487 NS_IMETHODIMP
488 nsJSChannel::GetIsDocument(bool* aIsDocument) {
489 return NS_GetIsDocumentChannel(this, aIsDocument);
493 // nsISupports implementation...
496 NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver,
497 nsIStreamListener, nsIScriptChannel, nsIPropertyBag,
498 nsIPropertyBag2)
501 // nsIRequest implementation...
504 NS_IMETHODIMP
505 nsJSChannel::GetName(nsACString& aResult) {
506 return mStreamChannel->GetName(aResult);
509 NS_IMETHODIMP
510 nsJSChannel::IsPending(bool* aResult) {
511 *aResult = mIsActive;
512 return NS_OK;
515 NS_IMETHODIMP
516 nsJSChannel::GetStatus(nsresult* aResult) {
517 if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) {
518 return mStreamChannel->GetStatus(aResult);
521 *aResult = mStatus;
523 return NS_OK;
526 NS_IMETHODIMP nsJSChannel::SetCanceledReason(const nsACString& aReason) {
527 return SetCanceledReasonImpl(aReason);
530 NS_IMETHODIMP nsJSChannel::GetCanceledReason(nsACString& aReason) {
531 return GetCanceledReasonImpl(aReason);
534 NS_IMETHODIMP nsJSChannel::CancelWithReason(nsresult aStatus,
535 const nsACString& aReason) {
536 return CancelWithReasonImpl(aStatus, aReason);
539 NS_IMETHODIMP
540 nsJSChannel::Cancel(nsresult aStatus) {
541 mStatus = aStatus;
543 if (mOpenedStreamChannel) {
544 mStreamChannel->Cancel(aStatus);
547 return NS_OK;
550 NS_IMETHODIMP
551 nsJSChannel::GetCanceled(bool* aCanceled) {
552 nsresult status = NS_ERROR_FAILURE;
553 GetStatus(&status);
554 *aCanceled = NS_FAILED(status);
555 return NS_OK;
558 NS_IMETHODIMP
559 nsJSChannel::Suspend() { return mStreamChannel->Suspend(); }
561 NS_IMETHODIMP
562 nsJSChannel::Resume() { return mStreamChannel->Resume(); }
565 // nsIChannel implementation
568 NS_IMETHODIMP
569 nsJSChannel::GetOriginalURI(nsIURI** aURI) {
570 return mStreamChannel->GetOriginalURI(aURI);
573 NS_IMETHODIMP
574 nsJSChannel::SetOriginalURI(nsIURI* aURI) {
575 return mStreamChannel->SetOriginalURI(aURI);
578 NS_IMETHODIMP
579 nsJSChannel::GetURI(nsIURI** aURI) { return mStreamChannel->GetURI(aURI); }
581 NS_IMETHODIMP
582 nsJSChannel::Open(nsIInputStream** aStream) {
583 nsCOMPtr<nsIStreamListener> listener;
584 nsresult rv =
585 nsContentSecurityManager::doContentSecurityCheck(this, listener);
586 NS_ENSURE_SUCCESS(rv, rv);
588 rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, mExecutionPolicy,
589 mOriginalInnerWindow);
590 NS_ENSURE_SUCCESS(rv, rv);
592 return mStreamChannel->Open(aStream);
595 NS_IMETHODIMP
596 nsJSChannel::AsyncOpen(nsIStreamListener* aListener) {
597 NS_ENSURE_ARG(aListener);
599 nsCOMPtr<nsIStreamListener> listener = aListener;
600 nsresult rv =
601 nsContentSecurityManager::doContentSecurityCheck(this, listener);
602 NS_ENSURE_SUCCESS(rv, rv);
604 #ifdef DEBUG
606 nsCOMPtr<nsILoadInfo> loadInfo = nsIChannel::LoadInfo();
607 MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 ||
608 loadInfo->GetInitialSecurityCheckDone(),
609 "security flags in loadInfo but asyncOpen() not called");
611 #endif
613 // First make sure that we have a usable inner window; we'll want to make
614 // sure that we execute against that inner and no other.
615 nsIScriptGlobalObject* global = GetGlobalObject(this);
616 if (!global) {
617 return NS_ERROR_NOT_AVAILABLE;
620 nsCOMPtr<nsPIDOMWindowOuter> win(do_QueryInterface(global));
621 NS_ASSERTION(win, "Our global is not a window??");
623 // Make sure we create a new inner window if one doesn't already exist (see
624 // bug 306630).
625 mOriginalInnerWindow = win->EnsureInnerWindow();
626 if (!mOriginalInnerWindow) {
627 return NS_ERROR_NOT_AVAILABLE;
630 mListener = aListener;
632 mIsActive = true;
634 // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
635 // notifications (and hence nsIWebProgressListener notifications) from
636 // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI,
637 // which means that the DocLoader would not generate document start and
638 // stop notifications (see bug 257875).
639 mActualLoadFlags = mLoadFlags;
640 mLoadFlags |= LOAD_BACKGROUND;
642 // Add the javascript channel to its loadgroup so that we know if
643 // network loads were canceled or not...
644 nsCOMPtr<nsILoadGroup> loadGroup;
645 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
646 if (loadGroup) {
647 rv = loadGroup->AddRequest(this, nullptr);
648 if (NS_FAILED(rv)) {
649 mIsActive = false;
650 CleanupStrongRefs();
651 return rv;
655 mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc();
656 if (mDocumentOnloadBlockedOn) {
657 // If we're a document channel, we need to actually block onload on our
658 // _parent_ document. This is because we don't actually set our
659 // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the
660 // document channel will claim to not be busy, and our parent's onload
661 // could fire too early.
662 nsLoadFlags loadFlags;
663 mStreamChannel->GetLoadFlags(&loadFlags);
664 if (loadFlags & LOAD_DOCUMENT_URI) {
665 mDocumentOnloadBlockedOn =
666 mDocumentOnloadBlockedOn->GetInProcessParentDocument();
669 if (mDocumentOnloadBlockedOn) {
670 mDocumentOnloadBlockedOn->BlockOnload();
673 mPopupState = mozilla::dom::PopupBlocker::GetPopupControlState();
675 void (nsJSChannel::*method)();
676 const char* name;
677 if (mIsAsync) {
678 // post an event to do the rest
679 method = &nsJSChannel::EvaluateScript;
680 name = "nsJSChannel::EvaluateScript";
681 } else {
682 EvaluateScript();
683 if (mOpenedStreamChannel) {
684 // That will handle notifying things
685 return NS_OK;
688 NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_");
690 // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't
691 // have any content resulting from the execution and NS_BINDING_ABORTED
692 // if something we did causes our own load to be stopped. Return
693 // success in those cases, and error out in all others.
694 if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED &&
695 mStatus != NS_BINDING_ABORTED) {
696 // Note that calling EvaluateScript() handled removing us from the
697 // loadgroup and marking us as not active anymore.
698 CleanupStrongRefs();
699 return mStatus;
702 // We're returning success from asyncOpen(), but we didn't open a
703 // stream channel. We'll have to notify ourselves, but make sure to do
704 // it asynchronously.
705 method = &nsJSChannel::NotifyListener;
706 name = "nsJSChannel::NotifyListener";
709 nsCOMPtr<nsIRunnable> runnable =
710 mozilla::NewRunnableMethod(name, this, method);
711 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(mOriginalInnerWindow);
712 rv = window->Dispatch(runnable.forget());
714 if (NS_FAILED(rv)) {
715 loadGroup->RemoveRequest(this, nullptr, rv);
716 mIsActive = false;
717 CleanupStrongRefs();
719 return rv;
722 void nsJSChannel::EvaluateScript() {
723 // Synchronously execute the script...
724 // mIsActive is used to indicate the the request is 'busy' during the
725 // the script evaluation phase. This means that IsPending() will
726 // indicate the the request is busy while the script is executing...
728 // Note that we want to be in the loadgroup and pending while we evaluate
729 // the script, so that we find out if the loadgroup gets canceled by the
730 // script execution (in which case we shouldn't pump out data even if the
731 // script returns it).
733 if (NS_SUCCEEDED(mStatus)) {
734 nsresult rv = mIOThunk->EvaluateScript(
735 mStreamChannel, mPopupState, mExecutionPolicy, mOriginalInnerWindow);
737 // Note that evaluation may have canceled us, so recheck mStatus again
738 if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) {
739 mStatus = rv;
743 // Remove the javascript channel from its loadgroup...
744 nsCOMPtr<nsILoadGroup> loadGroup;
745 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
746 if (loadGroup) {
747 loadGroup->RemoveRequest(this, nullptr, mStatus);
750 // Reset load flags to their original value...
751 mLoadFlags = mActualLoadFlags;
753 // We're no longer active, it's now up to the stream channel to do
754 // the loading, if needed.
755 mIsActive = false;
757 if (NS_FAILED(mStatus)) {
758 if (mIsAsync) {
759 NotifyListener();
761 return;
764 // EvaluateScript() succeeded, and we were not canceled, that
765 // means there's data to parse as a result of evaluating the
766 // script.
768 // Get the stream channels load flags (!= mLoadFlags).
769 nsLoadFlags loadFlags;
770 mStreamChannel->GetLoadFlags(&loadFlags);
772 uint32_t disposition;
773 if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition)))
774 disposition = nsIChannel::DISPOSITION_INLINE;
775 if (loadFlags & LOAD_DOCUMENT_URI &&
776 disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
777 // We're loaded as the document channel and not expecting to download
778 // the result. If we go on, we'll blow away the current document. Make
779 // sure that's ok. If so, stop all pending network loads.
781 nsCOMPtr<nsIDocShell> docShell;
782 NS_QueryNotificationCallbacks(mStreamChannel, docShell);
783 if (docShell) {
784 nsCOMPtr<nsIDocumentViewer> viewer;
785 docShell->GetDocViewer(getter_AddRefs(viewer));
787 if (viewer) {
788 bool okToUnload;
790 if (NS_SUCCEEDED(viewer->PermitUnload(&okToUnload)) && !okToUnload) {
791 // The user didn't want to unload the current
792 // page, translate this into an undefined
793 // return from the javascript: URL...
794 mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED;
799 if (NS_SUCCEEDED(mStatus)) {
800 mStatus = StopAll();
804 if (NS_FAILED(mStatus)) {
805 if (mIsAsync) {
806 NotifyListener();
808 return;
811 mStatus = mStreamChannel->AsyncOpen(this);
812 if (NS_SUCCEEDED(mStatus)) {
813 // mStreamChannel will call OnStartRequest and OnStopRequest on
814 // us, so we'll be sure to call them on our listener.
815 mOpenedStreamChannel = true;
817 // Now readd ourselves to the loadgroup so we can receive
818 // cancellation notifications.
819 mIsActive = true;
820 if (loadGroup) {
821 mStatus = loadGroup->AddRequest(this, nullptr);
823 // If AddRequest failed, that's OK. The key is to make sure we get
824 // cancelled if needed, and that call just canceled us if it
825 // failed. We'll still get notified by the stream channel when it
826 // finishes.
829 } else if (mIsAsync) {
830 NotifyListener();
834 void nsJSChannel::NotifyListener() {
835 mListener->OnStartRequest(this);
836 mListener->OnStopRequest(this, mStatus);
838 CleanupStrongRefs();
841 void nsJSChannel::CleanupStrongRefs() {
842 mListener = nullptr;
843 mOriginalInnerWindow = nullptr;
844 if (mDocumentOnloadBlockedOn) {
845 mDocumentOnloadBlockedOn->UnblockOnload(false);
846 mDocumentOnloadBlockedOn = nullptr;
850 NS_IMETHODIMP
851 nsJSChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
852 *aLoadFlags = mLoadFlags;
854 return NS_OK;
857 NS_IMETHODIMP
858 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
859 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
860 // actually right.
861 bool bogusLoadBackground = false;
862 if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) &&
863 (aLoadFlags & LOAD_BACKGROUND)) {
864 // We're getting a LOAD_BACKGROUND, but it's probably just our own fake
865 // flag being mirrored to us. The one exception is if our loadgroup is
866 // LOAD_BACKGROUND.
867 bool loadGroupIsBackground = false;
868 nsCOMPtr<nsILoadGroup> loadGroup;
869 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
870 if (loadGroup) {
871 nsLoadFlags loadGroupFlags;
872 loadGroup->GetLoadFlags(&loadGroupFlags);
873 loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0);
875 bogusLoadBackground = !loadGroupIsBackground;
878 // Since the javascript channel is never the actual channel that
879 // any data is loaded through, don't ever set the
880 // LOAD_DOCUMENT_URI flag on it, since that could lead to two
881 // 'document channels' in the loadgroup if a javascript: URL is
882 // loaded while a document is being loaded in the same window.
884 // XXXbz this, and a whole lot of other hackery, could go away if we'd just
885 // cancel the current document load on javascript: load start like IE does.
887 mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
889 if (bogusLoadBackground) {
890 aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND;
893 mActualLoadFlags = aLoadFlags;
895 // ... but the underlying stream channel should get this bit, if
896 // set, since that'll be the real document channel if the
897 // javascript: URL generated data.
899 return mStreamChannel->SetLoadFlags(aLoadFlags);
902 NS_IMETHODIMP
903 nsJSChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
904 return GetTRRModeImpl(aTRRMode);
907 NS_IMETHODIMP
908 nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
909 return SetTRRModeImpl(aTRRMode);
912 NS_IMETHODIMP
913 nsJSChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
914 return mStreamChannel->GetLoadGroup(aLoadGroup);
917 NS_IMETHODIMP
918 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
919 if (aLoadGroup) {
920 bool streamPending;
921 nsresult rv = mStreamChannel->IsPending(&streamPending);
922 NS_ENSURE_SUCCESS(rv, rv);
924 if (streamPending) {
925 nsCOMPtr<nsILoadGroup> curLoadGroup;
926 mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup));
928 if (aLoadGroup != curLoadGroup) {
929 // Move the stream channel to our new loadgroup. Make sure to
930 // add it before removing it, so that we don't trigger onload
931 // by accident.
932 aLoadGroup->AddRequest(mStreamChannel, nullptr);
933 if (curLoadGroup) {
934 curLoadGroup->RemoveRequest(mStreamChannel, nullptr,
935 NS_BINDING_RETARGETED);
941 return mStreamChannel->SetLoadGroup(aLoadGroup);
944 NS_IMETHODIMP
945 nsJSChannel::GetOwner(nsISupports** aOwner) {
946 return mStreamChannel->GetOwner(aOwner);
949 NS_IMETHODIMP
950 nsJSChannel::SetOwner(nsISupports* aOwner) {
951 return mStreamChannel->SetOwner(aOwner);
954 NS_IMETHODIMP
955 nsJSChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
956 return mStreamChannel->GetLoadInfo(aLoadInfo);
959 NS_IMETHODIMP
960 nsJSChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
961 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
962 return mStreamChannel->SetLoadInfo(aLoadInfo);
965 NS_IMETHODIMP
966 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
967 return mStreamChannel->GetNotificationCallbacks(aCallbacks);
970 NS_IMETHODIMP
971 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
972 return mStreamChannel->SetNotificationCallbacks(aCallbacks);
975 NS_IMETHODIMP
976 nsJSChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
977 return mStreamChannel->GetSecurityInfo(aSecurityInfo);
980 NS_IMETHODIMP
981 nsJSChannel::GetContentType(nsACString& aContentType) {
982 return mStreamChannel->GetContentType(aContentType);
985 NS_IMETHODIMP
986 nsJSChannel::SetContentType(const nsACString& aContentType) {
987 return mStreamChannel->SetContentType(aContentType);
990 NS_IMETHODIMP
991 nsJSChannel::GetContentCharset(nsACString& aContentCharset) {
992 return mStreamChannel->GetContentCharset(aContentCharset);
995 NS_IMETHODIMP
996 nsJSChannel::SetContentCharset(const nsACString& aContentCharset) {
997 return mStreamChannel->SetContentCharset(aContentCharset);
1000 NS_IMETHODIMP
1001 nsJSChannel::GetContentDisposition(uint32_t* aContentDisposition) {
1002 return mStreamChannel->GetContentDisposition(aContentDisposition);
1005 NS_IMETHODIMP
1006 nsJSChannel::SetContentDisposition(uint32_t aContentDisposition) {
1007 return mStreamChannel->SetContentDisposition(aContentDisposition);
1010 NS_IMETHODIMP
1011 nsJSChannel::GetContentDispositionFilename(
1012 nsAString& aContentDispositionFilename) {
1013 return mStreamChannel->GetContentDispositionFilename(
1014 aContentDispositionFilename);
1017 NS_IMETHODIMP
1018 nsJSChannel::SetContentDispositionFilename(
1019 const nsAString& aContentDispositionFilename) {
1020 return mStreamChannel->SetContentDispositionFilename(
1021 aContentDispositionFilename);
1024 NS_IMETHODIMP
1025 nsJSChannel::GetContentDispositionHeader(
1026 nsACString& aContentDispositionHeader) {
1027 return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader);
1030 NS_IMETHODIMP
1031 nsJSChannel::GetContentLength(int64_t* aContentLength) {
1032 return mStreamChannel->GetContentLength(aContentLength);
1035 NS_IMETHODIMP
1036 nsJSChannel::SetContentLength(int64_t aContentLength) {
1037 return mStreamChannel->SetContentLength(aContentLength);
1040 NS_IMETHODIMP
1041 nsJSChannel::OnStartRequest(nsIRequest* aRequest) {
1042 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1044 return mListener->OnStartRequest(this);
1047 NS_IMETHODIMP
1048 nsJSChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1049 uint64_t aOffset, uint32_t aCount) {
1050 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1052 return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount);
1055 NS_IMETHODIMP
1056 nsJSChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
1057 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1059 nsCOMPtr<nsIStreamListener> listener = mListener;
1061 CleanupStrongRefs();
1063 // Make sure aStatus matches what GetStatus() returns
1064 if (NS_FAILED(mStatus)) {
1065 aStatus = mStatus;
1068 nsresult rv = listener->OnStopRequest(this, aStatus);
1070 nsCOMPtr<nsILoadGroup> loadGroup;
1071 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1072 if (loadGroup) {
1073 loadGroup->RemoveRequest(this, nullptr, mStatus);
1076 mIsActive = false;
1078 return rv;
1081 NS_IMETHODIMP
1082 nsJSChannel::SetExecutionPolicy(uint32_t aPolicy) {
1083 NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL);
1085 mExecutionPolicy = aPolicy;
1086 return NS_OK;
1089 NS_IMETHODIMP
1090 nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy) {
1091 *aPolicy = mExecutionPolicy;
1092 return NS_OK;
1095 NS_IMETHODIMP
1096 nsJSChannel::SetExecuteAsync(bool aIsAsync) {
1097 if (!mIsActive) {
1098 mIsAsync = aIsAsync;
1100 // else ignore this call
1101 NS_WARNING_ASSERTION(!mIsActive,
1102 "Calling SetExecuteAsync on active channel?");
1104 return NS_OK;
1107 NS_IMETHODIMP
1108 nsJSChannel::GetExecuteAsync(bool* aIsAsync) {
1109 *aIsAsync = mIsAsync;
1110 return NS_OK;
1113 bool nsJSChannel::GetIsDocumentLoad() {
1114 // Our LOAD_DOCUMENT_URI flag, if any, lives on our stream channel.
1115 nsLoadFlags flags;
1116 mStreamChannel->GetLoadFlags(&flags);
1117 return flags & LOAD_DOCUMENT_URI;
1120 ////////////////////////////////////////////////////////////////////////////////
1122 nsJSProtocolHandler::nsJSProtocolHandler() = default;
1124 nsJSProtocolHandler::~nsJSProtocolHandler() = default;
1126 NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler)
1128 /* static */ nsresult nsJSProtocolHandler::EnsureUTF8Spec(
1129 const nsCString& aSpec, const char* aCharset, nsACString& aUTF8Spec) {
1130 aUTF8Spec.Truncate();
1132 nsAutoString uStr;
1133 nsresult rv = nsTextToSubURI::UnEscapeNonAsciiURI(
1134 nsDependentCString(aCharset), aSpec, uStr);
1135 NS_ENSURE_SUCCESS(rv, rv);
1137 if (!IsAscii(uStr)) {
1138 rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr),
1139 esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec,
1140 mozilla::fallible);
1141 NS_ENSURE_SUCCESS(rv, rv);
1144 return NS_OK;
1147 ////////////////////////////////////////////////////////////////////////////////
1148 // nsIProtocolHandler methods:
1150 NS_IMETHODIMP
1151 nsJSProtocolHandler::GetScheme(nsACString& result) {
1152 result = "javascript";
1153 return NS_OK;
1156 /* static */ nsresult nsJSProtocolHandler::CreateNewURI(const nsACString& aSpec,
1157 const char* aCharset,
1158 nsIURI* aBaseURI,
1159 nsIURI** result) {
1160 nsresult rv = NS_OK;
1162 // javascript: URLs (currently) have no additional structure beyond that
1163 // provided by standard URLs, so there is no "outer" object given to
1164 // CreateInstance.
1166 NS_MutateURI mutator(new nsJSURI::Mutator());
1167 nsCOMPtr<nsIURI> base(aBaseURI);
1168 mutator.Apply(&nsIJSURIMutator::SetBase, base);
1169 if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) {
1170 mutator.SetSpec(aSpec);
1171 } else {
1172 nsAutoCString utf8Spec;
1173 rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
1174 if (NS_FAILED(rv)) {
1175 return rv;
1177 if (utf8Spec.IsEmpty()) {
1178 mutator.SetSpec(aSpec);
1179 } else {
1180 mutator.SetSpec(utf8Spec);
1184 nsCOMPtr<nsIURI> url;
1185 rv = mutator.Finalize(url);
1186 if (NS_FAILED(rv)) {
1187 return rv;
1190 // use DefaultURI to check for validity when we have possible hostnames
1191 // since nsSimpleURI doesn't know about hostnames
1192 auto pos = aSpec.Find("javascript:");
1193 if (pos != kNotFound) {
1194 nsDependentCSubstring rest(aSpec, pos + sizeof("javascript:") - 1, -1);
1195 if (StringBeginsWith(rest, "//"_ns)) {
1196 nsCOMPtr<nsIURI> uriWithHost;
1197 rv = NS_MutateURI(new mozilla::net::DefaultURI::Mutator())
1198 .SetSpec(aSpec)
1199 .Finalize(uriWithHost);
1200 NS_ENSURE_SUCCESS(rv, rv);
1204 url.forget(result);
1205 return rv;
1208 NS_IMETHODIMP
1209 nsJSProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
1210 nsIChannel** result) {
1211 nsresult rv;
1213 NS_ENSURE_ARG_POINTER(uri);
1214 RefPtr<nsJSChannel> channel = new nsJSChannel();
1215 if (!channel) {
1216 return NS_ERROR_OUT_OF_MEMORY;
1219 rv = channel->Init(uri, aLoadInfo);
1220 NS_ENSURE_SUCCESS(rv, rv);
1222 if (NS_SUCCEEDED(rv)) {
1223 channel.forget(result);
1225 return rv;
1228 NS_IMETHODIMP
1229 nsJSProtocolHandler::AllowPort(int32_t port, const char* scheme,
1230 bool* _retval) {
1231 // don't override anything.
1232 *_retval = false;
1233 return NS_OK;
1236 ////////////////////////////////////////////////////////////
1237 // nsJSURI implementation
1238 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
1239 NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
1241 NS_IMPL_ADDREF_INHERITED(nsJSURI, mozilla::net::nsSimpleURI)
1242 NS_IMPL_RELEASE_INHERITED(nsJSURI, mozilla::net::nsSimpleURI)
1244 NS_IMPL_CLASSINFO(nsJSURI, nullptr, nsIClassInfo::THREADSAFE, NS_JSURI_CID);
1245 // Empty CI getter. We only need nsIClassInfo for Serialization
1246 NS_IMPL_CI_INTERFACE_GETTER0(nsJSURI)
1248 NS_INTERFACE_MAP_BEGIN(nsJSURI)
1249 if (aIID.Equals(kJSURICID))
1250 foundInterface = static_cast<nsIURI*>(this);
1251 else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
1252 // Need to return explicitly here, because if we just set foundInterface
1253 // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
1254 // nsSimplURI::QueryInterface and finding something for this CID.
1255 *aInstancePtr = nullptr;
1256 return NS_NOINTERFACE;
1257 } else
1258 NS_IMPL_QUERY_CLASSINFO(nsJSURI)
1259 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI)
1261 // nsISerializable methods:
1263 NS_IMETHODIMP
1264 nsJSURI::Read(nsIObjectInputStream* aStream) {
1265 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
1266 return NS_ERROR_NOT_IMPLEMENTED;
1269 nsresult nsJSURI::ReadPrivate(nsIObjectInputStream* aStream) {
1270 nsresult rv = mozilla::net::nsSimpleURI::ReadPrivate(aStream);
1271 if (NS_FAILED(rv)) return rv;
1273 bool haveBase;
1274 rv = aStream->ReadBoolean(&haveBase);
1275 if (NS_FAILED(rv)) return rv;
1277 if (haveBase) {
1278 nsCOMPtr<nsISupports> supports;
1279 rv = aStream->ReadObject(true, getter_AddRefs(supports));
1280 if (NS_FAILED(rv)) return rv;
1281 mBaseURI = do_QueryInterface(supports);
1284 return NS_OK;
1287 NS_IMETHODIMP
1288 nsJSURI::Write(nsIObjectOutputStream* aStream) {
1289 nsresult rv = mozilla::net::nsSimpleURI::Write(aStream);
1290 if (NS_FAILED(rv)) return rv;
1292 rv = aStream->WriteBoolean(mBaseURI != nullptr);
1293 if (NS_FAILED(rv)) return rv;
1295 if (mBaseURI) {
1296 rv = aStream->WriteObject(mBaseURI, true);
1297 if (NS_FAILED(rv)) return rv;
1300 return NS_OK;
1303 NS_IMETHODIMP_(void) nsJSURI::Serialize(mozilla::ipc::URIParams& aParams) {
1304 using namespace mozilla::ipc;
1306 JSURIParams jsParams;
1307 URIParams simpleParams;
1309 mozilla::net::nsSimpleURI::Serialize(simpleParams);
1311 jsParams.simpleParams() = simpleParams;
1312 if (mBaseURI) {
1313 SerializeURI(mBaseURI, jsParams.baseURI());
1314 } else {
1315 jsParams.baseURI() = mozilla::Nothing();
1318 aParams = jsParams;
1321 bool nsJSURI::Deserialize(const mozilla::ipc::URIParams& aParams) {
1322 using namespace mozilla::ipc;
1324 if (aParams.type() != URIParams::TJSURIParams) {
1325 NS_ERROR("Received unknown parameters from the other process!");
1326 return false;
1329 const JSURIParams& jsParams = aParams.get_JSURIParams();
1330 mozilla::net::nsSimpleURI::Deserialize(jsParams.simpleParams());
1332 if (jsParams.baseURI().isSome()) {
1333 mBaseURI = DeserializeURI(jsParams.baseURI().ref());
1334 } else {
1335 mBaseURI = nullptr;
1337 return true;
1340 // nsSimpleURI methods:
1341 /* virtual */ mozilla::net::nsSimpleURI* nsJSURI::StartClone(
1342 mozilla::net::nsSimpleURI::RefHandlingEnum refHandlingMode,
1343 const nsACString& newRef) {
1344 nsJSURI* url = new nsJSURI(mBaseURI);
1345 SetRefOnClone(url, refHandlingMode, newRef);
1346 return url;
1349 // Queries this list of interfaces. If none match, it queries mURI.
1350 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJSURI::Mutator, nsIURISetters, nsIURIMutator,
1351 nsISerializable, nsIJSURIMutator)
1353 NS_IMETHODIMP
1354 nsJSURI::Mutate(nsIURIMutator** aMutator) {
1355 RefPtr<nsJSURI::Mutator> mutator = new nsJSURI::Mutator();
1356 nsresult rv = mutator->InitFromURI(this);
1357 if (NS_FAILED(rv)) {
1358 return rv;
1360 mutator.forget(aMutator);
1361 return NS_OK;
1364 /* virtual */
1365 nsresult nsJSURI::EqualsInternal(
1366 nsIURI* aOther, mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
1367 bool* aResult) {
1368 NS_ENSURE_ARG_POINTER(aOther);
1369 MOZ_ASSERT(aResult, "null pointer for outparam");
1371 RefPtr<nsJSURI> otherJSURI;
1372 nsresult rv = aOther->QueryInterface(kJSURICID, getter_AddRefs(otherJSURI));
1373 if (NS_FAILED(rv)) {
1374 *aResult = false; // aOther is not a nsJSURI --> not equal.
1375 return NS_OK;
1378 // Compare the member data that our base class knows about.
1379 if (!mozilla::net::nsSimpleURI::EqualsInternal(otherJSURI,
1380 aRefHandlingMode)) {
1381 *aResult = false;
1382 return NS_OK;
1385 // Compare the piece of additional member data that we add to base class.
1386 nsIURI* otherBaseURI = otherJSURI->GetBaseURI();
1388 if (mBaseURI) {
1389 // (As noted in StartClone, we always honor refs on mBaseURI)
1390 return mBaseURI->Equals(otherBaseURI, aResult);
1393 *aResult = !otherBaseURI;
1394 return NS_OK;