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/. */
9 #include "js/Wrapper.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"
22 #include "nsIScriptContext.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsIPrincipal.h"
25 #include "nsIInterfaceRequestor.h"
26 #include "nsIInterfaceRequestorUtils.h"
27 #include "nsPIDOMWindow.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
{
67 NS_DECL_THREADSAFE_ISUPPORTS
68 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream
)
70 nsresult
Init(nsIURI
* uri
);
71 nsresult
EvaluateScript(
73 mozilla::dom::PopupBlocker::PopupControlState aPopupState
,
74 uint32_t aExecutionPolicy
, nsPIDOMWindowInner
* aOriginalInnerWindow
);
79 nsCOMPtr
<nsIInputStream
> mInnerStream
;
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
;
101 rv
= uri
->GetSpec(mURL
);
102 if (NS_FAILED(rv
)) return rv
;
107 static bool IsISO88591(const nsString
& aString
) {
108 for (nsString::const_char_iterator c
= aString
.BeginReading(),
109 c_end
= aString
.EndReading();
111 if (*c
> 255) return false;
116 static nsIScriptGlobalObject
* GetGlobalObject(nsIChannel
* aChannel
) {
117 // Get the global object owner from the channel
118 nsCOMPtr
<nsIDocShell
> docShell
;
119 NS_QueryNotificationCallbacks(aChannel
, docShell
);
121 NS_WARNING("Unable to get a docShell from the channel!");
125 // So far so good: get the script global from its docshell
126 nsIScriptGlobalObject
* global
= docShell
->GetScriptGlobalObject();
129 "Unable to get an nsIScriptGlobalObject from the "
134 static bool AllowedByCSP(nsIContentSecurityPolicy
* aCSP
,
135 const nsACString
& aJavaScriptURL
) {
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
148 // This means /type/ is "navigation" and /source string/ is the
149 // "navigation request’s current URL".
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;
156 aCSP
->GetAllowsInline(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
,
157 true, // aHasUnsafeHash
159 true, // aParserCreated
160 nullptr, // aElement,
161 nullptr, // nsICSPEventListener
162 NS_ConvertASCIItoUTF16(aJavaScriptURL
), // aContent
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();
191 // We're not going to be able to have a sane URL, so just don't run the
193 return NS_ERROR_DOM_RETVAL_UNDEFINED
;
195 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
196 loadInfo
->SetResultPrincipalURI(docURI
);
199 DOMSecurityMonitor::AuditUseOfJavaScriptURI(aChannel
);
202 // Get principal of code for execution
203 nsCOMPtr
<nsISupports
> owner
;
204 aChannel
->GetOwner(getter_AddRefs(owner
));
205 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(owner
);
207 if (loadInfo
->GetForceInheritPrincipal()) {
208 principal
= loadInfo
->FindPrincipalToInherit(aChannel
);
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
;
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
);
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
;
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
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
);
305 rv
= principal
->Subsumes(objectPrincipal
, &subsumes
);
306 if (NS_FAILED(rv
)) return rv
;
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
;
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
;
361 bytes
= ToNewUTF8String(result
, &bytesLen
);
362 charset
= &utf8Charset
;
364 aChannel
->SetContentCharset(*charset
);
366 rv
= NS_NewByteInputStream(getter_AddRefs(mInnerStream
),
367 mozilla::Span(bytes
, bytesLen
),
368 NS_ASSIGNMENT_ADOPT
);
370 rv
= NS_ERROR_OUT_OF_MEMORY
;
376 ////////////////////////////////////////////////////////////////////////////////
378 class nsJSChannel
: public nsIChannel
,
379 public nsIStreamListener
,
380 public nsIScriptChannel
,
381 public nsIPropertyBag2
{
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();
400 virtual ~nsJSChannel();
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
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
;
427 bool mOpenedStreamChannel
;
430 nsJSChannel::nsJSChannel()
432 mLoadFlags(LOAD_NORMAL
),
433 mActualLoadFlags(LOAD_NORMAL
),
434 mPopupState(mozilla::dom::PopupBlocker::openOverridden
),
435 mExecutionPolicy(NO_EXECUTION
),
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!");
449 rv
= webNav
->Stop(nsIWebNavigation::STOP_ALL
);
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
,
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());
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
,
500 // nsIRequest implementation...
504 nsJSChannel::GetName(nsACString
& aResult
) {
505 return mStreamChannel
->GetName(aResult
);
509 nsJSChannel::IsPending(bool* aResult
) {
510 *aResult
= mIsActive
;
515 nsJSChannel::GetStatus(nsresult
* aResult
) {
516 if (NS_SUCCEEDED(mStatus
) && mOpenedStreamChannel
) {
517 return mStreamChannel
->GetStatus(aResult
);
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
);
539 nsJSChannel::Cancel(nsresult aStatus
) {
542 if (mOpenedStreamChannel
) {
543 mStreamChannel
->Cancel(aStatus
);
550 nsJSChannel::GetCanceled(bool* aCanceled
) {
551 nsresult status
= NS_ERROR_FAILURE
;
553 *aCanceled
= NS_FAILED(status
);
558 nsJSChannel::Suspend() { return mStreamChannel
->Suspend(); }
561 nsJSChannel::Resume() { return mStreamChannel
->Resume(); }
564 // nsIChannel implementation
568 nsJSChannel::GetOriginalURI(nsIURI
** aURI
) {
569 return mStreamChannel
->GetOriginalURI(aURI
);
573 nsJSChannel::SetOriginalURI(nsIURI
* aURI
) {
574 return mStreamChannel
->SetOriginalURI(aURI
);
578 nsJSChannel::GetURI(nsIURI
** aURI
) { return mStreamChannel
->GetURI(aURI
); }
581 nsJSChannel::Open(nsIInputStream
** aStream
) {
582 nsCOMPtr
<nsIStreamListener
> listener
;
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
);
595 nsJSChannel::AsyncOpen(nsIStreamListener
* aListener
) {
596 NS_ENSURE_ARG(aListener
);
598 nsCOMPtr
<nsIStreamListener
> listener
= aListener
;
600 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
601 NS_ENSURE_SUCCESS(rv
, rv
);
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");
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);
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
624 mOriginalInnerWindow
= win
->EnsureInnerWindow();
625 if (!mOriginalInnerWindow
) {
626 return NS_ERROR_NOT_AVAILABLE
;
629 mListener
= aListener
;
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
));
646 rv
= loadGroup
->AddRequest(this, nullptr);
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
)();
677 // post an event to do the rest
678 method
= &nsJSChannel::EvaluateScript
;
679 name
= "nsJSChannel::EvaluateScript";
682 if (mOpenedStreamChannel
) {
683 // That will handle notifying things
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.
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());
714 loadGroup
->RemoveRequest(this, nullptr, 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
)) {
742 // Remove the javascript channel from its loadgroup...
743 nsCOMPtr
<nsILoadGroup
> loadGroup
;
744 mStreamChannel
->GetLoadGroup(getter_AddRefs(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.
756 if (NS_FAILED(mStatus
)) {
763 // EvaluateScript() succeeded, and we were not canceled, that
764 // means there's data to parse as a result of evaluating the
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
);
783 nsCOMPtr
<nsIContentViewer
> cv
;
784 docShell
->GetContentViewer(getter_AddRefs(cv
));
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
)) {
803 if (NS_FAILED(mStatus
)) {
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.
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
828 } else if (mIsAsync
) {
833 void nsJSChannel::NotifyListener() {
834 mListener
->OnStartRequest(this);
835 mListener
->OnStopRequest(this, mStatus
);
840 void nsJSChannel::CleanupStrongRefs() {
842 mOriginalInnerWindow
= nullptr;
843 if (mDocumentOnloadBlockedOn
) {
844 mDocumentOnloadBlockedOn
->UnblockOnload(false);
845 mDocumentOnloadBlockedOn
= nullptr;
850 nsJSChannel::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
851 *aLoadFlags
= mLoadFlags
;
857 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags
) {
858 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
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
866 bool loadGroupIsBackground
= false;
867 nsCOMPtr
<nsILoadGroup
> loadGroup
;
868 mStreamChannel
->GetLoadGroup(getter_AddRefs(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
);
902 nsJSChannel::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
903 return GetTRRModeImpl(aTRRMode
);
907 nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
908 return SetTRRModeImpl(aTRRMode
);
912 nsJSChannel::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
913 return mStreamChannel
->GetLoadGroup(aLoadGroup
);
917 nsJSChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
920 nsresult rv
= mStreamChannel
->IsPending(&streamPending
);
921 NS_ENSURE_SUCCESS(rv
, rv
);
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
931 aLoadGroup
->AddRequest(mStreamChannel
, nullptr);
933 curLoadGroup
->RemoveRequest(mStreamChannel
, nullptr,
934 NS_BINDING_RETARGETED
);
940 return mStreamChannel
->SetLoadGroup(aLoadGroup
);
944 nsJSChannel::GetOwner(nsISupports
** aOwner
) {
945 return mStreamChannel
->GetOwner(aOwner
);
949 nsJSChannel::SetOwner(nsISupports
* aOwner
) {
950 return mStreamChannel
->SetOwner(aOwner
);
954 nsJSChannel::GetLoadInfo(nsILoadInfo
** aLoadInfo
) {
955 return mStreamChannel
->GetLoadInfo(aLoadInfo
);
959 nsJSChannel::SetLoadInfo(nsILoadInfo
* aLoadInfo
) {
960 MOZ_RELEASE_ASSERT(aLoadInfo
, "loadinfo can't be null");
961 return mStreamChannel
->SetLoadInfo(aLoadInfo
);
965 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
966 return mStreamChannel
->GetNotificationCallbacks(aCallbacks
);
970 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
971 return mStreamChannel
->SetNotificationCallbacks(aCallbacks
);
975 nsJSChannel::GetSecurityInfo(nsITransportSecurityInfo
** aSecurityInfo
) {
976 return mStreamChannel
->GetSecurityInfo(aSecurityInfo
);
980 nsJSChannel::GetContentType(nsACString
& aContentType
) {
981 return mStreamChannel
->GetContentType(aContentType
);
985 nsJSChannel::SetContentType(const nsACString
& aContentType
) {
986 return mStreamChannel
->SetContentType(aContentType
);
990 nsJSChannel::GetContentCharset(nsACString
& aContentCharset
) {
991 return mStreamChannel
->GetContentCharset(aContentCharset
);
995 nsJSChannel::SetContentCharset(const nsACString
& aContentCharset
) {
996 return mStreamChannel
->SetContentCharset(aContentCharset
);
1000 nsJSChannel::GetContentDisposition(uint32_t* aContentDisposition
) {
1001 return mStreamChannel
->GetContentDisposition(aContentDisposition
);
1005 nsJSChannel::SetContentDisposition(uint32_t aContentDisposition
) {
1006 return mStreamChannel
->SetContentDisposition(aContentDisposition
);
1010 nsJSChannel::GetContentDispositionFilename(
1011 nsAString
& aContentDispositionFilename
) {
1012 return mStreamChannel
->GetContentDispositionFilename(
1013 aContentDispositionFilename
);
1017 nsJSChannel::SetContentDispositionFilename(
1018 const nsAString
& aContentDispositionFilename
) {
1019 return mStreamChannel
->SetContentDispositionFilename(
1020 aContentDispositionFilename
);
1024 nsJSChannel::GetContentDispositionHeader(
1025 nsACString
& aContentDispositionHeader
) {
1026 return mStreamChannel
->GetContentDispositionHeader(aContentDispositionHeader
);
1030 nsJSChannel::GetContentLength(int64_t* aContentLength
) {
1031 return mStreamChannel
->GetContentLength(aContentLength
);
1035 nsJSChannel::SetContentLength(int64_t aContentLength
) {
1036 return mStreamChannel
->SetContentLength(aContentLength
);
1040 nsJSChannel::OnStartRequest(nsIRequest
* aRequest
) {
1041 NS_ENSURE_TRUE(aRequest
== mStreamChannel
, NS_ERROR_UNEXPECTED
);
1043 return mListener
->OnStartRequest(this);
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
);
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
)) {
1067 nsresult rv
= listener
->OnStopRequest(this, aStatus
);
1069 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1070 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1072 loadGroup
->RemoveRequest(this, nullptr, mStatus
);
1081 nsJSChannel::SetExecutionPolicy(uint32_t aPolicy
) {
1082 NS_ENSURE_ARG(aPolicy
<= EXECUTE_NORMAL
);
1084 mExecutionPolicy
= aPolicy
;
1089 nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy
) {
1090 *aPolicy
= mExecutionPolicy
;
1095 nsJSChannel::SetExecuteAsync(bool aIsAsync
) {
1097 mIsAsync
= aIsAsync
;
1099 // else ignore this call
1100 NS_WARNING_ASSERTION(!mIsActive
,
1101 "Calling SetExecuteAsync on active channel?");
1107 nsJSChannel::GetExecuteAsync(bool* aIsAsync
) {
1108 *aIsAsync
= mIsAsync
;
1112 bool nsJSChannel::GetIsDocumentLoad() {
1113 // Our LOAD_DOCUMENT_URI flag, if any, lives on our stream channel.
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();
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
,
1140 NS_ENSURE_SUCCESS(rv
, rv
);
1146 ////////////////////////////////////////////////////////////////////////////////
1147 // nsIProtocolHandler methods:
1150 nsJSProtocolHandler::GetScheme(nsACString
& result
) {
1151 result
= "javascript";
1155 /* static */ nsresult
nsJSProtocolHandler::CreateNewURI(const nsACString
& aSpec
,
1156 const char* aCharset
,
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
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
);
1171 nsAutoCString utf8Spec
;
1172 rv
= EnsureUTF8Spec(PromiseFlatCString(aSpec
), aCharset
, utf8Spec
);
1173 if (NS_FAILED(rv
)) {
1176 if (utf8Spec
.IsEmpty()) {
1177 mutator
.SetSpec(aSpec
);
1179 mutator
.SetSpec(utf8Spec
);
1183 nsCOMPtr
<nsIURI
> url
;
1184 rv
= mutator
.Finalize(url
);
1185 if (NS_FAILED(rv
)) {
1194 nsJSProtocolHandler::NewChannel(nsIURI
* uri
, nsILoadInfo
* aLoadInfo
,
1195 nsIChannel
** result
) {
1198 NS_ENSURE_ARG_POINTER(uri
);
1199 RefPtr
<nsJSChannel
> channel
= new nsJSChannel();
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
);
1214 nsJSProtocolHandler::AllowPort(int32_t port
, const char* scheme
,
1216 // don't override anything.
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
;
1243 NS_IMPL_QUERY_CLASSINFO(nsJSURI
)
1244 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI
)
1246 // nsISerializable methods:
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
;
1259 rv
= aStream
->ReadBoolean(&haveBase
);
1260 if (NS_FAILED(rv
)) return rv
;
1263 nsCOMPtr
<nsISupports
> supports
;
1264 rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
1265 if (NS_FAILED(rv
)) return rv
;
1266 mBaseURI
= do_QueryInterface(supports
);
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
;
1281 rv
= aStream
->WriteObject(mBaseURI
, true);
1282 if (NS_FAILED(rv
)) return rv
;
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
;
1298 SerializeURI(mBaseURI
, jsParams
.baseURI());
1300 jsParams
.baseURI() = mozilla::Nothing();
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!");
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());
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
);
1334 // Queries this list of interfaces. If none match, it queries mURI.
1335 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJSURI::Mutator
, nsIURISetters
, nsIURIMutator
,
1336 nsISerializable
, nsIJSURIMutator
)
1339 nsJSURI::Mutate(nsIURIMutator
** aMutator
) {
1340 RefPtr
<nsJSURI::Mutator
> mutator
= new nsJSURI::Mutator();
1341 nsresult rv
= mutator
->InitFromURI(this);
1342 if (NS_FAILED(rv
)) {
1345 mutator
.forget(aMutator
);
1350 nsresult
nsJSURI::EqualsInternal(
1351 nsIURI
* aOther
, mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode
,
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.
1363 // Compare the member data that our base class knows about.
1364 if (!mozilla::net::nsSimpleURI::EqualsInternal(otherJSURI
,
1365 aRefHandlingMode
)) {
1370 // Compare the piece of additional member data that we add to base class.
1371 nsIURI
* otherBaseURI
= otherJSURI
->GetBaseURI();
1374 // (As noted in StartClone, we always honor refs on mBaseURI)
1375 return mBaseURI
->Equals(otherBaseURI
, aResult
);
1378 *aResult
= !otherBaseURI
;