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 "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
{
68 NS_DECL_THREADSAFE_ISUPPORTS
69 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream
)
71 nsresult
Init(nsIURI
* uri
);
72 nsresult
EvaluateScript(
74 mozilla::dom::PopupBlocker::PopupControlState aPopupState
,
75 uint32_t aExecutionPolicy
, nsPIDOMWindowInner
* aOriginalInnerWindow
);
80 nsCOMPtr
<nsIInputStream
> mInnerStream
;
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
;
102 rv
= uri
->GetSpec(mURL
);
103 if (NS_FAILED(rv
)) return rv
;
108 static bool IsISO88591(const nsString
& aString
) {
109 for (nsString::const_char_iterator c
= aString
.BeginReading(),
110 c_end
= aString
.EndReading();
112 if (*c
> 255) return false;
117 static nsIScriptGlobalObject
* GetGlobalObject(nsIChannel
* aChannel
) {
118 // Get the global object owner from the channel
119 nsCOMPtr
<nsIDocShell
> docShell
;
120 NS_QueryNotificationCallbacks(aChannel
, docShell
);
122 NS_WARNING("Unable to get a docShell from the channel!");
126 // So far so good: get the script global from its docshell
127 nsIScriptGlobalObject
* global
= docShell
->GetScriptGlobalObject();
130 "Unable to get an nsIScriptGlobalObject from the "
135 static bool AllowedByCSP(nsIContentSecurityPolicy
* aCSP
,
136 const nsACString
& aJavaScriptURL
) {
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
149 // This means /type/ is "navigation" and /source string/ is the
150 // "navigation request’s current URL".
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;
157 aCSP
->GetAllowsInline(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
,
158 true, // aHasUnsafeHash
160 true, // aParserCreated
161 nullptr, // aElement,
162 nullptr, // nsICSPEventListener
163 NS_ConvertASCIItoUTF16(aJavaScriptURL
), // aContent
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();
192 // We're not going to be able to have a sane URL, so just don't run the
194 return NS_ERROR_DOM_RETVAL_UNDEFINED
;
196 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
197 loadInfo
->SetResultPrincipalURI(docURI
);
200 DOMSecurityMonitor::AuditUseOfJavaScriptURI(aChannel
);
203 // Get principal of code for execution
204 nsCOMPtr
<nsISupports
> owner
;
205 aChannel
->GetOwner(getter_AddRefs(owner
));
206 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(owner
);
208 if (loadInfo
->GetForceInheritPrincipal()) {
209 principal
= loadInfo
->FindPrincipalToInherit(aChannel
);
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
;
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
);
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
;
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
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
);
306 rv
= principal
->Subsumes(objectPrincipal
, &subsumes
);
307 if (NS_FAILED(rv
)) return rv
;
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
;
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
;
362 bytes
= ToNewUTF8String(result
, &bytesLen
);
363 charset
= &utf8Charset
;
365 aChannel
->SetContentCharset(*charset
);
367 rv
= NS_NewByteInputStream(getter_AddRefs(mInnerStream
),
368 mozilla::Span(bytes
, bytesLen
),
369 NS_ASSIGNMENT_ADOPT
);
371 rv
= NS_ERROR_OUT_OF_MEMORY
;
377 ////////////////////////////////////////////////////////////////////////////////
379 class nsJSChannel
: public nsIChannel
,
380 public nsIStreamListener
,
381 public nsIScriptChannel
,
382 public nsIPropertyBag2
{
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();
401 virtual ~nsJSChannel();
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
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
;
428 bool mOpenedStreamChannel
;
431 nsJSChannel::nsJSChannel()
433 mLoadFlags(LOAD_NORMAL
),
434 mActualLoadFlags(LOAD_NORMAL
),
435 mPopupState(mozilla::dom::PopupBlocker::openOverridden
),
436 mExecutionPolicy(NO_EXECUTION
),
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!");
450 rv
= webNav
->Stop(nsIWebNavigation::STOP_ALL
);
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
,
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());
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
,
501 // nsIRequest implementation...
505 nsJSChannel::GetName(nsACString
& aResult
) {
506 return mStreamChannel
->GetName(aResult
);
510 nsJSChannel::IsPending(bool* aResult
) {
511 *aResult
= mIsActive
;
516 nsJSChannel::GetStatus(nsresult
* aResult
) {
517 if (NS_SUCCEEDED(mStatus
) && mOpenedStreamChannel
) {
518 return mStreamChannel
->GetStatus(aResult
);
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
);
540 nsJSChannel::Cancel(nsresult aStatus
) {
543 if (mOpenedStreamChannel
) {
544 mStreamChannel
->Cancel(aStatus
);
551 nsJSChannel::GetCanceled(bool* aCanceled
) {
552 nsresult status
= NS_ERROR_FAILURE
;
554 *aCanceled
= NS_FAILED(status
);
559 nsJSChannel::Suspend() { return mStreamChannel
->Suspend(); }
562 nsJSChannel::Resume() { return mStreamChannel
->Resume(); }
565 // nsIChannel implementation
569 nsJSChannel::GetOriginalURI(nsIURI
** aURI
) {
570 return mStreamChannel
->GetOriginalURI(aURI
);
574 nsJSChannel::SetOriginalURI(nsIURI
* aURI
) {
575 return mStreamChannel
->SetOriginalURI(aURI
);
579 nsJSChannel::GetURI(nsIURI
** aURI
) { return mStreamChannel
->GetURI(aURI
); }
582 nsJSChannel::Open(nsIInputStream
** aStream
) {
583 nsCOMPtr
<nsIStreamListener
> listener
;
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
);
596 nsJSChannel::AsyncOpen(nsIStreamListener
* aListener
) {
597 NS_ENSURE_ARG(aListener
);
599 nsCOMPtr
<nsIStreamListener
> listener
= aListener
;
601 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
602 NS_ENSURE_SUCCESS(rv
, rv
);
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");
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);
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
625 mOriginalInnerWindow
= win
->EnsureInnerWindow();
626 if (!mOriginalInnerWindow
) {
627 return NS_ERROR_NOT_AVAILABLE
;
630 mListener
= aListener
;
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
));
647 rv
= loadGroup
->AddRequest(this, nullptr);
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
)();
678 // post an event to do the rest
679 method
= &nsJSChannel::EvaluateScript
;
680 name
= "nsJSChannel::EvaluateScript";
683 if (mOpenedStreamChannel
) {
684 // That will handle notifying things
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.
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());
715 loadGroup
->RemoveRequest(this, nullptr, 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
)) {
743 // Remove the javascript channel from its loadgroup...
744 nsCOMPtr
<nsILoadGroup
> loadGroup
;
745 mStreamChannel
->GetLoadGroup(getter_AddRefs(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.
757 if (NS_FAILED(mStatus
)) {
764 // EvaluateScript() succeeded, and we were not canceled, that
765 // means there's data to parse as a result of evaluating the
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
);
784 nsCOMPtr
<nsIDocumentViewer
> viewer
;
785 docShell
->GetDocViewer(getter_AddRefs(viewer
));
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
)) {
804 if (NS_FAILED(mStatus
)) {
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.
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
829 } else if (mIsAsync
) {
834 void nsJSChannel::NotifyListener() {
835 mListener
->OnStartRequest(this);
836 mListener
->OnStopRequest(this, mStatus
);
841 void nsJSChannel::CleanupStrongRefs() {
843 mOriginalInnerWindow
= nullptr;
844 if (mDocumentOnloadBlockedOn
) {
845 mDocumentOnloadBlockedOn
->UnblockOnload(false);
846 mDocumentOnloadBlockedOn
= nullptr;
851 nsJSChannel::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
852 *aLoadFlags
= mLoadFlags
;
858 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags
) {
859 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
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
867 bool loadGroupIsBackground
= false;
868 nsCOMPtr
<nsILoadGroup
> loadGroup
;
869 mStreamChannel
->GetLoadGroup(getter_AddRefs(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
);
903 nsJSChannel::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
904 return GetTRRModeImpl(aTRRMode
);
908 nsJSChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
909 return SetTRRModeImpl(aTRRMode
);
913 nsJSChannel::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
914 return mStreamChannel
->GetLoadGroup(aLoadGroup
);
918 nsJSChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
921 nsresult rv
= mStreamChannel
->IsPending(&streamPending
);
922 NS_ENSURE_SUCCESS(rv
, rv
);
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
932 aLoadGroup
->AddRequest(mStreamChannel
, nullptr);
934 curLoadGroup
->RemoveRequest(mStreamChannel
, nullptr,
935 NS_BINDING_RETARGETED
);
941 return mStreamChannel
->SetLoadGroup(aLoadGroup
);
945 nsJSChannel::GetOwner(nsISupports
** aOwner
) {
946 return mStreamChannel
->GetOwner(aOwner
);
950 nsJSChannel::SetOwner(nsISupports
* aOwner
) {
951 return mStreamChannel
->SetOwner(aOwner
);
955 nsJSChannel::GetLoadInfo(nsILoadInfo
** aLoadInfo
) {
956 return mStreamChannel
->GetLoadInfo(aLoadInfo
);
960 nsJSChannel::SetLoadInfo(nsILoadInfo
* aLoadInfo
) {
961 MOZ_RELEASE_ASSERT(aLoadInfo
, "loadinfo can't be null");
962 return mStreamChannel
->SetLoadInfo(aLoadInfo
);
966 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
967 return mStreamChannel
->GetNotificationCallbacks(aCallbacks
);
971 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
972 return mStreamChannel
->SetNotificationCallbacks(aCallbacks
);
976 nsJSChannel::GetSecurityInfo(nsITransportSecurityInfo
** aSecurityInfo
) {
977 return mStreamChannel
->GetSecurityInfo(aSecurityInfo
);
981 nsJSChannel::GetContentType(nsACString
& aContentType
) {
982 return mStreamChannel
->GetContentType(aContentType
);
986 nsJSChannel::SetContentType(const nsACString
& aContentType
) {
987 return mStreamChannel
->SetContentType(aContentType
);
991 nsJSChannel::GetContentCharset(nsACString
& aContentCharset
) {
992 return mStreamChannel
->GetContentCharset(aContentCharset
);
996 nsJSChannel::SetContentCharset(const nsACString
& aContentCharset
) {
997 return mStreamChannel
->SetContentCharset(aContentCharset
);
1001 nsJSChannel::GetContentDisposition(uint32_t* aContentDisposition
) {
1002 return mStreamChannel
->GetContentDisposition(aContentDisposition
);
1006 nsJSChannel::SetContentDisposition(uint32_t aContentDisposition
) {
1007 return mStreamChannel
->SetContentDisposition(aContentDisposition
);
1011 nsJSChannel::GetContentDispositionFilename(
1012 nsAString
& aContentDispositionFilename
) {
1013 return mStreamChannel
->GetContentDispositionFilename(
1014 aContentDispositionFilename
);
1018 nsJSChannel::SetContentDispositionFilename(
1019 const nsAString
& aContentDispositionFilename
) {
1020 return mStreamChannel
->SetContentDispositionFilename(
1021 aContentDispositionFilename
);
1025 nsJSChannel::GetContentDispositionHeader(
1026 nsACString
& aContentDispositionHeader
) {
1027 return mStreamChannel
->GetContentDispositionHeader(aContentDispositionHeader
);
1031 nsJSChannel::GetContentLength(int64_t* aContentLength
) {
1032 return mStreamChannel
->GetContentLength(aContentLength
);
1036 nsJSChannel::SetContentLength(int64_t aContentLength
) {
1037 return mStreamChannel
->SetContentLength(aContentLength
);
1041 nsJSChannel::OnStartRequest(nsIRequest
* aRequest
) {
1042 NS_ENSURE_TRUE(aRequest
== mStreamChannel
, NS_ERROR_UNEXPECTED
);
1044 return mListener
->OnStartRequest(this);
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
);
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
)) {
1068 nsresult rv
= listener
->OnStopRequest(this, aStatus
);
1070 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1071 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1073 loadGroup
->RemoveRequest(this, nullptr, mStatus
);
1082 nsJSChannel::SetExecutionPolicy(uint32_t aPolicy
) {
1083 NS_ENSURE_ARG(aPolicy
<= EXECUTE_NORMAL
);
1085 mExecutionPolicy
= aPolicy
;
1090 nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy
) {
1091 *aPolicy
= mExecutionPolicy
;
1096 nsJSChannel::SetExecuteAsync(bool aIsAsync
) {
1098 mIsAsync
= aIsAsync
;
1100 // else ignore this call
1101 NS_WARNING_ASSERTION(!mIsActive
,
1102 "Calling SetExecuteAsync on active channel?");
1108 nsJSChannel::GetExecuteAsync(bool* aIsAsync
) {
1109 *aIsAsync
= mIsAsync
;
1113 bool nsJSChannel::GetIsDocumentLoad() {
1114 // Our LOAD_DOCUMENT_URI flag, if any, lives on our stream channel.
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();
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
,
1141 NS_ENSURE_SUCCESS(rv
, rv
);
1147 ////////////////////////////////////////////////////////////////////////////////
1148 // nsIProtocolHandler methods:
1151 nsJSProtocolHandler::GetScheme(nsACString
& result
) {
1152 result
= "javascript";
1156 /* static */ nsresult
nsJSProtocolHandler::CreateNewURI(const nsACString
& aSpec
,
1157 const char* aCharset
,
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
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
);
1172 nsAutoCString utf8Spec
;
1173 rv
= EnsureUTF8Spec(PromiseFlatCString(aSpec
), aCharset
, utf8Spec
);
1174 if (NS_FAILED(rv
)) {
1177 if (utf8Spec
.IsEmpty()) {
1178 mutator
.SetSpec(aSpec
);
1180 mutator
.SetSpec(utf8Spec
);
1184 nsCOMPtr
<nsIURI
> url
;
1185 rv
= mutator
.Finalize(url
);
1186 if (NS_FAILED(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())
1199 .Finalize(uriWithHost
);
1200 NS_ENSURE_SUCCESS(rv
, rv
);
1209 nsJSProtocolHandler::NewChannel(nsIURI
* uri
, nsILoadInfo
* aLoadInfo
,
1210 nsIChannel
** result
) {
1213 NS_ENSURE_ARG_POINTER(uri
);
1214 RefPtr
<nsJSChannel
> channel
= new nsJSChannel();
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
);
1229 nsJSProtocolHandler::AllowPort(int32_t port
, const char* scheme
,
1231 // don't override anything.
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
;
1258 NS_IMPL_QUERY_CLASSINFO(nsJSURI
)
1259 NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI
)
1261 // nsISerializable methods:
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
;
1274 rv
= aStream
->ReadBoolean(&haveBase
);
1275 if (NS_FAILED(rv
)) return rv
;
1278 nsCOMPtr
<nsISupports
> supports
;
1279 rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
1280 if (NS_FAILED(rv
)) return rv
;
1281 mBaseURI
= do_QueryInterface(supports
);
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
;
1296 rv
= aStream
->WriteObject(mBaseURI
, true);
1297 if (NS_FAILED(rv
)) return rv
;
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
;
1313 SerializeURI(mBaseURI
, jsParams
.baseURI());
1315 jsParams
.baseURI() = mozilla::Nothing();
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!");
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());
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
);
1349 // Queries this list of interfaces. If none match, it queries mURI.
1350 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJSURI::Mutator
, nsIURISetters
, nsIURIMutator
,
1351 nsISerializable
, nsIJSURIMutator
)
1354 nsJSURI::Mutate(nsIURIMutator
** aMutator
) {
1355 RefPtr
<nsJSURI::Mutator
> mutator
= new nsJSURI::Mutator();
1356 nsresult rv
= mutator
->InitFromURI(this);
1357 if (NS_FAILED(rv
)) {
1360 mutator
.forget(aMutator
);
1365 nsresult
nsJSURI::EqualsInternal(
1366 nsIURI
* aOther
, mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode
,
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.
1378 // Compare the member data that our base class knows about.
1379 if (!mozilla::net::nsSimpleURI::EqualsInternal(otherJSURI
,
1380 aRefHandlingMode
)) {
1385 // Compare the piece of additional member data that we add to base class.
1386 nsIURI
* otherBaseURI
= otherJSURI
->GetBaseURI();
1389 // (As noted in StartClone, we always honor refs on mBaseURI)
1390 return mBaseURI
->Equals(otherBaseURI
, aResult
);
1393 *aResult
= !otherBaseURI
;