Bug 1854367 - Mark storage-access-permission.sub.https.window.html subtest as intermi...
[gecko.git] / dom / jsurl / nsJSProtocolHandler.cpp
blobb4e0f2187a2b35b4f68a958751a2fc65ce603062
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 "nsIContentViewer.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"
52 #include "mozilla/LoadInfo.h"
53 #include "mozilla/Maybe.h"
54 #include "mozilla/TextUtils.h"
55 #include "mozilla/ipc/URIUtils.h"
57 using mozilla::IsAscii;
58 using mozilla::dom::AutoEntryScript;
59 using mozilla::dom::JSExecutionContext;
61 static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID);
63 class nsJSThunk : public nsIInputStream {
64 public:
65 nsJSThunk();
67 NS_DECL_THREADSAFE_ISUPPORTS
68 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream)
70 nsresult Init(nsIURI* uri);
71 nsresult EvaluateScript(
72 nsIChannel* aChannel,
73 mozilla::dom::PopupBlocker::PopupControlState aPopupState,
74 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow);
76 protected:
77 virtual ~nsJSThunk();
79 nsCOMPtr<nsIInputStream> mInnerStream;
80 nsCString mScript;
81 nsCString mURL;
85 // nsISupports implementation...
87 NS_IMPL_ISUPPORTS(nsJSThunk, nsIInputStream)
89 nsJSThunk::nsJSThunk() = default;
91 nsJSThunk::~nsJSThunk() = default;
93 nsresult nsJSThunk::Init(nsIURI* uri) {
94 NS_ENSURE_ARG_POINTER(uri);
96 // Get the script string to evaluate...
97 nsresult rv = uri->GetPathQueryRef(mScript);
98 if (NS_FAILED(rv)) return rv;
100 // Get the url.
101 rv = uri->GetSpec(mURL);
102 if (NS_FAILED(rv)) return rv;
104 return NS_OK;
107 static bool IsISO88591(const nsString& aString) {
108 for (nsString::const_char_iterator c = aString.BeginReading(),
109 c_end = aString.EndReading();
110 c < c_end; ++c) {
111 if (*c > 255) return false;
113 return true;
116 static nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel) {
117 // Get the global object owner from the channel
118 nsCOMPtr<nsIDocShell> docShell;
119 NS_QueryNotificationCallbacks(aChannel, docShell);
120 if (!docShell) {
121 NS_WARNING("Unable to get a docShell from the channel!");
122 return nullptr;
125 // So far so good: get the script global from its docshell
126 nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject();
128 NS_ASSERTION(global,
129 "Unable to get an nsIScriptGlobalObject from the "
130 "docShell!");
131 return global;
134 static bool AllowedByCSP(nsIContentSecurityPolicy* aCSP,
135 const nsACString& aJavaScriptURL) {
136 if (!aCSP) {
137 return true;
140 // https://w3c.github.io/webappsec-csp/#should-block-navigation-request
141 // Step 3. If result is "Allowed", and if navigation request’s current URL’s
142 // scheme is javascript:
144 // Step 3.1.1.2 If directive’s inline check returns "Allowed" when executed
145 // upon null, "navigation" and navigation request’s current URL, skip to the
146 // next directive.
148 // This means /type/ is "navigation" and /source string/ is the
149 // "navigation request’s current URL".
151 // Per
152 // https://w3c.github.io/webappsec-csp/#effective-directive-for-inline-check
153 // type "navigation" maps to the effective directive script-src-elem.
154 bool allowsInlineScript = true;
155 nsresult rv =
156 aCSP->GetAllowsInline(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE,
157 true, // aHasUnsafeHash
158 u""_ns, // aNonce
159 true, // aParserCreated
160 nullptr, // aElement,
161 nullptr, // nsICSPEventListener
162 NS_ConvertASCIItoUTF16(aJavaScriptURL), // aContent
163 0, // aLineNumber
164 0, // aColumnNumber
165 &allowsInlineScript);
167 return (NS_SUCCEEDED(rv) && allowsInlineScript);
170 nsresult nsJSThunk::EvaluateScript(
171 nsIChannel* aChannel,
172 mozilla::dom::PopupBlocker::PopupControlState aPopupState,
173 uint32_t aExecutionPolicy, nsPIDOMWindowInner* aOriginalInnerWindow) {
174 if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) {
175 // Nothing to do here.
176 return NS_ERROR_DOM_RETVAL_UNDEFINED;
179 NS_ENSURE_ARG_POINTER(aChannel);
180 MOZ_ASSERT(aOriginalInnerWindow,
181 "We should not have gotten here if this was null!");
183 // Set the channel's resultPrincipalURI to the active document's URI. This
184 // corresponds to treating that URI as the URI of our channel's response. In
185 // the spec we're supposed to use the URL of the active document, but since
186 // we bail out of here if the inner window has changed, and GetDocumentURI()
187 // on the inner window returns the URL of the active document if the inner
188 // window is current, this is equivalent to the spec behavior.
189 nsCOMPtr<nsIURI> docURI = aOriginalInnerWindow->GetDocumentURI();
190 if (!docURI) {
191 // We're not going to be able to have a sane URL, so just don't run the
192 // script at all.
193 return NS_ERROR_DOM_RETVAL_UNDEFINED;
195 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
196 loadInfo->SetResultPrincipalURI(docURI);
198 #ifdef DEBUG
199 DOMSecurityMonitor::AuditUseOfJavaScriptURI(aChannel);
200 #endif
202 // Get principal of code for execution
203 nsCOMPtr<nsISupports> owner;
204 aChannel->GetOwner(getter_AddRefs(owner));
205 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
206 if (!principal) {
207 if (loadInfo->GetForceInheritPrincipal()) {
208 principal = loadInfo->FindPrincipalToInherit(aChannel);
209 } else {
210 // No execution without a principal!
211 NS_ASSERTION(!owner, "Non-principal owner?");
212 NS_WARNING("No principal to execute JS with");
213 return NS_ERROR_DOM_RETVAL_UNDEFINED;
217 nsresult rv;
219 // CSP check: javascript: URIs are disabled unless "inline" scripts
220 // are allowed by both the CSP of the thing that started the load
221 // (which is the CSPToInherit of the loadinfo) and the CSP of the
222 // target document. The target document check is performed below,
223 // once we have determined the target document.
224 nsCOMPtr<nsIContentSecurityPolicy> csp = loadInfo->GetCspToInherit();
226 if (!AllowedByCSP(csp, mURL)) {
227 return NS_ERROR_DOM_RETVAL_UNDEFINED;
230 // Get the global object we should be running on.
231 nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
232 if (!global) {
233 return NS_ERROR_FAILURE;
236 // Make sure we still have the same inner window as we used to.
237 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(global);
238 nsPIDOMWindowInner* innerWin = win->GetCurrentInnerWindow();
240 if (innerWin != aOriginalInnerWindow) {
241 return NS_ERROR_UNEXPECTED;
244 mozilla::dom::Document* targetDoc = innerWin->GetExtantDoc();
246 // Sandboxed document check: javascript: URI execution is disabled in a
247 // sandboxed document unless 'allow-scripts' was specified.
248 if ((targetDoc && !targetDoc->IsScriptEnabled()) ||
249 (loadInfo->GetTriggeringSandboxFlags() & SANDBOXED_SCRIPTS)) {
250 if (nsCOMPtr<nsIObserverService> obs =
251 mozilla::services::GetObserverService()) {
252 obs->NotifyWhenScriptSafe(ToSupports(innerWin),
253 "javascript-uri-blocked-by-sandbox");
255 return NS_ERROR_DOM_RETVAL_UNDEFINED;
258 if (targetDoc) {
259 // Perform a Security check against the CSP of the document we are
260 // running against. javascript: URIs are disabled unless "inline"
261 // scripts are allowed. We only do that if targetDoc->NodePrincipal()
262 // subsumes loadInfo->TriggeringPrincipal(). If it doesn't, then
263 // someone more-privileged (our UI or an extension) started the
264 // load and hence the load should not be subject to the target
265 // document's CSP.
267 // The "more privileged" assumption is safe, because if the triggering
268 // principal does not subsume targetDoc->NodePrincipal() we won't run the
269 // script at all. More precisely, we test that "principal" subsumes the
270 // target's principal, but "principal" should never be higher-privilege
271 // than the triggering principal here: it's either the triggering
272 // principal, or the principal of the document we started the load
273 // against if the triggering principal is system.
274 if (targetDoc->NodePrincipal()->Subsumes(loadInfo->TriggeringPrincipal())) {
275 nsCOMPtr<nsIContentSecurityPolicy> targetCSP = targetDoc->GetCsp();
276 if (!AllowedByCSP(targetCSP, mURL)) {
277 return NS_ERROR_DOM_RETVAL_UNDEFINED;
282 // Push our popup control state
283 AutoPopupStatePusher popupStatePusher(aPopupState);
285 nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin);
287 // So far so good: get the script context from its owner.
288 nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext();
289 if (!scriptContext) return NS_ERROR_FAILURE;
291 // New script entry point required, due to the "Create a script" step of
292 // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
293 mozilla::nsAutoMicroTask mt;
294 AutoEntryScript aes(innerGlobal, "javascript: URI", true);
295 JSContext* cx = aes.cx();
296 JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
297 NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
299 //-- Don't execute unless the script principal subsumes the
300 // principal of the context.
301 nsIPrincipal* objectPrincipal =
302 nsContentUtils::ObjectPrincipal(globalJSObject);
304 bool subsumes;
305 rv = principal->Subsumes(objectPrincipal, &subsumes);
306 if (NS_FAILED(rv)) return rv;
308 if (!subsumes) {
309 return NS_ERROR_DOM_RETVAL_UNDEFINED;
312 // Fail if someone tries to execute in a global with system principal.
313 if (objectPrincipal->IsSystemPrincipal()) {
314 return NS_ERROR_DOM_SECURITY_ERR;
317 nsAutoCString script(mScript);
318 // Unescape the script
319 NS_UnescapeURL(script);
321 JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
322 // Finally, we have everything needed to evaluate the expression.
323 JS::CompileOptions options(cx);
324 options.setFileAndLine(mURL.get(), 1);
325 options.setIntroductionType("javascriptURL");
327 JSExecutionContext exec(cx, globalJSObject, options);
328 exec.SetCoerceToString(true);
329 exec.Compile(NS_ConvertUTF8toUTF16(script));
330 rv = exec.ExecScript(&v);
333 js::AssertSameCompartment(cx, v);
335 if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
336 return NS_ERROR_MALFORMED_URI;
338 if (v.isUndefined()) {
339 return NS_ERROR_DOM_RETVAL_UNDEFINED;
341 MOZ_ASSERT(rv != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW,
342 "How did we get a non-undefined return value?");
343 nsAutoJSString result;
344 if (!result.init(cx, v)) {
345 return NS_ERROR_OUT_OF_MEMORY;
348 char* bytes;
349 uint32_t bytesLen;
350 constexpr auto isoCharset = "windows-1252"_ns;
351 constexpr auto utf8Charset = "UTF-8"_ns;
352 const nsLiteralCString* charset;
353 if (IsISO88591(result)) {
354 // For compatibility, if the result is ISO-8859-1, we use
355 // windows-1252, so that people can compatibly create images
356 // using javascript: URLs.
357 bytes = ToNewCString(result, mozilla::fallible);
358 bytesLen = result.Length();
359 charset = &isoCharset;
360 } else {
361 bytes = ToNewUTF8String(result, &bytesLen);
362 charset = &utf8Charset;
364 aChannel->SetContentCharset(*charset);
365 if (bytes) {
366 rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream),
367 mozilla::Span(bytes, bytesLen),
368 NS_ASSIGNMENT_ADOPT);
369 } else {
370 rv = NS_ERROR_OUT_OF_MEMORY;
373 return rv;
376 ////////////////////////////////////////////////////////////////////////////////
378 class nsJSChannel : public nsIChannel,
379 public nsIStreamListener,
380 public nsIScriptChannel,
381 public nsIPropertyBag2 {
382 public:
383 nsJSChannel();
385 NS_DECL_ISUPPORTS
386 NS_DECL_NSIREQUEST
387 NS_DECL_NSICHANNEL
388 NS_DECL_NSIREQUESTOBSERVER
389 NS_DECL_NSISTREAMLISTENER
390 NS_DECL_NSISCRIPTCHANNEL
391 NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag)
392 NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag)
394 nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo);
396 // Actually evaluate the script.
397 void EvaluateScript();
399 protected:
400 virtual ~nsJSChannel();
402 nsresult StopAll();
404 void NotifyListener();
406 void CleanupStrongRefs();
408 nsCOMPtr<nsIChannel> mStreamChannel;
409 nsCOMPtr<nsIPropertyBag2> mPropertyBag;
410 nsCOMPtr<nsIStreamListener> mListener; // Our final listener
411 nsCOMPtr<nsPIDOMWindowInner> mOriginalInnerWindow; // The inner window our
412 // load started against.
413 // If we blocked onload on a document in AsyncOpen, this is the document we
414 // did it on.
415 RefPtr<mozilla::dom::Document> mDocumentOnloadBlockedOn;
417 nsresult mStatus; // Our status
419 nsLoadFlags mLoadFlags;
420 nsLoadFlags mActualLoadFlags; // See AsyncOpen
422 RefPtr<nsJSThunk> mIOThunk;
423 mozilla::dom::PopupBlocker::PopupControlState mPopupState;
424 uint32_t mExecutionPolicy;
425 bool mIsAsync;
426 bool mIsActive;
427 bool mOpenedStreamChannel;
430 nsJSChannel::nsJSChannel()
431 : mStatus(NS_OK),
432 mLoadFlags(LOAD_NORMAL),
433 mActualLoadFlags(LOAD_NORMAL),
434 mPopupState(mozilla::dom::PopupBlocker::openOverridden),
435 mExecutionPolicy(NO_EXECUTION),
436 mIsAsync(true),
437 mIsActive(false),
438 mOpenedStreamChannel(false) {}
440 nsJSChannel::~nsJSChannel() = default;
442 nsresult nsJSChannel::StopAll() {
443 nsresult rv = NS_ERROR_UNEXPECTED;
444 nsCOMPtr<nsIWebNavigation> webNav;
445 NS_QueryNotificationCallbacks(mStreamChannel, webNav);
447 NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!");
448 if (webNav) {
449 rv = webNav->Stop(nsIWebNavigation::STOP_ALL);
452 return rv;
455 nsresult nsJSChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
456 RefPtr<nsJSURI> jsURI;
457 nsresult rv = aURI->QueryInterface(kJSURICID, getter_AddRefs(jsURI));
458 NS_ENSURE_SUCCESS(rv, rv);
460 // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
461 mIOThunk = new nsJSThunk();
463 // Create a stock input stream channel...
464 // Remember, until AsyncOpen is called, the script will not be evaluated
465 // and the underlying Input Stream will not be created...
466 nsCOMPtr<nsIChannel> channel;
467 RefPtr<nsJSThunk> thunk = mIOThunk;
468 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
469 thunk.forget(), "text/html"_ns, ""_ns,
470 aLoadInfo);
471 NS_ENSURE_SUCCESS(rv, rv);
473 rv = mIOThunk->Init(aURI);
474 if (NS_SUCCEEDED(rv)) {
475 mStreamChannel = channel;
476 mPropertyBag = do_QueryInterface(channel);
477 nsCOMPtr<nsIWritablePropertyBag2> writableBag = do_QueryInterface(channel);
478 if (writableBag && jsURI->GetBaseURI()) {
479 writableBag->SetPropertyAsInterface(u"baseURI"_ns, jsURI->GetBaseURI());
483 return rv;
486 NS_IMETHODIMP
487 nsJSChannel::GetIsDocument(bool* aIsDocument) {
488 return NS_GetIsDocumentChannel(this, aIsDocument);
492 // nsISupports implementation...
495 NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver,
496 nsIStreamListener, nsIScriptChannel, nsIPropertyBag,
497 nsIPropertyBag2)
500 // nsIRequest implementation...
503 NS_IMETHODIMP
504 nsJSChannel::GetName(nsACString& aResult) {
505 return mStreamChannel->GetName(aResult);
508 NS_IMETHODIMP
509 nsJSChannel::IsPending(bool* aResult) {
510 *aResult = mIsActive;
511 return NS_OK;
514 NS_IMETHODIMP
515 nsJSChannel::GetStatus(nsresult* aResult) {
516 if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) {
517 return mStreamChannel->GetStatus(aResult);
520 *aResult = mStatus;
522 return NS_OK;
525 NS_IMETHODIMP nsJSChannel::SetCanceledReason(const nsACString& aReason) {
526 return SetCanceledReasonImpl(aReason);
529 NS_IMETHODIMP nsJSChannel::GetCanceledReason(nsACString& aReason) {
530 return GetCanceledReasonImpl(aReason);
533 NS_IMETHODIMP nsJSChannel::CancelWithReason(nsresult aStatus,
534 const nsACString& aReason) {
535 return CancelWithReasonImpl(aStatus, aReason);
538 NS_IMETHODIMP
539 nsJSChannel::Cancel(nsresult aStatus) {
540 mStatus = aStatus;
542 if (mOpenedStreamChannel) {
543 mStreamChannel->Cancel(aStatus);
546 return NS_OK;
549 NS_IMETHODIMP
550 nsJSChannel::GetCanceled(bool* aCanceled) {
551 nsresult status = NS_ERROR_FAILURE;
552 GetStatus(&status);
553 *aCanceled = NS_FAILED(status);
554 return NS_OK;
557 NS_IMETHODIMP
558 nsJSChannel::Suspend() { return mStreamChannel->Suspend(); }
560 NS_IMETHODIMP
561 nsJSChannel::Resume() { return mStreamChannel->Resume(); }
564 // nsIChannel implementation
567 NS_IMETHODIMP
568 nsJSChannel::GetOriginalURI(nsIURI** aURI) {
569 return mStreamChannel->GetOriginalURI(aURI);
572 NS_IMETHODIMP
573 nsJSChannel::SetOriginalURI(nsIURI* aURI) {
574 return mStreamChannel->SetOriginalURI(aURI);
577 NS_IMETHODIMP
578 nsJSChannel::GetURI(nsIURI** aURI) { return mStreamChannel->GetURI(aURI); }
580 NS_IMETHODIMP
581 nsJSChannel::Open(nsIInputStream** aStream) {
582 nsCOMPtr<nsIStreamListener> listener;
583 nsresult rv =
584 nsContentSecurityManager::doContentSecurityCheck(this, listener);
585 NS_ENSURE_SUCCESS(rv, rv);
587 rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, mExecutionPolicy,
588 mOriginalInnerWindow);
589 NS_ENSURE_SUCCESS(rv, rv);
591 return mStreamChannel->Open(aStream);
594 NS_IMETHODIMP
595 nsJSChannel::AsyncOpen(nsIStreamListener* aListener) {
596 NS_ENSURE_ARG(aListener);
598 nsCOMPtr<nsIStreamListener> listener = aListener;
599 nsresult rv =
600 nsContentSecurityManager::doContentSecurityCheck(this, listener);
601 NS_ENSURE_SUCCESS(rv, rv);
603 #ifdef DEBUG
605 nsCOMPtr<nsILoadInfo> loadInfo = nsIChannel::LoadInfo();
606 MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 ||
607 loadInfo->GetInitialSecurityCheckDone(),
608 "security flags in loadInfo but asyncOpen() not called");
610 #endif
612 // First make sure that we have a usable inner window; we'll want to make
613 // sure that we execute against that inner and no other.
614 nsIScriptGlobalObject* global = GetGlobalObject(this);
615 if (!global) {
616 return NS_ERROR_NOT_AVAILABLE;
619 nsCOMPtr<nsPIDOMWindowOuter> win(do_QueryInterface(global));
620 NS_ASSERTION(win, "Our global is not a window??");
622 // Make sure we create a new inner window if one doesn't already exist (see
623 // bug 306630).
624 mOriginalInnerWindow = win->EnsureInnerWindow();
625 if (!mOriginalInnerWindow) {
626 return NS_ERROR_NOT_AVAILABLE;
629 mListener = aListener;
631 mIsActive = true;
633 // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
634 // notifications (and hence nsIWebProgressListener notifications) from
635 // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI,
636 // which means that the DocLoader would not generate document start and
637 // stop notifications (see bug 257875).
638 mActualLoadFlags = mLoadFlags;
639 mLoadFlags |= LOAD_BACKGROUND;
641 // Add the javascript channel to its loadgroup so that we know if
642 // network loads were canceled or not...
643 nsCOMPtr<nsILoadGroup> loadGroup;
644 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
645 if (loadGroup) {
646 rv = loadGroup->AddRequest(this, nullptr);
647 if (NS_FAILED(rv)) {
648 mIsActive = false;
649 CleanupStrongRefs();
650 return rv;
654 mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc();
655 if (mDocumentOnloadBlockedOn) {
656 // If we're a document channel, we need to actually block onload on our
657 // _parent_ document. This is because we don't actually set our
658 // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the
659 // document channel will claim to not be busy, and our parent's onload
660 // could fire too early.
661 nsLoadFlags loadFlags;
662 mStreamChannel->GetLoadFlags(&loadFlags);
663 if (loadFlags & LOAD_DOCUMENT_URI) {
664 mDocumentOnloadBlockedOn =
665 mDocumentOnloadBlockedOn->GetInProcessParentDocument();
668 if (mDocumentOnloadBlockedOn) {
669 mDocumentOnloadBlockedOn->BlockOnload();
672 mPopupState = mozilla::dom::PopupBlocker::GetPopupControlState();
674 void (nsJSChannel::*method)();
675 const char* name;
676 if (mIsAsync) {
677 // post an event to do the rest
678 method = &nsJSChannel::EvaluateScript;
679 name = "nsJSChannel::EvaluateScript";
680 } else {
681 EvaluateScript();
682 if (mOpenedStreamChannel) {
683 // That will handle notifying things
684 return NS_OK;
687 NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_");
689 // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't
690 // have any content resulting from the execution and NS_BINDING_ABORTED
691 // if something we did causes our own load to be stopped. Return
692 // success in those cases, and error out in all others.
693 if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED &&
694 mStatus != NS_BINDING_ABORTED) {
695 // Note that calling EvaluateScript() handled removing us from the
696 // loadgroup and marking us as not active anymore.
697 CleanupStrongRefs();
698 return mStatus;
701 // We're returning success from asyncOpen(), but we didn't open a
702 // stream channel. We'll have to notify ourselves, but make sure to do
703 // it asynchronously.
704 method = &nsJSChannel::NotifyListener;
705 name = "nsJSChannel::NotifyListener";
708 nsCOMPtr<nsIRunnable> runnable =
709 mozilla::NewRunnableMethod(name, this, method);
710 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(mOriginalInnerWindow);
711 rv = window->Dispatch(mozilla::TaskCategory::Other, runnable.forget());
713 if (NS_FAILED(rv)) {
714 loadGroup->RemoveRequest(this, nullptr, rv);
715 mIsActive = false;
716 CleanupStrongRefs();
718 return rv;
721 void nsJSChannel::EvaluateScript() {
722 // Synchronously execute the script...
723 // mIsActive is used to indicate the the request is 'busy' during the
724 // the script evaluation phase. This means that IsPending() will
725 // indicate the the request is busy while the script is executing...
727 // Note that we want to be in the loadgroup and pending while we evaluate
728 // the script, so that we find out if the loadgroup gets canceled by the
729 // script execution (in which case we shouldn't pump out data even if the
730 // script returns it).
732 if (NS_SUCCEEDED(mStatus)) {
733 nsresult rv = mIOThunk->EvaluateScript(
734 mStreamChannel, mPopupState, mExecutionPolicy, mOriginalInnerWindow);
736 // Note that evaluation may have canceled us, so recheck mStatus again
737 if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) {
738 mStatus = rv;
742 // Remove the javascript channel from its loadgroup...
743 nsCOMPtr<nsILoadGroup> loadGroup;
744 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
745 if (loadGroup) {
746 loadGroup->RemoveRequest(this, nullptr, mStatus);
749 // Reset load flags to their original value...
750 mLoadFlags = mActualLoadFlags;
752 // We're no longer active, it's now up to the stream channel to do
753 // the loading, if needed.
754 mIsActive = false;
756 if (NS_FAILED(mStatus)) {
757 if (mIsAsync) {
758 NotifyListener();
760 return;
763 // EvaluateScript() succeeded, and we were not canceled, that
764 // means there's data to parse as a result of evaluating the
765 // script.
767 // Get the stream channels load flags (!= mLoadFlags).
768 nsLoadFlags loadFlags;
769 mStreamChannel->GetLoadFlags(&loadFlags);
771 uint32_t disposition;
772 if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition)))
773 disposition = nsIChannel::DISPOSITION_INLINE;
774 if (loadFlags & LOAD_DOCUMENT_URI &&
775 disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
776 // We're loaded as the document channel and not expecting to download
777 // the result. If we go on, we'll blow away the current document. Make
778 // sure that's ok. If so, stop all pending network loads.
780 nsCOMPtr<nsIDocShell> docShell;
781 NS_QueryNotificationCallbacks(mStreamChannel, docShell);
782 if (docShell) {
783 nsCOMPtr<nsIContentViewer> cv;
784 docShell->GetContentViewer(getter_AddRefs(cv));
786 if (cv) {
787 bool okToUnload;
789 if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
790 // The user didn't want to unload the current
791 // page, translate this into an undefined
792 // return from the javascript: URL...
793 mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED;
798 if (NS_SUCCEEDED(mStatus)) {
799 mStatus = StopAll();
803 if (NS_FAILED(mStatus)) {
804 if (mIsAsync) {
805 NotifyListener();
807 return;
810 mStatus = mStreamChannel->AsyncOpen(this);
811 if (NS_SUCCEEDED(mStatus)) {
812 // mStreamChannel will call OnStartRequest and OnStopRequest on
813 // us, so we'll be sure to call them on our listener.
814 mOpenedStreamChannel = true;
816 // Now readd ourselves to the loadgroup so we can receive
817 // cancellation notifications.
818 mIsActive = true;
819 if (loadGroup) {
820 mStatus = loadGroup->AddRequest(this, nullptr);
822 // If AddRequest failed, that's OK. The key is to make sure we get
823 // cancelled if needed, and that call just canceled us if it
824 // failed. We'll still get notified by the stream channel when it
825 // finishes.
828 } else if (mIsAsync) {
829 NotifyListener();
833 void nsJSChannel::NotifyListener() {
834 mListener->OnStartRequest(this);
835 mListener->OnStopRequest(this, mStatus);
837 CleanupStrongRefs();
840 void nsJSChannel::CleanupStrongRefs() {
841 mListener = nullptr;
842 mOriginalInnerWindow = nullptr;
843 if (mDocumentOnloadBlockedOn) {
844 mDocumentOnloadBlockedOn->UnblockOnload(false);
845 mDocumentOnloadBlockedOn = nullptr;
849 NS_IMETHODIMP
850 nsJSChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
851 *aLoadFlags = mLoadFlags;
853 return NS_OK;
856 NS_IMETHODIMP
857 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
858 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
859 // actually right.
860 bool bogusLoadBackground = false;
861 if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) &&
862 (aLoadFlags & LOAD_BACKGROUND)) {
863 // We're getting a LOAD_BACKGROUND, but it's probably just our own fake
864 // flag being mirrored to us. The one exception is if our loadgroup is
865 // LOAD_BACKGROUND.
866 bool loadGroupIsBackground = false;
867 nsCOMPtr<nsILoadGroup> loadGroup;
868 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
869 if (loadGroup) {
870 nsLoadFlags loadGroupFlags;
871 loadGroup->GetLoadFlags(&loadGroupFlags);
872 loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0);
874 bogusLoadBackground = !loadGroupIsBackground;
877 // Since the javascript channel is never the actual channel that
878 // any data is loaded through, don't ever set the
879 // LOAD_DOCUMENT_URI flag on it, since that could lead to two
880 // 'document channels' in the loadgroup if a javascript: URL is
881 // loaded while a document is being loaded in the same window.
883 // XXXbz this, and a whole lot of other hackery, could go away if we'd just
884 // cancel the current document load on javascript: load start like IE does.
886 mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
888 if (bogusLoadBackground) {
889 aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND;
892 mActualLoadFlags = aLoadFlags;
894 // ... but the underlying stream channel should get this bit, if
895 // set, since that'll be the real document channel if the
896 // javascript: URL generated data.
898 return mStreamChannel->SetLoadFlags(aLoadFlags);
901 NS_IMETHODIMP
902 nsJSChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
903 return GetTRRModeImpl(aTRRMode);
906 NS_IMETHODIMP
907 nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
908 return SetTRRModeImpl(aTRRMode);
911 NS_IMETHODIMP
912 nsJSChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
913 return mStreamChannel->GetLoadGroup(aLoadGroup);
916 NS_IMETHODIMP
917 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
918 if (aLoadGroup) {
919 bool streamPending;
920 nsresult rv = mStreamChannel->IsPending(&streamPending);
921 NS_ENSURE_SUCCESS(rv, rv);
923 if (streamPending) {
924 nsCOMPtr<nsILoadGroup> curLoadGroup;
925 mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup));
927 if (aLoadGroup != curLoadGroup) {
928 // Move the stream channel to our new loadgroup. Make sure to
929 // add it before removing it, so that we don't trigger onload
930 // by accident.
931 aLoadGroup->AddRequest(mStreamChannel, nullptr);
932 if (curLoadGroup) {
933 curLoadGroup->RemoveRequest(mStreamChannel, nullptr,
934 NS_BINDING_RETARGETED);
940 return mStreamChannel->SetLoadGroup(aLoadGroup);
943 NS_IMETHODIMP
944 nsJSChannel::GetOwner(nsISupports** aOwner) {
945 return mStreamChannel->GetOwner(aOwner);
948 NS_IMETHODIMP
949 nsJSChannel::SetOwner(nsISupports* aOwner) {
950 return mStreamChannel->SetOwner(aOwner);
953 NS_IMETHODIMP
954 nsJSChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
955 return mStreamChannel->GetLoadInfo(aLoadInfo);
958 NS_IMETHODIMP
959 nsJSChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
960 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
961 return mStreamChannel->SetLoadInfo(aLoadInfo);
964 NS_IMETHODIMP
965 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
966 return mStreamChannel->GetNotificationCallbacks(aCallbacks);
969 NS_IMETHODIMP
970 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
971 return mStreamChannel->SetNotificationCallbacks(aCallbacks);
974 NS_IMETHODIMP
975 nsJSChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
976 return mStreamChannel->GetSecurityInfo(aSecurityInfo);
979 NS_IMETHODIMP
980 nsJSChannel::GetContentType(nsACString& aContentType) {
981 return mStreamChannel->GetContentType(aContentType);
984 NS_IMETHODIMP
985 nsJSChannel::SetContentType(const nsACString& aContentType) {
986 return mStreamChannel->SetContentType(aContentType);
989 NS_IMETHODIMP
990 nsJSChannel::GetContentCharset(nsACString& aContentCharset) {
991 return mStreamChannel->GetContentCharset(aContentCharset);
994 NS_IMETHODIMP
995 nsJSChannel::SetContentCharset(const nsACString& aContentCharset) {
996 return mStreamChannel->SetContentCharset(aContentCharset);
999 NS_IMETHODIMP
1000 nsJSChannel::GetContentDisposition(uint32_t* aContentDisposition) {
1001 return mStreamChannel->GetContentDisposition(aContentDisposition);
1004 NS_IMETHODIMP
1005 nsJSChannel::SetContentDisposition(uint32_t aContentDisposition) {
1006 return mStreamChannel->SetContentDisposition(aContentDisposition);
1009 NS_IMETHODIMP
1010 nsJSChannel::GetContentDispositionFilename(
1011 nsAString& aContentDispositionFilename) {
1012 return mStreamChannel->GetContentDispositionFilename(
1013 aContentDispositionFilename);
1016 NS_IMETHODIMP
1017 nsJSChannel::SetContentDispositionFilename(
1018 const nsAString& aContentDispositionFilename) {
1019 return mStreamChannel->SetContentDispositionFilename(
1020 aContentDispositionFilename);
1023 NS_IMETHODIMP
1024 nsJSChannel::GetContentDispositionHeader(
1025 nsACString& aContentDispositionHeader) {
1026 return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader);
1029 NS_IMETHODIMP
1030 nsJSChannel::GetContentLength(int64_t* aContentLength) {
1031 return mStreamChannel->GetContentLength(aContentLength);
1034 NS_IMETHODIMP
1035 nsJSChannel::SetContentLength(int64_t aContentLength) {
1036 return mStreamChannel->SetContentLength(aContentLength);
1039 NS_IMETHODIMP
1040 nsJSChannel::OnStartRequest(nsIRequest* aRequest) {
1041 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1043 return mListener->OnStartRequest(this);
1046 NS_IMETHODIMP
1047 nsJSChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1048 uint64_t aOffset, uint32_t aCount) {
1049 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1051 return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount);
1054 NS_IMETHODIMP
1055 nsJSChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
1056 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1058 nsCOMPtr<nsIStreamListener> listener = mListener;
1060 CleanupStrongRefs();
1062 // Make sure aStatus matches what GetStatus() returns
1063 if (NS_FAILED(mStatus)) {
1064 aStatus = mStatus;
1067 nsresult rv = listener->OnStopRequest(this, aStatus);
1069 nsCOMPtr<nsILoadGroup> loadGroup;
1070 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1071 if (loadGroup) {
1072 loadGroup->RemoveRequest(this, nullptr, mStatus);
1075 mIsActive = false;
1077 return rv;
1080 NS_IMETHODIMP
1081 nsJSChannel::SetExecutionPolicy(uint32_t aPolicy) {
1082 NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL);
1084 mExecutionPolicy = aPolicy;
1085 return NS_OK;
1088 NS_IMETHODIMP
1089 nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy) {
1090 *aPolicy = mExecutionPolicy;
1091 return NS_OK;
1094 NS_IMETHODIMP
1095 nsJSChannel::SetExecuteAsync(bool aIsAsync) {
1096 if (!mIsActive) {
1097 mIsAsync = aIsAsync;
1099 // else ignore this call
1100 NS_WARNING_ASSERTION(!mIsActive,
1101 "Calling SetExecuteAsync on active channel?");
1103 return NS_OK;
1106 NS_IMETHODIMP
1107 nsJSChannel::GetExecuteAsync(bool* aIsAsync) {
1108 *aIsAsync = mIsAsync;
1109 return NS_OK;
1112 bool nsJSChannel::GetIsDocumentLoad() {
1113 // Our LOAD_DOCUMENT_URI flag, if any, lives on our stream channel.
1114 nsLoadFlags flags;
1115 mStreamChannel->GetLoadFlags(&flags);
1116 return flags & LOAD_DOCUMENT_URI;
1119 ////////////////////////////////////////////////////////////////////////////////
1121 nsJSProtocolHandler::nsJSProtocolHandler() = default;
1123 nsJSProtocolHandler::~nsJSProtocolHandler() = default;
1125 NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler)
1127 /* static */ nsresult nsJSProtocolHandler::EnsureUTF8Spec(
1128 const nsCString& aSpec, const char* aCharset, nsACString& aUTF8Spec) {
1129 aUTF8Spec.Truncate();
1131 nsAutoString uStr;
1132 nsresult rv = nsTextToSubURI::UnEscapeNonAsciiURI(
1133 nsDependentCString(aCharset), aSpec, uStr);
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 if (!IsAscii(uStr)) {
1137 rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr),
1138 esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec,
1139 mozilla::fallible);
1140 NS_ENSURE_SUCCESS(rv, rv);
1143 return NS_OK;
1146 ////////////////////////////////////////////////////////////////////////////////
1147 // nsIProtocolHandler methods:
1149 NS_IMETHODIMP
1150 nsJSProtocolHandler::GetScheme(nsACString& result) {
1151 result = "javascript";
1152 return NS_OK;
1155 /* static */ nsresult nsJSProtocolHandler::CreateNewURI(const nsACString& aSpec,
1156 const char* aCharset,
1157 nsIURI* aBaseURI,
1158 nsIURI** result) {
1159 nsresult rv = NS_OK;
1161 // javascript: URLs (currently) have no additional structure beyond that
1162 // provided by standard URLs, so there is no "outer" object given to
1163 // CreateInstance.
1165 NS_MutateURI mutator(new nsJSURI::Mutator());
1166 nsCOMPtr<nsIURI> base(aBaseURI);
1167 mutator.Apply(&nsIJSURIMutator::SetBase, base);
1168 if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) {
1169 mutator.SetSpec(aSpec);
1170 } else {
1171 nsAutoCString utf8Spec;
1172 rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
1173 if (NS_FAILED(rv)) {
1174 return rv;
1176 if (utf8Spec.IsEmpty()) {
1177 mutator.SetSpec(aSpec);
1178 } else {
1179 mutator.SetSpec(utf8Spec);
1183 nsCOMPtr<nsIURI> url;
1184 rv = mutator.Finalize(url);
1185 if (NS_FAILED(rv)) {
1186 return rv;
1189 url.forget(result);
1190 return rv;
1193 NS_IMETHODIMP
1194 nsJSProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
1195 nsIChannel** result) {
1196 nsresult rv;
1198 NS_ENSURE_ARG_POINTER(uri);
1199 RefPtr<nsJSChannel> channel = new nsJSChannel();
1200 if (!channel) {
1201 return NS_ERROR_OUT_OF_MEMORY;
1204 rv = channel->Init(uri, aLoadInfo);
1205 NS_ENSURE_SUCCESS(rv, rv);
1207 if (NS_SUCCEEDED(rv)) {
1208 channel.forget(result);
1210 return rv;
1213 NS_IMETHODIMP
1214 nsJSProtocolHandler::AllowPort(int32_t port, const char* scheme,
1215 bool* _retval) {
1216 // don't override anything.
1217 *_retval = false;
1218 return NS_OK;
1221 ////////////////////////////////////////////////////////////
1222 // nsJSURI implementation
1223 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
1224 NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
1226 NS_IMPL_ADDREF_INHERITED(nsJSURI, mozilla::net::nsSimpleURI)
1227 NS_IMPL_RELEASE_INHERITED(nsJSURI, mozilla::net::nsSimpleURI)
1229 NS_IMPL_CLASSINFO(nsJSURI, nullptr, nsIClassInfo::THREADSAFE, NS_JSURI_CID);
1230 // Empty CI getter. We only need nsIClassInfo for Serialization
1231 NS_IMPL_CI_INTERFACE_GETTER0(nsJSURI)
1233 NS_INTERFACE_MAP_BEGIN(nsJSURI)
1234 if (aIID.Equals(kJSURICID))
1235 foundInterface = static_cast<nsIURI*>(this);
1236 else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
1237 // Need to return explicitly here, because if we just set foundInterface
1238 // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
1239 // nsSimplURI::QueryInterface and finding something for this CID.
1240 *aInstancePtr = nullptr;
1241 return NS_NOINTERFACE;
1242 } else
1243 NS_IMPL_QUERY_CLASSINFO(nsJSURI)
1244 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI)
1246 // nsISerializable methods:
1248 NS_IMETHODIMP
1249 nsJSURI::Read(nsIObjectInputStream* aStream) {
1250 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
1251 return NS_ERROR_NOT_IMPLEMENTED;
1254 nsresult nsJSURI::ReadPrivate(nsIObjectInputStream* aStream) {
1255 nsresult rv = mozilla::net::nsSimpleURI::ReadPrivate(aStream);
1256 if (NS_FAILED(rv)) return rv;
1258 bool haveBase;
1259 rv = aStream->ReadBoolean(&haveBase);
1260 if (NS_FAILED(rv)) return rv;
1262 if (haveBase) {
1263 nsCOMPtr<nsISupports> supports;
1264 rv = aStream->ReadObject(true, getter_AddRefs(supports));
1265 if (NS_FAILED(rv)) return rv;
1266 mBaseURI = do_QueryInterface(supports);
1269 return NS_OK;
1272 NS_IMETHODIMP
1273 nsJSURI::Write(nsIObjectOutputStream* aStream) {
1274 nsresult rv = mozilla::net::nsSimpleURI::Write(aStream);
1275 if (NS_FAILED(rv)) return rv;
1277 rv = aStream->WriteBoolean(mBaseURI != nullptr);
1278 if (NS_FAILED(rv)) return rv;
1280 if (mBaseURI) {
1281 rv = aStream->WriteObject(mBaseURI, true);
1282 if (NS_FAILED(rv)) return rv;
1285 return NS_OK;
1288 NS_IMETHODIMP_(void) nsJSURI::Serialize(mozilla::ipc::URIParams& aParams) {
1289 using namespace mozilla::ipc;
1291 JSURIParams jsParams;
1292 URIParams simpleParams;
1294 mozilla::net::nsSimpleURI::Serialize(simpleParams);
1296 jsParams.simpleParams() = simpleParams;
1297 if (mBaseURI) {
1298 SerializeURI(mBaseURI, jsParams.baseURI());
1299 } else {
1300 jsParams.baseURI() = mozilla::Nothing();
1303 aParams = jsParams;
1306 bool nsJSURI::Deserialize(const mozilla::ipc::URIParams& aParams) {
1307 using namespace mozilla::ipc;
1309 if (aParams.type() != URIParams::TJSURIParams) {
1310 NS_ERROR("Received unknown parameters from the other process!");
1311 return false;
1314 const JSURIParams& jsParams = aParams.get_JSURIParams();
1315 mozilla::net::nsSimpleURI::Deserialize(jsParams.simpleParams());
1317 if (jsParams.baseURI().isSome()) {
1318 mBaseURI = DeserializeURI(jsParams.baseURI().ref());
1319 } else {
1320 mBaseURI = nullptr;
1322 return true;
1325 // nsSimpleURI methods:
1326 /* virtual */ mozilla::net::nsSimpleURI* nsJSURI::StartClone(
1327 mozilla::net::nsSimpleURI::RefHandlingEnum refHandlingMode,
1328 const nsACString& newRef) {
1329 nsJSURI* url = new nsJSURI(mBaseURI);
1330 SetRefOnClone(url, refHandlingMode, newRef);
1331 return url;
1334 // Queries this list of interfaces. If none match, it queries mURI.
1335 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJSURI::Mutator, nsIURISetters, nsIURIMutator,
1336 nsISerializable, nsIJSURIMutator)
1338 NS_IMETHODIMP
1339 nsJSURI::Mutate(nsIURIMutator** aMutator) {
1340 RefPtr<nsJSURI::Mutator> mutator = new nsJSURI::Mutator();
1341 nsresult rv = mutator->InitFromURI(this);
1342 if (NS_FAILED(rv)) {
1343 return rv;
1345 mutator.forget(aMutator);
1346 return NS_OK;
1349 /* virtual */
1350 nsresult nsJSURI::EqualsInternal(
1351 nsIURI* aOther, mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
1352 bool* aResult) {
1353 NS_ENSURE_ARG_POINTER(aOther);
1354 MOZ_ASSERT(aResult, "null pointer for outparam");
1356 RefPtr<nsJSURI> otherJSURI;
1357 nsresult rv = aOther->QueryInterface(kJSURICID, getter_AddRefs(otherJSURI));
1358 if (NS_FAILED(rv)) {
1359 *aResult = false; // aOther is not a nsJSURI --> not equal.
1360 return NS_OK;
1363 // Compare the member data that our base class knows about.
1364 if (!mozilla::net::nsSimpleURI::EqualsInternal(otherJSURI,
1365 aRefHandlingMode)) {
1366 *aResult = false;
1367 return NS_OK;
1370 // Compare the piece of additional member data that we add to base class.
1371 nsIURI* otherBaseURI = otherJSURI->GetBaseURI();
1373 if (mBaseURI) {
1374 // (As noted in StartClone, we always honor refs on mBaseURI)
1375 return mBaseURI->Equals(otherBaseURI, aResult);
1378 *aResult = !otherBaseURI;
1379 return NS_OK;