1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 sw=4 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsAutoPtr.h"
43 #include "nsDOMError.h"
44 #include "nsXPIDLString.h"
45 #include "nsReadableUtils.h"
46 #include "nsJSProtocolHandler.h"
47 #include "nsStringStream.h"
48 #include "nsNetUtil.h"
50 #include "nsIComponentManager.h"
51 #include "nsIServiceManager.h"
53 #include "nsIScriptContext.h"
54 #include "nsIScriptGlobalObject.h"
55 #include "nsIScriptGlobalObjectOwner.h"
56 #include "nsIPrincipal.h"
57 #include "nsIScriptSecurityManager.h"
58 #include "nsIInterfaceRequestor.h"
59 #include "nsIInterfaceRequestorUtils.h"
60 #include "nsIWindowMediator.h"
61 #include "nsPIDOMWindow.h"
62 #include "nsIDOMDocument.h"
63 #include "nsIConsoleService.h"
64 #include "nsXPIDLString.h"
67 #include "nsIWebNavigation.h"
68 #include "nsIDocShell.h"
69 #include "nsIContentViewer.h"
70 #include "nsIXPConnect.h"
71 #include "nsContentUtils.h"
72 #include "nsJSUtils.h"
73 #include "nsThreadUtils.h"
74 #include "nsIJSContextStack.h"
75 #include "nsIScriptChannel.h"
76 #include "nsIDocument.h"
77 #include "nsIObjectInputStream.h"
78 #include "nsIObjectOutputStream.h"
79 #include "nsIWritablePropertyBag2.h"
80 #include "nsIContentSecurityPolicy.h"
82 static NS_DEFINE_CID(kJSURICID
, NS_JSURI_CID
);
84 class nsJSThunk
: public nsIInputStream
90 NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream
)
92 nsresult
Init(nsIURI
* uri
);
93 nsresult
EvaluateScript(nsIChannel
*aChannel
,
94 PopupControlState aPopupState
,
95 PRUint32 aExecutionPolicy
,
96 nsPIDOMWindow
*aOriginalInnerWindow
);
101 nsCOMPtr
<nsIInputStream
> mInnerStream
;
107 // nsISupports implementation...
109 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJSThunk
, nsIInputStream
)
112 nsJSThunk::nsJSThunk()
116 nsJSThunk::~nsJSThunk()
120 nsresult
nsJSThunk::Init(nsIURI
* uri
)
122 NS_ENSURE_ARG_POINTER(uri
);
124 // Get the script string to evaluate...
125 nsresult rv
= uri
->GetPath(mScript
);
126 if (NS_FAILED(rv
)) return rv
;
129 rv
= uri
->GetSpec(mURL
);
130 if (NS_FAILED(rv
)) return rv
;
136 IsISO88591(const nsString
& aString
)
138 for (nsString::const_char_iterator c
= aString
.BeginReading(),
139 c_end
= aString
.EndReading();
148 nsIScriptGlobalObject
* GetGlobalObject(nsIChannel
* aChannel
)
150 // Get the global object owner from the channel
151 nsCOMPtr
<nsIScriptGlobalObjectOwner
> globalOwner
;
152 NS_QueryNotificationCallbacks(aChannel
, globalOwner
);
154 NS_WARNING("Unable to get an nsIScriptGlobalObjectOwner from the "
161 // So far so good: get the script context from its owner.
162 nsIScriptGlobalObject
* global
= globalOwner
->GetScriptGlobalObject();
165 "Unable to get an nsIScriptGlobalObject from the "
166 "ScriptGlobalObjectOwner!");
170 nsresult
nsJSThunk::EvaluateScript(nsIChannel
*aChannel
,
171 PopupControlState aPopupState
,
172 PRUint32 aExecutionPolicy
,
173 nsPIDOMWindow
*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
);
182 // Get principal of code for execution
183 nsCOMPtr
<nsISupports
> owner
;
184 aChannel
->GetOwner(getter_AddRefs(owner
));
185 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(owner
);
187 // No execution without a principal!
188 NS_ASSERTION(!owner
, "Non-principal owner?");
189 NS_WARNING("No principal to execute JS with");
190 return NS_ERROR_DOM_RETVAL_UNDEFINED
;
195 // CSP check: javascript: URIs disabled unless "inline" scripts are
197 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
198 rv
= principal
->GetCsp(getter_AddRefs(csp
));
199 NS_ENSURE_SUCCESS(rv
, rv
);
202 // this call will send violation reports as warranted (and return true if
203 // reportOnly is set).
204 rv
= csp
->GetAllowsInlineScript(&allowsInline
);
205 NS_ENSURE_SUCCESS(rv
, rv
);
207 // TODO: log that we're blocking this javascript: uri
209 return NS_ERROR_DOM_RETVAL_UNDEFINED
;
212 // Get the global object we should be running on.
213 nsIScriptGlobalObject
* global
= GetGlobalObject(aChannel
);
215 return NS_ERROR_FAILURE
;
218 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryInterface(global
));
220 // Push our popup control state
221 nsAutoPopupStatePusher
popupStatePusher(win
, aPopupState
);
223 // Make sure we still have the same inner window as we used to.
224 nsPIDOMWindow
*innerWin
= win
->GetCurrentInnerWindow();
226 if (innerWin
!= aOriginalInnerWindow
) {
227 return NS_ERROR_UNEXPECTED
;
230 nsCOMPtr
<nsIScriptGlobalObject
> innerGlobal
= do_QueryInterface(innerWin
);
232 JSObject
*globalJSObject
= innerGlobal
->GetGlobalJSObject();
234 nsCOMPtr
<nsIDOMWindow
> domWindow(do_QueryInterface(global
, &rv
));
236 return NS_ERROR_FAILURE
;
239 // So far so good: get the script context from its owner.
240 nsCOMPtr
<nsIScriptContext
> scriptContext
= global
->GetContext();
242 return NS_ERROR_FAILURE
;
244 nsCAutoString
script(mScript
);
245 // Unescape the script
246 NS_UnescapeURL(script
);
249 nsCOMPtr
<nsIScriptSecurityManager
> securityManager
;
250 securityManager
= do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rv
);
255 (aExecutionPolicy
== nsIScriptChannel::EXECUTE_IN_SANDBOX
);
258 //-- Don't outside a sandbox unless the script principal subsumes the
259 // principal of the context.
260 nsCOMPtr
<nsIPrincipal
> objectPrincipal
;
261 rv
= securityManager
->
262 GetObjectPrincipal((JSContext
*)scriptContext
->GetNativeContext(),
264 getter_AddRefs(objectPrincipal
));
269 rv
= principal
->Subsumes(objectPrincipal
, &subsumes
);
273 useSandbox
= !subsumes
;
279 // Finally, we have everything needed to evaluate the expression.
282 // We were asked to use a sandbox, or the channel owner isn't allowed
283 // to execute in this context. Evaluate the javascript URL in a
284 // sandbox to prevent it from accessing data it doesn't have
285 // permissions to access.
287 // First check to make sure it's OK to evaluate this script to
288 // start with. For example, script could be disabled.
289 JSContext
*cx
= (JSContext
*)scriptContext
->GetNativeContext();
290 JSAutoRequest
ar(cx
);
293 rv
= securityManager
->CanExecuteScripts(cx
, principal
, &ok
);
299 // Treat this as returning undefined from the script. That's what
301 return NS_ERROR_DOM_RETVAL_UNDEFINED
;
304 nsIXPConnect
*xpc
= nsContentUtils::XPConnect();
306 nsCOMPtr
<nsIXPConnectJSObjectHolder
> sandbox
;
307 rv
= xpc
->CreateSandbox(cx
, principal
, getter_AddRefs(sandbox
));
308 NS_ENSURE_SUCCESS(rv
, rv
);
310 jsval rval
= JSVAL_VOID
;
311 nsAutoGCRoot
root(&rval
, &rv
);
316 // Push our JSContext on the context stack so the JS_ValueToString call (and
317 // JS_ReportPendingException, if relevant) will use the principal of cx.
318 // Note that we do this as late as possible to make popping simpler.
319 nsCOMPtr
<nsIJSContextStack
> stack
=
320 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv
);
321 if (NS_SUCCEEDED(rv
)) {
322 rv
= stack
->Push(cx
);
328 rv
= xpc
->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script
), cx
,
329 sandbox
, PR_TRUE
, &rval
);
331 // Propagate and report exceptions that happened in the
333 if (JS_IsExceptionPending(cx
)) {
334 JS_ReportPendingException(cx
);
335 isUndefined
= PR_TRUE
;
337 isUndefined
= rval
== JSVAL_VOID
;
340 if (!isUndefined
&& NS_SUCCEEDED(rv
)) {
341 NS_ASSERTION(JSVAL_IS_STRING(rval
), "evalInSandbox is broken");
342 result
= nsDependentJSString(JSVAL_TO_STRING(rval
));
347 // No need to use the sandbox, evaluate the script directly in
349 rv
= scriptContext
->EvaluateString(NS_ConvertUTF8toUTF16(script
),
350 globalJSObject
, // obj
358 // If there's an error on cx as a result of that call, report
359 // it now -- either we're just running under the event loop,
360 // so we shouldn't propagate JS exceptions out of here, or we
361 // can't be sure that our caller is JS (and if it's not we'll
362 // lose the error), or it might be JS that then proceeds to
363 // cause an error of its own (which will also make us lose
365 JSContext
*cx
= (JSContext
*)scriptContext
->GetNativeContext();
366 JSAutoRequest
ar(cx
);
367 ::JS_ReportPendingException(cx
);
371 rv
= NS_ERROR_MALFORMED_URI
;
373 else if (isUndefined
) {
374 rv
= NS_ERROR_DOM_RETVAL_UNDEFINED
;
379 NS_NAMED_LITERAL_CSTRING(isoCharset
, "ISO-8859-1");
380 NS_NAMED_LITERAL_CSTRING(utf8Charset
, "UTF-8");
381 const nsCString
*charset
;
382 if (IsISO88591(result
)) {
383 // For compatibility, if the result is ISO-8859-1, we use
384 // ISO-8859-1, so that people can compatibly create images
385 // using javascript: URLs.
386 bytes
= ToNewCString(result
);
387 bytesLen
= result
.Length();
388 charset
= &isoCharset
;
391 bytes
= ToNewUTF8String(result
, &bytesLen
);
392 charset
= &utf8Charset
;
394 aChannel
->SetContentCharset(*charset
);
396 rv
= NS_NewByteInputStream(getter_AddRefs(mInnerStream
),
398 NS_ASSIGNMENT_ADOPT
);
400 rv
= NS_ERROR_OUT_OF_MEMORY
;
406 ////////////////////////////////////////////////////////////////////////////////
408 class nsJSChannel
: public nsIChannel
,
409 public nsIStreamListener
,
410 public nsIScriptChannel
,
411 public nsIPropertyBag2
419 NS_DECL_NSIREQUESTOBSERVER
420 NS_DECL_NSISTREAMLISTENER
421 NS_DECL_NSISCRIPTCHANNEL
422 NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag
)
423 NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag
)
425 nsresult
Init(nsIURI
*aURI
);
427 // Actually evaluate the script.
428 void EvaluateScript();
431 virtual ~nsJSChannel();
435 void NotifyListener();
437 void CleanupStrongRefs();
440 nsCOMPtr
<nsIChannel
> mStreamChannel
;
441 nsCOMPtr
<nsIPropertyBag2
> mPropertyBag
;
442 nsCOMPtr
<nsIStreamListener
> mListener
; // Our final listener
443 nsCOMPtr
<nsISupports
> mContext
; // The context passed to AsyncOpen
444 nsCOMPtr
<nsPIDOMWindow
> mOriginalInnerWindow
; // The inner window our load
446 // If we blocked onload on a document in AsyncOpen, this is the document we
448 nsCOMPtr
<nsIDocument
> mDocumentOnloadBlockedOn
;
450 nsresult mStatus
; // Our status
452 nsLoadFlags mLoadFlags
;
453 nsLoadFlags mActualLoadFlags
; // See AsyncOpen
455 nsRefPtr
<nsJSThunk
> mIOThunk
;
456 PopupControlState mPopupState
;
457 PRUint32 mExecutionPolicy
;
458 PRPackedBool mIsAsync
;
459 PRPackedBool mIsActive
;
460 PRPackedBool mOpenedStreamChannel
;
463 nsJSChannel::nsJSChannel() :
465 mLoadFlags(LOAD_NORMAL
),
466 mActualLoadFlags(LOAD_NORMAL
),
467 mPopupState(openOverridden
),
468 mExecutionPolicy(EXECUTE_IN_SANDBOX
),
471 mOpenedStreamChannel(PR_FALSE
)
475 nsJSChannel::~nsJSChannel()
479 nsresult
nsJSChannel::StopAll()
481 nsresult rv
= NS_ERROR_UNEXPECTED
;
482 nsCOMPtr
<nsIWebNavigation
> webNav
;
483 NS_QueryNotificationCallbacks(mStreamChannel
, webNav
);
485 NS_ASSERTION(webNav
, "Can't get nsIWebNavigation from channel!");
487 rv
= webNav
->Stop(nsIWebNavigation::STOP_ALL
);
493 nsresult
nsJSChannel::Init(nsIURI
*aURI
)
495 nsRefPtr
<nsJSURI
> jsURI
;
496 nsresult rv
= aURI
->QueryInterface(kJSURICID
,
497 getter_AddRefs(jsURI
));
498 NS_ENSURE_SUCCESS(rv
, rv
);
500 // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
501 mIOThunk
= new nsJSThunk();
503 return NS_ERROR_OUT_OF_MEMORY
;
505 // Create a stock input stream channel...
506 // Remember, until AsyncOpen is called, the script will not be evaluated
507 // and the underlying Input Stream will not be created...
508 nsCOMPtr
<nsIChannel
> channel
;
510 // If the resultant script evaluation actually does return a value, we
512 rv
= NS_NewInputStreamChannel(getter_AddRefs(channel
), aURI
, mIOThunk
,
513 NS_LITERAL_CSTRING("text/html"));
514 if (NS_FAILED(rv
)) return rv
;
516 rv
= mIOThunk
->Init(aURI
);
517 if (NS_SUCCEEDED(rv
)) {
518 mStreamChannel
= channel
;
519 mPropertyBag
= do_QueryInterface(channel
);
520 nsCOMPtr
<nsIWritablePropertyBag2
> writableBag
=
521 do_QueryInterface(channel
);
522 if (writableBag
&& jsURI
->GetBaseURI()) {
523 writableBag
->SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
524 jsURI
->GetBaseURI());
532 // nsISupports implementation...
535 NS_IMPL_ISUPPORTS7(nsJSChannel
, nsIChannel
, nsIRequest
, nsIRequestObserver
,
536 nsIStreamListener
, nsIScriptChannel
, nsIPropertyBag
,
540 // nsIRequest implementation...
544 nsJSChannel::GetName(nsACString
&aResult
)
546 return mStreamChannel
->GetName(aResult
);
550 nsJSChannel::IsPending(PRBool
*aResult
)
552 *aResult
= mIsActive
;
557 nsJSChannel::GetStatus(nsresult
*aResult
)
559 if (NS_SUCCEEDED(mStatus
) && mOpenedStreamChannel
) {
560 return mStreamChannel
->GetStatus(aResult
);
569 nsJSChannel::Cancel(nsresult aStatus
)
573 if (mOpenedStreamChannel
) {
574 mStreamChannel
->Cancel(aStatus
);
581 nsJSChannel::Suspend()
583 return mStreamChannel
->Suspend();
587 nsJSChannel::Resume()
589 return mStreamChannel
->Resume();
593 // nsIChannel implementation
597 nsJSChannel::GetOriginalURI(nsIURI
* *aURI
)
599 return mStreamChannel
->GetOriginalURI(aURI
);
603 nsJSChannel::SetOriginalURI(nsIURI
*aURI
)
605 return mStreamChannel
->SetOriginalURI(aURI
);
609 nsJSChannel::GetURI(nsIURI
* *aURI
)
611 return mStreamChannel
->GetURI(aURI
);
615 nsJSChannel::Open(nsIInputStream
**aResult
)
617 nsresult rv
= mIOThunk
->EvaluateScript(mStreamChannel
, mPopupState
,
619 mOriginalInnerWindow
);
620 NS_ENSURE_SUCCESS(rv
, rv
);
622 return mStreamChannel
->Open(aResult
);
626 nsJSChannel::AsyncOpen(nsIStreamListener
*aListener
, nsISupports
*aContext
)
628 NS_ENSURE_ARG(aListener
);
630 // First make sure that we have a usable inner window; we'll want to make
631 // sure that we execute against that inner and no other.
632 nsIScriptGlobalObject
* global
= GetGlobalObject(this);
634 return NS_ERROR_NOT_AVAILABLE
;
637 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryInterface(global
));
638 NS_ASSERTION(win
, "Our global is not a window??");
640 // Make sure we create a new inner window if one doesn't already exist (see
642 mOriginalInnerWindow
= win
->EnsureInnerWindow();
643 if (!mOriginalInnerWindow
) {
644 return NS_ERROR_NOT_AVAILABLE
;
647 mListener
= aListener
;
652 // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
653 // notifications (and hence nsIWebProgressListener notifications) from
654 // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI,
655 // which means that the DocLoader would not generate document start and
656 // stop notifications (see bug 257875).
657 mActualLoadFlags
= mLoadFlags
;
658 mLoadFlags
|= LOAD_BACKGROUND
;
660 // Add the javascript channel to its loadgroup so that we know if
661 // network loads were canceled or not...
662 nsCOMPtr
<nsILoadGroup
> loadGroup
;
663 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
665 nsresult rv
= loadGroup
->AddRequest(this, nsnull
);
667 mIsActive
= PR_FALSE
;
673 mDocumentOnloadBlockedOn
=
674 do_QueryInterface(mOriginalInnerWindow
->GetExtantDocument());
675 if (mDocumentOnloadBlockedOn
) {
676 // If we're a document channel, we need to actually block onload on our
677 // _parent_ document. This is because we don't actually set our
678 // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the
679 // document channel will claim to not be busy, and our parent's onload
680 // could fire too early.
681 nsLoadFlags loadFlags
;
682 mStreamChannel
->GetLoadFlags(&loadFlags
);
683 if (loadFlags
& LOAD_DOCUMENT_URI
) {
684 mDocumentOnloadBlockedOn
=
685 mDocumentOnloadBlockedOn
->GetParentDocument();
688 if (mDocumentOnloadBlockedOn
) {
689 mDocumentOnloadBlockedOn
->BlockOnload();
693 mPopupState
= win
->GetPopupControlState();
695 void (nsJSChannel::*method
)();
697 // post an event to do the rest
698 method
= &nsJSChannel::EvaluateScript
;
701 if (mOpenedStreamChannel
) {
702 // That will handle notifying things
706 NS_ASSERTION(NS_FAILED(mStatus
), "We should have failed _somehow_");
708 // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't
709 // have any content resulting from the execution and NS_BINDING_ABORTED
710 // if something we did causes our own load to be stopped. Return
711 // success in those cases, and error out in all others.
712 if (mStatus
!= NS_ERROR_DOM_RETVAL_UNDEFINED
&&
713 mStatus
!= NS_BINDING_ABORTED
) {
714 // Note that calling EvaluateScript() handled removing us from the
715 // loadgroup and marking us as not active anymore.
720 // We're returning success from asyncOpen(), but we didn't open a
721 // stream channel. We'll have to notify ourselves, but make sure to do
722 // it asynchronously.
723 method
= &nsJSChannel::NotifyListener
;
726 nsCOMPtr
<nsIRunnable
> ev
= NS_NewRunnableMethod(this, method
);
727 nsresult rv
= NS_DispatchToCurrentThread(ev
);
730 loadGroup
->RemoveRequest(this, nsnull
, rv
);
731 mIsActive
= PR_FALSE
;
738 nsJSChannel::EvaluateScript()
740 // Synchronously execute the script...
741 // mIsActive is used to indicate the the request is 'busy' during the
742 // the script evaluation phase. This means that IsPending() will
743 // indicate the the request is busy while the script is executing...
745 // Note that we want to be in the loadgroup and pending while we evaluate
746 // the script, so that we find out if the loadgroup gets canceled by the
747 // script execution (in which case we shouldn't pump out data even if the
748 // script returns it).
750 if (NS_SUCCEEDED(mStatus
)) {
751 nsresult rv
= mIOThunk
->EvaluateScript(mStreamChannel
, mPopupState
,
753 mOriginalInnerWindow
);
755 // Note that evaluation may have canceled us, so recheck mStatus again
756 if (NS_FAILED(rv
) && NS_SUCCEEDED(mStatus
)) {
761 // Remove the javascript channel from its loadgroup...
762 nsCOMPtr
<nsILoadGroup
> loadGroup
;
763 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
765 loadGroup
->RemoveRequest(this, nsnull
, mStatus
);
768 // Reset load flags to their original value...
769 mLoadFlags
= mActualLoadFlags
;
771 // We're no longer active, it's now up to the stream channel to do
772 // the loading, if needed.
773 mIsActive
= PR_FALSE
;
775 if (NS_FAILED(mStatus
)) {
782 // EvaluateScript() succeeded, and we were not canceled, that
783 // means there's data to parse as a result of evaluating the
786 // Get the stream channels load flags (!= mLoadFlags).
787 nsLoadFlags loadFlags
;
788 mStreamChannel
->GetLoadFlags(&loadFlags
);
790 if (loadFlags
& LOAD_DOCUMENT_URI
) {
791 // We're loaded as the document channel. If we go on,
792 // we'll blow away the current document. Make sure that's
793 // ok. If so, stop all pending network loads.
795 nsCOMPtr
<nsIDocShell
> docShell
;
796 NS_QueryNotificationCallbacks(mStreamChannel
, docShell
);
798 nsCOMPtr
<nsIContentViewer
> cv
;
799 docShell
->GetContentViewer(getter_AddRefs(cv
));
804 if (NS_SUCCEEDED(cv
->PermitUnload(PR_FALSE
, &okToUnload
)) &&
806 // The user didn't want to unload the current
807 // page, translate this into an undefined
808 // return from the javascript: URL...
809 mStatus
= NS_ERROR_DOM_RETVAL_UNDEFINED
;
814 if (NS_SUCCEEDED(mStatus
)) {
819 if (NS_FAILED(mStatus
)) {
826 mStatus
= mStreamChannel
->AsyncOpen(this, mContext
);
827 if (NS_SUCCEEDED(mStatus
)) {
828 // mStreamChannel will call OnStartRequest and OnStopRequest on
829 // us, so we'll be sure to call them on our listener.
830 mOpenedStreamChannel
= PR_TRUE
;
832 // Now readd ourselves to the loadgroup so we can receive
833 // cancellation notifications.
836 mStatus
= loadGroup
->AddRequest(this, nsnull
);
838 // If AddRequest failed, that's OK. The key is to make sure we get
839 // cancelled if needed, and that call just canceled us if it
840 // failed. We'll still get notified by the stream channel when it
844 } else if (mIsAsync
) {
852 nsJSChannel::NotifyListener()
854 mListener
->OnStartRequest(this, mContext
);
855 mListener
->OnStopRequest(this, mContext
, mStatus
);
861 nsJSChannel::CleanupStrongRefs()
865 mOriginalInnerWindow
= nsnull
;
866 if (mDocumentOnloadBlockedOn
) {
867 mDocumentOnloadBlockedOn
->UnblockOnload(PR_FALSE
);
868 mDocumentOnloadBlockedOn
= nsnull
;
873 nsJSChannel::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
875 *aLoadFlags
= mLoadFlags
;
881 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags
)
883 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
885 PRBool bogusLoadBackground
= PR_FALSE
;
886 if (mIsActive
&& !(mActualLoadFlags
& LOAD_BACKGROUND
) &&
887 (aLoadFlags
& LOAD_BACKGROUND
)) {
888 // We're getting a LOAD_BACKGROUND, but it's probably just our own fake
889 // flag being mirrored to us. The one exception is if our loadgroup is
891 PRBool loadGroupIsBackground
= PR_FALSE
;
892 nsCOMPtr
<nsILoadGroup
> loadGroup
;
893 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
895 nsLoadFlags loadGroupFlags
;
896 loadGroup
->GetLoadFlags(&loadGroupFlags
);
897 loadGroupIsBackground
= ((loadGroupFlags
& LOAD_BACKGROUND
) != 0);
899 bogusLoadBackground
= !loadGroupIsBackground
;
902 // Since the javascript channel is never the actual channel that
903 // any data is loaded through, don't ever set the
904 // LOAD_DOCUMENT_URI flag on it, since that could lead to two
905 // 'document channels' in the loadgroup if a javascript: URL is
906 // loaded while a document is being loaded in the same window.
908 // XXXbz this, and a whole lot of other hackery, could go away if we'd just
909 // cancel the current document load on javascript: load start like IE does.
911 mLoadFlags
= aLoadFlags
& ~LOAD_DOCUMENT_URI
;
913 if (bogusLoadBackground
) {
914 aLoadFlags
= aLoadFlags
& ~LOAD_BACKGROUND
;
917 mActualLoadFlags
= aLoadFlags
;
919 // ... but the underlying stream channel should get this bit, if
920 // set, since that'll be the real document channel if the
921 // javascript: URL generated data.
923 return mStreamChannel
->SetLoadFlags(aLoadFlags
);
927 nsJSChannel::GetLoadGroup(nsILoadGroup
* *aLoadGroup
)
929 return mStreamChannel
->GetLoadGroup(aLoadGroup
);
933 nsJSChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
936 PRBool streamPending
;
937 nsresult rv
= mStreamChannel
->IsPending(&streamPending
);
938 NS_ENSURE_SUCCESS(rv
, rv
);
941 nsCOMPtr
<nsILoadGroup
> curLoadGroup
;
942 mStreamChannel
->GetLoadGroup(getter_AddRefs(curLoadGroup
));
944 if (aLoadGroup
!= curLoadGroup
) {
945 // Move the stream channel to our new loadgroup. Make sure to
946 // add it before removing it, so that we don't trigger onload
948 aLoadGroup
->AddRequest(mStreamChannel
, nsnull
);
950 curLoadGroup
->RemoveRequest(mStreamChannel
, nsnull
,
951 NS_BINDING_RETARGETED
);
957 return mStreamChannel
->SetLoadGroup(aLoadGroup
);
961 nsJSChannel::GetOwner(nsISupports
* *aOwner
)
963 return mStreamChannel
->GetOwner(aOwner
);
967 nsJSChannel::SetOwner(nsISupports
* aOwner
)
969 return mStreamChannel
->SetOwner(aOwner
);
973 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor
* *aCallbacks
)
975 return mStreamChannel
->GetNotificationCallbacks(aCallbacks
);
979 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
)
981 return mStreamChannel
->SetNotificationCallbacks(aCallbacks
);
985 nsJSChannel::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
987 return mStreamChannel
->GetSecurityInfo(aSecurityInfo
);
991 nsJSChannel::GetContentType(nsACString
&aContentType
)
993 return mStreamChannel
->GetContentType(aContentType
);
997 nsJSChannel::SetContentType(const nsACString
&aContentType
)
999 return mStreamChannel
->SetContentType(aContentType
);
1003 nsJSChannel::GetContentCharset(nsACString
&aContentCharset
)
1005 return mStreamChannel
->GetContentCharset(aContentCharset
);
1009 nsJSChannel::SetContentCharset(const nsACString
&aContentCharset
)
1011 return mStreamChannel
->SetContentCharset(aContentCharset
);
1015 nsJSChannel::GetContentLength(PRInt32
*aContentLength
)
1017 return mStreamChannel
->GetContentLength(aContentLength
);
1021 nsJSChannel::SetContentLength(PRInt32 aContentLength
)
1023 return mStreamChannel
->SetContentLength(aContentLength
);
1027 nsJSChannel::OnStartRequest(nsIRequest
* aRequest
,
1028 nsISupports
* aContext
)
1030 NS_ENSURE_TRUE(aRequest
== mStreamChannel
, NS_ERROR_UNEXPECTED
);
1032 return mListener
->OnStartRequest(this, aContext
);
1036 nsJSChannel::OnDataAvailable(nsIRequest
* aRequest
,
1037 nsISupports
* aContext
,
1038 nsIInputStream
* aInputStream
,
1042 NS_ENSURE_TRUE(aRequest
== mStreamChannel
, NS_ERROR_UNEXPECTED
);
1044 return mListener
->OnDataAvailable(this, aContext
, aInputStream
, aOffset
,
1049 nsJSChannel::OnStopRequest(nsIRequest
* aRequest
,
1050 nsISupports
* aContext
,
1053 NS_ENSURE_TRUE(aRequest
== mStreamChannel
, NS_ERROR_UNEXPECTED
);
1055 nsCOMPtr
<nsIStreamListener
> listener
= mListener
;
1057 CleanupStrongRefs();
1059 // Make sure aStatus matches what GetStatus() returns
1060 if (NS_FAILED(mStatus
)) {
1064 nsresult rv
= listener
->OnStopRequest(this, aContext
, aStatus
);
1066 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1067 mStreamChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1069 loadGroup
->RemoveRequest(this, nsnull
, mStatus
);
1072 mIsActive
= PR_FALSE
;
1078 nsJSChannel::SetExecutionPolicy(PRUint32 aPolicy
)
1080 NS_ENSURE_ARG(aPolicy
<= EXECUTE_NORMAL
);
1082 mExecutionPolicy
= aPolicy
;
1087 nsJSChannel::GetExecutionPolicy(PRUint32
* aPolicy
)
1089 *aPolicy
= mExecutionPolicy
;
1094 nsJSChannel::SetExecuteAsync(PRBool aIsAsync
)
1097 mIsAsync
= aIsAsync
;
1099 // else ignore this call
1100 NS_WARN_IF_FALSE(!mIsActive
, "Calling SetExecuteAsync on active channel?");
1106 nsJSChannel::GetExecuteAsync(PRBool
* aIsAsync
)
1108 *aIsAsync
= mIsAsync
;
1112 ////////////////////////////////////////////////////////////////////////////////
1114 nsJSProtocolHandler::nsJSProtocolHandler()
1119 nsJSProtocolHandler::Init()
1124 nsJSProtocolHandler::~nsJSProtocolHandler()
1128 NS_IMPL_ISUPPORTS1(nsJSProtocolHandler
, nsIProtocolHandler
)
1131 nsJSProtocolHandler::Create(nsISupports
*aOuter
, REFNSIID aIID
, void **aResult
)
1134 return NS_ERROR_NO_AGGREGATION
;
1136 nsJSProtocolHandler
* ph
= new nsJSProtocolHandler();
1138 return NS_ERROR_OUT_OF_MEMORY
;
1140 nsresult rv
= ph
->Init();
1141 if (NS_SUCCEEDED(rv
)) {
1142 rv
= ph
->QueryInterface(aIID
, aResult
);
1149 nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString
&aSpec
, const char *aCharset
,
1150 nsACString
&aUTF8Spec
)
1152 aUTF8Spec
.Truncate();
1156 if (!mTextToSubURI
) {
1157 mTextToSubURI
= do_GetService(NS_ITEXTTOSUBURI_CONTRACTID
, &rv
);
1158 NS_ENSURE_SUCCESS(rv
, rv
);
1161 rv
= mTextToSubURI
->UnEscapeNonAsciiURI(nsDependentCString(aCharset
), aSpec
, uStr
);
1162 NS_ENSURE_SUCCESS(rv
, rv
);
1165 NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr
), esc_AlwaysCopy
| esc_OnlyNonASCII
, aUTF8Spec
);
1170 ////////////////////////////////////////////////////////////////////////////////
1171 // nsIProtocolHandler methods:
1174 nsJSProtocolHandler::GetScheme(nsACString
&result
)
1176 result
= "javascript";
1181 nsJSProtocolHandler::GetDefaultPort(PRInt32
*result
)
1183 *result
= -1; // no port for javascript: URLs
1188 nsJSProtocolHandler::GetProtocolFlags(PRUint32
*result
)
1190 *result
= URI_NORELATIVE
| URI_NOAUTH
| URI_INHERITS_SECURITY_CONTEXT
|
1191 URI_LOADABLE_BY_ANYONE
| URI_NON_PERSISTABLE
| URI_OPENING_EXECUTES_SCRIPT
;
1196 nsJSProtocolHandler::NewURI(const nsACString
&aSpec
,
1197 const char *aCharset
,
1203 // javascript: URLs (currently) have no additional structure beyond that
1204 // provided by standard URLs, so there is no "outer" object given to
1207 nsCOMPtr
<nsIURI
> url
= do_CreateInstance(NS_SIMPLEURI_CONTRACTID
, &rv
);
1212 if (!aCharset
|| !nsCRT::strcasecmp("UTF-8", aCharset
))
1213 rv
= url
->SetSpec(aSpec
);
1215 nsCAutoString utf8Spec
;
1216 rv
= EnsureUTF8Spec(PromiseFlatCString(aSpec
), aCharset
, utf8Spec
);
1217 if (NS_SUCCEEDED(rv
)) {
1218 if (utf8Spec
.IsEmpty())
1219 rv
= url
->SetSpec(aSpec
);
1221 rv
= url
->SetSpec(utf8Spec
);
1225 if (NS_FAILED(rv
)) {
1229 *result
= new nsJSURI(aBaseURI
, url
);
1230 NS_ENSURE_TRUE(*result
, NS_ERROR_OUT_OF_MEMORY
);
1237 nsJSProtocolHandler::NewChannel(nsIURI
* uri
, nsIChannel
* *result
)
1240 nsJSChannel
* channel
;
1242 NS_ENSURE_ARG_POINTER(uri
);
1244 channel
= new nsJSChannel();
1246 return NS_ERROR_OUT_OF_MEMORY
;
1250 rv
= channel
->Init(uri
);
1251 if (NS_SUCCEEDED(rv
)) {
1255 NS_RELEASE(channel
);
1260 nsJSProtocolHandler::AllowPort(PRInt32 port
, const char *scheme
, PRBool
*_retval
)
1262 // don't override anything.
1263 *_retval
= PR_FALSE
;
1267 ////////////////////////////////////////////////////////////
1268 // nsJSURI implementation
1270 NS_IMPL_ADDREF(nsJSURI
)
1271 NS_IMPL_RELEASE(nsJSURI
)
1273 NS_INTERFACE_MAP_BEGIN(nsJSURI
)
1274 NS_INTERFACE_MAP_ENTRY(nsIURI
)
1275 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
1276 NS_INTERFACE_MAP_ENTRY(nsIClassInfo
)
1277 NS_INTERFACE_MAP_ENTRY(nsIMutable
)
1278 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURI
)
1279 if (aIID
.Equals(kJSURICID
))
1280 foundInterface
= static_cast<nsIURI
*>(this);
1282 NS_INTERFACE_MAP_END
1284 // nsISerializable methods:
1287 nsJSURI::Read(nsIObjectInputStream
* aStream
)
1291 rv
= aStream
->ReadObject(PR_TRUE
, getter_AddRefs(mSimpleURI
));
1292 if (NS_FAILED(rv
)) return rv
;
1294 mMutable
= do_QueryInterface(mSimpleURI
);
1295 NS_ENSURE_TRUE(mMutable
, NS_ERROR_UNEXPECTED
);
1298 rv
= aStream
->ReadBoolean(&haveBase
);
1299 if (NS_FAILED(rv
)) return rv
;
1302 rv
= aStream
->ReadObject(PR_TRUE
, getter_AddRefs(mBaseURI
));
1303 if (NS_FAILED(rv
)) return rv
;
1310 nsJSURI::Write(nsIObjectOutputStream
* aStream
)
1314 rv
= aStream
->WriteObject(mSimpleURI
, PR_TRUE
);
1315 if (NS_FAILED(rv
)) return rv
;
1317 rv
= aStream
->WriteBoolean(mBaseURI
!= nsnull
);
1318 if (NS_FAILED(rv
)) return rv
;
1321 rv
= aStream
->WriteObject(mBaseURI
, PR_TRUE
);
1322 if (NS_FAILED(rv
)) return rv
;
1331 nsJSURI::Clone(nsIURI
** aClone
)
1333 nsCOMPtr
<nsIURI
> simpleClone
;
1334 nsresult rv
= mSimpleURI
->Clone(getter_AddRefs(simpleClone
));
1335 NS_ENSURE_SUCCESS(rv
, rv
);
1337 nsCOMPtr
<nsIURI
> baseClone
;
1339 rv
= mBaseURI
->Clone(getter_AddRefs(baseClone
));
1340 NS_ENSURE_SUCCESS(rv
, rv
);
1343 nsIURI
* newURI
= new nsJSURI(baseClone
, simpleClone
);
1344 NS_ENSURE_TRUE(newURI
, NS_ERROR_OUT_OF_MEMORY
);
1346 NS_ADDREF(*aClone
= newURI
);
1351 nsJSURI::Equals(nsIURI
* aOther
, PRBool
*aResult
)
1354 *aResult
= PR_FALSE
;
1358 nsRefPtr
<nsJSURI
> otherJSUri
;
1359 aOther
->QueryInterface(kJSURICID
, getter_AddRefs(otherJSUri
));
1361 *aResult
= PR_FALSE
;
1365 return mSimpleURI
->Equals(otherJSUri
->mSimpleURI
, aResult
);
1368 // nsIClassInfo methods:
1370 nsJSURI::GetInterfaces(PRUint32
*count
, nsIID
* **array
)
1378 nsJSURI::GetHelperForLanguage(PRUint32 language
, nsISupports
**_retval
)
1385 nsJSURI::GetContractID(char * *aContractID
)
1387 // Make sure to modify any subclasses as needed if this ever
1389 *aContractID
= nsnull
;
1394 nsJSURI::GetClassDescription(char * *aClassDescription
)
1396 *aClassDescription
= nsnull
;
1401 nsJSURI::GetClassID(nsCID
* *aClassID
)
1403 // Make sure to modify any subclasses as needed if this ever
1404 // changes to not call the virtual GetClassIDNoAlloc.
1405 *aClassID
= (nsCID
*) nsMemory::Alloc(sizeof(nsCID
));
1407 return NS_ERROR_OUT_OF_MEMORY
;
1408 return GetClassIDNoAlloc(*aClassID
);
1412 nsJSURI::GetImplementationLanguage(PRUint32
*aImplementationLanguage
)
1414 *aImplementationLanguage
= nsIProgrammingLanguage::CPLUSPLUS
;
1419 nsJSURI::GetFlags(PRUint32
*aFlags
)
1421 *aFlags
= nsIClassInfo::MAIN_THREAD_ONLY
;
1426 nsJSURI::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
1428 *aClassIDNoAlloc
= kJSURICID
;