Fix register allocation bug in return values (bug 604996, r=dmandelin).
[mozilla-central.git] / dom / src / jsurl / nsJSProtocolHandler.cpp
blob93e7a2973281a7dc50b284e293fbf6320e0a68cc
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
14 * License.
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.
23 * Contributor(s):
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 ***** */
39 #include "nsCOMPtr.h"
40 #include "nsAutoPtr.h"
41 #include "jsapi.h"
42 #include "nsCRT.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"
52 #include "nsIURI.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"
65 #include "prprf.h"
66 #include "nsEscape.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
86 public:
87 nsJSThunk();
89 NS_DECL_ISUPPORTS
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);
98 protected:
99 virtual ~nsJSThunk();
101 nsCOMPtr<nsIInputStream> mInnerStream;
102 nsCString mScript;
103 nsCString mURL;
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;
128 // Get the url.
129 rv = uri->GetSpec(mURL);
130 if (NS_FAILED(rv)) return rv;
132 return NS_OK;
135 static PRBool
136 IsISO88591(const nsString& aString)
138 for (nsString::const_char_iterator c = aString.BeginReading(),
139 c_end = aString.EndReading();
140 c < c_end; ++c) {
141 if (*c > 255)
142 return PR_FALSE;
144 return PR_TRUE;
147 static
148 nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel)
150 // Get the global object owner from the channel
151 nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner;
152 NS_QueryNotificationCallbacks(aChannel, globalOwner);
153 if (!globalOwner) {
154 NS_WARNING("Unable to get an nsIScriptGlobalObjectOwner from the "
155 "channel!");
157 if (!globalOwner) {
158 return nsnull;
161 // So far so good: get the script context from its owner.
162 nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject();
164 NS_ASSERTION(global,
165 "Unable to get an nsIScriptGlobalObject from the "
166 "ScriptGlobalObjectOwner!");
167 return global;
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);
186 if (!principal) {
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;
193 nsresult rv;
195 // CSP check: javascript: URIs disabled unless "inline" scripts are
196 // allowed.
197 nsCOMPtr<nsIContentSecurityPolicy> csp;
198 rv = principal->GetCsp(getter_AddRefs(csp));
199 NS_ENSURE_SUCCESS(rv, rv);
200 if(csp) {
201 PRBool allowsInline;
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
208 if (!allowsInline)
209 return NS_ERROR_DOM_RETVAL_UNDEFINED;
212 // Get the global object we should be running on.
213 nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
214 if (!global) {
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));
235 if (NS_FAILED(rv)) {
236 return NS_ERROR_FAILURE;
239 // So far so good: get the script context from its owner.
240 nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext();
241 if (!scriptContext)
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);
251 if (NS_FAILED(rv))
252 return rv;
254 PRBool useSandbox =
255 (aExecutionPolicy == nsIScriptChannel::EXECUTE_IN_SANDBOX);
257 if (!useSandbox) {
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(),
263 globalJSObject,
264 getter_AddRefs(objectPrincipal));
265 if (NS_FAILED(rv))
266 return rv;
268 PRBool subsumes;
269 rv = principal->Subsumes(objectPrincipal, &subsumes);
270 if (NS_FAILED(rv))
271 return rv;
273 useSandbox = !subsumes;
276 nsString result;
277 PRBool isUndefined;
279 // Finally, we have everything needed to evaluate the expression.
281 if (useSandbox) {
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);
292 PRBool ok;
293 rv = securityManager->CanExecuteScripts(cx, principal, &ok);
294 if (NS_FAILED(rv)) {
295 return rv;
298 if (!ok) {
299 // Treat this as returning undefined from the script. That's what
300 // nsJSContext does.
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);
312 if (NS_FAILED(rv)) {
313 return 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);
324 if (NS_FAILED(rv)) {
325 return rv;
328 rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), cx,
329 sandbox, PR_TRUE, &rval);
331 // Propagate and report exceptions that happened in the
332 // sandbox.
333 if (JS_IsExceptionPending(cx)) {
334 JS_ReportPendingException(cx);
335 isUndefined = PR_TRUE;
336 } else {
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));
345 stack->Pop(nsnull);
346 } else {
347 // No need to use the sandbox, evaluate the script directly in
348 // the given scope.
349 rv = scriptContext->EvaluateString(NS_ConvertUTF8toUTF16(script),
350 globalJSObject, // obj
351 principal,
352 mURL.get(), // url
353 1, // line no
354 nsnull,
355 &result,
356 &isUndefined);
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
364 // this error).
365 JSContext *cx = (JSContext*)scriptContext->GetNativeContext();
366 JSAutoRequest ar(cx);
367 ::JS_ReportPendingException(cx);
370 if (NS_FAILED(rv)) {
371 rv = NS_ERROR_MALFORMED_URI;
373 else if (isUndefined) {
374 rv = NS_ERROR_DOM_RETVAL_UNDEFINED;
376 else {
377 char *bytes;
378 PRUint32 bytesLen;
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;
390 else {
391 bytes = ToNewUTF8String(result, &bytesLen);
392 charset = &utf8Charset;
394 aChannel->SetContentCharset(*charset);
395 if (bytes)
396 rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream),
397 bytes, bytesLen,
398 NS_ASSIGNMENT_ADOPT);
399 else
400 rv = NS_ERROR_OUT_OF_MEMORY;
403 return rv;
406 ////////////////////////////////////////////////////////////////////////////////
408 class nsJSChannel : public nsIChannel,
409 public nsIStreamListener,
410 public nsIScriptChannel,
411 public nsIPropertyBag2
413 public:
414 nsJSChannel();
416 NS_DECL_ISUPPORTS
417 NS_DECL_NSIREQUEST
418 NS_DECL_NSICHANNEL
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();
430 protected:
431 virtual ~nsJSChannel();
433 nsresult StopAll();
435 void NotifyListener();
437 void CleanupStrongRefs();
439 protected:
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
445 // started against.
446 // If we blocked onload on a document in AsyncOpen, this is the document we
447 // did it on.
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() :
464 mStatus(NS_OK),
465 mLoadFlags(LOAD_NORMAL),
466 mActualLoadFlags(LOAD_NORMAL),
467 mPopupState(openOverridden),
468 mExecutionPolicy(EXECUTE_IN_SANDBOX),
469 mIsAsync(PR_TRUE),
470 mIsActive(PR_FALSE),
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!");
486 if (webNav) {
487 rv = webNav->Stop(nsIWebNavigation::STOP_ALL);
490 return rv;
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();
502 if (!mIOThunk)
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
511 // treat it as html.
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());
528 return rv;
532 // nsISupports implementation...
535 NS_IMPL_ISUPPORTS7(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver,
536 nsIStreamListener, nsIScriptChannel, nsIPropertyBag,
537 nsIPropertyBag2)
540 // nsIRequest implementation...
543 NS_IMETHODIMP
544 nsJSChannel::GetName(nsACString &aResult)
546 return mStreamChannel->GetName(aResult);
549 NS_IMETHODIMP
550 nsJSChannel::IsPending(PRBool *aResult)
552 *aResult = mIsActive;
553 return NS_OK;
556 NS_IMETHODIMP
557 nsJSChannel::GetStatus(nsresult *aResult)
559 if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) {
560 return mStreamChannel->GetStatus(aResult);
563 *aResult = mStatus;
565 return NS_OK;
568 NS_IMETHODIMP
569 nsJSChannel::Cancel(nsresult aStatus)
571 mStatus = aStatus;
573 if (mOpenedStreamChannel) {
574 mStreamChannel->Cancel(aStatus);
577 return NS_OK;
580 NS_IMETHODIMP
581 nsJSChannel::Suspend()
583 return mStreamChannel->Suspend();
586 NS_IMETHODIMP
587 nsJSChannel::Resume()
589 return mStreamChannel->Resume();
593 // nsIChannel implementation
596 NS_IMETHODIMP
597 nsJSChannel::GetOriginalURI(nsIURI * *aURI)
599 return mStreamChannel->GetOriginalURI(aURI);
602 NS_IMETHODIMP
603 nsJSChannel::SetOriginalURI(nsIURI *aURI)
605 return mStreamChannel->SetOriginalURI(aURI);
608 NS_IMETHODIMP
609 nsJSChannel::GetURI(nsIURI * *aURI)
611 return mStreamChannel->GetURI(aURI);
614 NS_IMETHODIMP
615 nsJSChannel::Open(nsIInputStream **aResult)
617 nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState,
618 mExecutionPolicy,
619 mOriginalInnerWindow);
620 NS_ENSURE_SUCCESS(rv, rv);
622 return mStreamChannel->Open(aResult);
625 NS_IMETHODIMP
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);
633 if (!global) {
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
641 // bug 306630).
642 mOriginalInnerWindow = win->EnsureInnerWindow();
643 if (!mOriginalInnerWindow) {
644 return NS_ERROR_NOT_AVAILABLE;
647 mListener = aListener;
648 mContext = aContext;
650 mIsActive = PR_TRUE;
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));
664 if (loadGroup) {
665 nsresult rv = loadGroup->AddRequest(this, nsnull);
666 if (NS_FAILED(rv)) {
667 mIsActive = PR_FALSE;
668 CleanupStrongRefs();
669 return rv;
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)();
696 if (mIsAsync) {
697 // post an event to do the rest
698 method = &nsJSChannel::EvaluateScript;
699 } else {
700 EvaluateScript();
701 if (mOpenedStreamChannel) {
702 // That will handle notifying things
703 return NS_OK;
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.
716 CleanupStrongRefs();
717 return mStatus;
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);
729 if (NS_FAILED(rv)) {
730 loadGroup->RemoveRequest(this, nsnull, rv);
731 mIsActive = PR_FALSE;
732 CleanupStrongRefs();
734 return rv;
737 void
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,
752 mExecutionPolicy,
753 mOriginalInnerWindow);
755 // Note that evaluation may have canceled us, so recheck mStatus again
756 if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) {
757 mStatus = rv;
761 // Remove the javascript channel from its loadgroup...
762 nsCOMPtr<nsILoadGroup> loadGroup;
763 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
764 if (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)) {
776 if (mIsAsync) {
777 NotifyListener();
779 return;
782 // EvaluateScript() succeeded, and we were not canceled, that
783 // means there's data to parse as a result of evaluating the
784 // script.
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);
797 if (docShell) {
798 nsCOMPtr<nsIContentViewer> cv;
799 docShell->GetContentViewer(getter_AddRefs(cv));
801 if (cv) {
802 PRBool okToUnload;
804 if (NS_SUCCEEDED(cv->PermitUnload(PR_FALSE, &okToUnload)) &&
805 !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)) {
815 mStatus = StopAll();
819 if (NS_FAILED(mStatus)) {
820 if (mIsAsync) {
821 NotifyListener();
823 return;
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.
834 mIsActive = PR_TRUE;
835 if (loadGroup) {
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
841 // finishes.
844 } else if (mIsAsync) {
845 NotifyListener();
848 return;
851 void
852 nsJSChannel::NotifyListener()
854 mListener->OnStartRequest(this, mContext);
855 mListener->OnStopRequest(this, mContext, mStatus);
857 CleanupStrongRefs();
860 void
861 nsJSChannel::CleanupStrongRefs()
863 mListener = nsnull;
864 mContext = nsnull;
865 mOriginalInnerWindow = nsnull;
866 if (mDocumentOnloadBlockedOn) {
867 mDocumentOnloadBlockedOn->UnblockOnload(PR_FALSE);
868 mDocumentOnloadBlockedOn = nsnull;
872 NS_IMETHODIMP
873 nsJSChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
875 *aLoadFlags = mLoadFlags;
877 return NS_OK;
880 NS_IMETHODIMP
881 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
883 // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
884 // actually right.
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
890 // LOAD_BACKGROUND.
891 PRBool loadGroupIsBackground = PR_FALSE;
892 nsCOMPtr<nsILoadGroup> loadGroup;
893 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
894 if (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);
926 NS_IMETHODIMP
927 nsJSChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
929 return mStreamChannel->GetLoadGroup(aLoadGroup);
932 NS_IMETHODIMP
933 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
935 if (aLoadGroup) {
936 PRBool streamPending;
937 nsresult rv = mStreamChannel->IsPending(&streamPending);
938 NS_ENSURE_SUCCESS(rv, rv);
940 if (streamPending) {
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
947 // by accident.
948 aLoadGroup->AddRequest(mStreamChannel, nsnull);
949 if (curLoadGroup) {
950 curLoadGroup->RemoveRequest(mStreamChannel, nsnull,
951 NS_BINDING_RETARGETED);
957 return mStreamChannel->SetLoadGroup(aLoadGroup);
960 NS_IMETHODIMP
961 nsJSChannel::GetOwner(nsISupports* *aOwner)
963 return mStreamChannel->GetOwner(aOwner);
966 NS_IMETHODIMP
967 nsJSChannel::SetOwner(nsISupports* aOwner)
969 return mStreamChannel->SetOwner(aOwner);
972 NS_IMETHODIMP
973 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
975 return mStreamChannel->GetNotificationCallbacks(aCallbacks);
978 NS_IMETHODIMP
979 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
981 return mStreamChannel->SetNotificationCallbacks(aCallbacks);
984 NS_IMETHODIMP
985 nsJSChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
987 return mStreamChannel->GetSecurityInfo(aSecurityInfo);
990 NS_IMETHODIMP
991 nsJSChannel::GetContentType(nsACString &aContentType)
993 return mStreamChannel->GetContentType(aContentType);
996 NS_IMETHODIMP
997 nsJSChannel::SetContentType(const nsACString &aContentType)
999 return mStreamChannel->SetContentType(aContentType);
1002 NS_IMETHODIMP
1003 nsJSChannel::GetContentCharset(nsACString &aContentCharset)
1005 return mStreamChannel->GetContentCharset(aContentCharset);
1008 NS_IMETHODIMP
1009 nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
1011 return mStreamChannel->SetContentCharset(aContentCharset);
1014 NS_IMETHODIMP
1015 nsJSChannel::GetContentLength(PRInt32 *aContentLength)
1017 return mStreamChannel->GetContentLength(aContentLength);
1020 NS_IMETHODIMP
1021 nsJSChannel::SetContentLength(PRInt32 aContentLength)
1023 return mStreamChannel->SetContentLength(aContentLength);
1026 NS_IMETHODIMP
1027 nsJSChannel::OnStartRequest(nsIRequest* aRequest,
1028 nsISupports* aContext)
1030 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1032 return mListener->OnStartRequest(this, aContext);
1035 NS_IMETHODIMP
1036 nsJSChannel::OnDataAvailable(nsIRequest* aRequest,
1037 nsISupports* aContext,
1038 nsIInputStream* aInputStream,
1039 PRUint32 aOffset,
1040 PRUint32 aCount)
1042 NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
1044 return mListener->OnDataAvailable(this, aContext, aInputStream, aOffset,
1045 aCount);
1048 NS_IMETHODIMP
1049 nsJSChannel::OnStopRequest(nsIRequest* aRequest,
1050 nsISupports* aContext,
1051 nsresult aStatus)
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)) {
1061 aStatus = mStatus;
1064 nsresult rv = listener->OnStopRequest(this, aContext, aStatus);
1066 nsCOMPtr<nsILoadGroup> loadGroup;
1067 mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1068 if (loadGroup) {
1069 loadGroup->RemoveRequest(this, nsnull, mStatus);
1072 mIsActive = PR_FALSE;
1074 return rv;
1077 NS_IMETHODIMP
1078 nsJSChannel::SetExecutionPolicy(PRUint32 aPolicy)
1080 NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL);
1082 mExecutionPolicy = aPolicy;
1083 return NS_OK;
1086 NS_IMETHODIMP
1087 nsJSChannel::GetExecutionPolicy(PRUint32* aPolicy)
1089 *aPolicy = mExecutionPolicy;
1090 return NS_OK;
1093 NS_IMETHODIMP
1094 nsJSChannel::SetExecuteAsync(PRBool aIsAsync)
1096 if (!mIsActive) {
1097 mIsAsync = aIsAsync;
1099 // else ignore this call
1100 NS_WARN_IF_FALSE(!mIsActive, "Calling SetExecuteAsync on active channel?");
1102 return NS_OK;
1105 NS_IMETHODIMP
1106 nsJSChannel::GetExecuteAsync(PRBool* aIsAsync)
1108 *aIsAsync = mIsAsync;
1109 return NS_OK;
1112 ////////////////////////////////////////////////////////////////////////////////
1114 nsJSProtocolHandler::nsJSProtocolHandler()
1118 nsresult
1119 nsJSProtocolHandler::Init()
1121 return NS_OK;
1124 nsJSProtocolHandler::~nsJSProtocolHandler()
1128 NS_IMPL_ISUPPORTS1(nsJSProtocolHandler, nsIProtocolHandler)
1130 nsresult
1131 nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
1133 if (aOuter)
1134 return NS_ERROR_NO_AGGREGATION;
1136 nsJSProtocolHandler* ph = new nsJSProtocolHandler();
1137 if (!ph)
1138 return NS_ERROR_OUT_OF_MEMORY;
1139 NS_ADDREF(ph);
1140 nsresult rv = ph->Init();
1141 if (NS_SUCCEEDED(rv)) {
1142 rv = ph->QueryInterface(aIID, aResult);
1144 NS_RELEASE(ph);
1145 return rv;
1148 nsresult
1149 nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString &aSpec, const char *aCharset,
1150 nsACString &aUTF8Spec)
1152 aUTF8Spec.Truncate();
1154 nsresult rv;
1156 if (!mTextToSubURI) {
1157 mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
1158 NS_ENSURE_SUCCESS(rv, rv);
1160 nsAutoString uStr;
1161 rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, uStr);
1162 NS_ENSURE_SUCCESS(rv, rv);
1164 if (!IsASCII(uStr))
1165 NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr), esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec);
1167 return NS_OK;
1170 ////////////////////////////////////////////////////////////////////////////////
1171 // nsIProtocolHandler methods:
1173 NS_IMETHODIMP
1174 nsJSProtocolHandler::GetScheme(nsACString &result)
1176 result = "javascript";
1177 return NS_OK;
1180 NS_IMETHODIMP
1181 nsJSProtocolHandler::GetDefaultPort(PRInt32 *result)
1183 *result = -1; // no port for javascript: URLs
1184 return NS_OK;
1187 NS_IMETHODIMP
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;
1192 return NS_OK;
1195 NS_IMETHODIMP
1196 nsJSProtocolHandler::NewURI(const nsACString &aSpec,
1197 const char *aCharset,
1198 nsIURI *aBaseURI,
1199 nsIURI **result)
1201 nsresult rv;
1203 // javascript: URLs (currently) have no additional structure beyond that
1204 // provided by standard URLs, so there is no "outer" object given to
1205 // CreateInstance.
1207 nsCOMPtr<nsIURI> url = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
1209 if (NS_FAILED(rv))
1210 return rv;
1212 if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset))
1213 rv = url->SetSpec(aSpec);
1214 else {
1215 nsCAutoString utf8Spec;
1216 rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
1217 if (NS_SUCCEEDED(rv)) {
1218 if (utf8Spec.IsEmpty())
1219 rv = url->SetSpec(aSpec);
1220 else
1221 rv = url->SetSpec(utf8Spec);
1225 if (NS_FAILED(rv)) {
1226 return rv;
1229 *result = new nsJSURI(aBaseURI, url);
1230 NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY);
1232 NS_ADDREF(*result);
1233 return rv;
1236 NS_IMETHODIMP
1237 nsJSProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
1239 nsresult rv;
1240 nsJSChannel * channel;
1242 NS_ENSURE_ARG_POINTER(uri);
1244 channel = new nsJSChannel();
1245 if (!channel) {
1246 return NS_ERROR_OUT_OF_MEMORY;
1248 NS_ADDREF(channel);
1250 rv = channel->Init(uri);
1251 if (NS_SUCCEEDED(rv)) {
1252 *result = channel;
1253 NS_ADDREF(*result);
1255 NS_RELEASE(channel);
1256 return rv;
1259 NS_IMETHODIMP
1260 nsJSProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
1262 // don't override anything.
1263 *_retval = PR_FALSE;
1264 return NS_OK;
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);
1281 else
1282 NS_INTERFACE_MAP_END
1284 // nsISerializable methods:
1286 NS_IMETHODIMP
1287 nsJSURI::Read(nsIObjectInputStream* aStream)
1289 nsresult rv;
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);
1297 PRBool haveBase;
1298 rv = aStream->ReadBoolean(&haveBase);
1299 if (NS_FAILED(rv)) return rv;
1301 if (haveBase) {
1302 rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(mBaseURI));
1303 if (NS_FAILED(rv)) return rv;
1306 return NS_OK;
1309 NS_IMETHODIMP
1310 nsJSURI::Write(nsIObjectOutputStream* aStream)
1312 nsresult rv;
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;
1320 if (mBaseURI) {
1321 rv = aStream->WriteObject(mBaseURI, PR_TRUE);
1322 if (NS_FAILED(rv)) return rv;
1325 return NS_OK;
1328 // nsIURI methods:
1330 NS_IMETHODIMP
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;
1338 if (mBaseURI) {
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);
1347 return NS_OK;
1350 NS_IMETHODIMP
1351 nsJSURI::Equals(nsIURI* aOther, PRBool *aResult)
1353 if (!aOther) {
1354 *aResult = PR_FALSE;
1355 return NS_OK;
1358 nsRefPtr<nsJSURI> otherJSUri;
1359 aOther->QueryInterface(kJSURICID, getter_AddRefs(otherJSUri));
1360 if (!otherJSUri) {
1361 *aResult = PR_FALSE;
1362 return NS_OK;
1365 return mSimpleURI->Equals(otherJSUri->mSimpleURI, aResult);
1368 // nsIClassInfo methods:
1369 NS_IMETHODIMP
1370 nsJSURI::GetInterfaces(PRUint32 *count, nsIID * **array)
1372 *count = 0;
1373 *array = nsnull;
1374 return NS_OK;
1377 NS_IMETHODIMP
1378 nsJSURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
1380 *_retval = nsnull;
1381 return NS_OK;
1384 NS_IMETHODIMP
1385 nsJSURI::GetContractID(char * *aContractID)
1387 // Make sure to modify any subclasses as needed if this ever
1388 // changes.
1389 *aContractID = nsnull;
1390 return NS_OK;
1393 NS_IMETHODIMP
1394 nsJSURI::GetClassDescription(char * *aClassDescription)
1396 *aClassDescription = nsnull;
1397 return NS_OK;
1400 NS_IMETHODIMP
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));
1406 if (!*aClassID)
1407 return NS_ERROR_OUT_OF_MEMORY;
1408 return GetClassIDNoAlloc(*aClassID);
1411 NS_IMETHODIMP
1412 nsJSURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
1414 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
1415 return NS_OK;
1418 NS_IMETHODIMP
1419 nsJSURI::GetFlags(PRUint32 *aFlags)
1421 *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
1422 return NS_OK;
1425 NS_IMETHODIMP
1426 nsJSURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
1428 *aClassIDNoAlloc = kJSURICID;
1429 return NS_OK;