Back to index

lightning-sunbird  0.9+nobinonly
nsJSProtocolHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim: set ts=4 sw=4 et tw=78: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsCOMPtr.h"
00040 #include "nsAutoPtr.h"
00041 #include "jsapi.h"
00042 #include "nsCRT.h"
00043 #include "nsDOMError.h"
00044 #include "nsXPIDLString.h"
00045 #include "nsReadableUtils.h"
00046 #include "nsJSProtocolHandler.h"
00047 #include "nsNetUtil.h"
00048 
00049 #include "nsIComponentManager.h"
00050 #include "nsIGenericFactory.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsIURI.h"
00053 #include "nsIScriptContext.h"
00054 #include "nsIScriptGlobalObject.h"
00055 #include "nsIScriptGlobalObjectOwner.h"
00056 #include "nsIPrincipal.h"
00057 #include "nsIScriptSecurityManager.h"
00058 #include "nsIInterfaceRequestor.h"
00059 #include "nsIInterfaceRequestorUtils.h"
00060 #include "nsIStringStream.h"
00061 #include "nsIWindowMediator.h"
00062 #include "nsPIDOMWindow.h"
00063 #include "nsIDOMDocument.h"
00064 #include "nsIJSConsoleService.h"
00065 #include "nsIConsoleService.h"
00066 #include "nsXPIDLString.h"
00067 #include "prprf.h"
00068 #include "nsEscape.h"
00069 #include "nsIJSContextStack.h"
00070 #include "nsIWebNavigation.h"
00071 #include "nsIDocShell.h"
00072 #include "nsIContentViewer.h"
00073 #include "nsIStringStream.h"
00074 #include "nsIXPConnect.h"
00075 #include "nsContentUtils.h"
00076 #include "nsJSUtils.h"
00077 
00078 
00079 static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
00080 static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID);
00081 
00082 
00083 
00084 class nsJSThunk : public nsIInputStream
00085 {
00086 public:
00087     nsJSThunk();
00088 
00089     NS_DECL_ISUPPORTS
00090     NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream)
00091 
00092     nsresult Init(nsIURI* uri);
00093     nsresult EvaluateScript(nsIChannel *aChannel);
00094     nsresult BringUpConsole(nsIDOMWindow *aDomWindow);
00095 
00096 protected:
00097     virtual ~nsJSThunk();
00098 
00099     nsCOMPtr<nsIURI>            mURI;
00100     nsCOMPtr<nsIInputStream>    mInnerStream;
00101 };
00102 
00103 //
00104 // nsISupports implementation...
00105 //
00106 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJSThunk, nsIInputStream)
00107 
00108 
00109 nsJSThunk::nsJSThunk()
00110 {
00111 }
00112 
00113 nsJSThunk::~nsJSThunk()
00114 {
00115 }
00116 
00117 nsresult nsJSThunk::Init(nsIURI* uri)
00118 {
00119     NS_ENSURE_ARG_POINTER(uri);
00120 
00121     mURI = uri;
00122     return NS_OK;
00123 }
00124 
00125 static PRBool
00126 IsISO88591(const nsString& aString)
00127 {
00128     for (nsString::const_char_iterator c = aString.BeginReading(),
00129                                    c_end = aString.EndReading();
00130          c < c_end; ++c) {
00131         if (*c > 255)
00132             return PR_FALSE;
00133     }
00134     return PR_TRUE;
00135 }
00136 
00137 nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel)
00138 {
00139     nsresult rv;
00140 
00141     NS_ENSURE_ARG_POINTER(aChannel);
00142 
00143     // Get the script string to evaluate...
00144     nsCAutoString script;
00145     rv = mURI->GetPath(script);
00146     if (NS_FAILED(rv)) return rv;
00147 
00148     // The the global object owner from the channel
00149     nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner;
00150     NS_QueryNotificationCallbacks(aChannel, globalOwner);
00151     NS_ASSERTION(globalOwner, 
00152                  "Unable to get an nsIScriptGlobalObjectOwner from the "
00153                  "channel!");
00154     if (!globalOwner) {
00155         return NS_ERROR_FAILURE;
00156     }
00157 
00158     // So far so good: get the script context from its owner.
00159     nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject();
00160 
00161     NS_ASSERTION(global,
00162                  "Unable to get an nsIScriptGlobalObject from the "
00163                  "ScriptGlobalObjectOwner!");
00164     if (!global) {
00165         return NS_ERROR_FAILURE;
00166     }
00167 
00168     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global));
00169 
00170     // Get the document out of the window to make sure we create a new
00171     // inner window if one doesn't already exist (see bug 306630).
00172     nsCOMPtr<nsIDOMDocument> doc;
00173     win->GetDocument(getter_AddRefs(doc));
00174 
00175     nsPIDOMWindow *innerWin = win->GetCurrentInnerWindow();
00176 
00177     if (!innerWin) {
00178         return NS_ERROR_UNEXPECTED;
00179     }
00180 
00181     nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin);
00182 
00183     JSObject *globalJSObject = innerGlobal->GetGlobalJSObject();
00184 
00185     nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(global, &rv));
00186     if (NS_FAILED(rv)) {
00187         return NS_ERROR_FAILURE;
00188     }
00189 
00190     // If mURI is just "javascript:", we bring up the Error console
00191     // and return NS_ERROR_DOM_RETVAL_UNDEFINED.
00192     if (script.IsEmpty()) {
00193         rv = BringUpConsole(domWindow);
00194         if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
00195         return NS_ERROR_DOM_RETVAL_UNDEFINED;
00196     }
00197 
00198     // Now get the DOM Document.  Accessing the document will create one
00199     // if necessary.  So, basically, this call ensures that a document gets
00200     // created -- if necessary.
00201     rv = domWindow->GetDocument(getter_AddRefs(doc));
00202     NS_ASSERTION(doc, "No DOMDocument!");
00203     if (NS_FAILED(rv)) {
00204         return NS_ERROR_FAILURE;
00205     }
00206 
00207     nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext();
00208     if (!scriptContext)
00209         return NS_ERROR_FAILURE;
00210 
00211     // Unescape the script
00212     NS_UnescapeURL(script);
00213 
00214     // Get the url.
00215     nsCAutoString url;
00216     rv = mURI->GetSpec(url);
00217     if (NS_FAILED(rv)) return rv;
00218 
00219     // Get principal of code for execution
00220     nsCOMPtr<nsISupports> owner;
00221     rv = aChannel->GetOwner(getter_AddRefs(owner));
00222     nsCOMPtr<nsIPrincipal> principal;
00223     if (NS_FAILED(rv))
00224         return rv;
00225 
00226     nsCOMPtr<nsIScriptSecurityManager> securityManager;
00227     securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00228     if (NS_FAILED(rv))
00229         return rv;
00230 
00231     PRBool useSandbox = PR_TRUE;
00232 
00233     if (owner) {
00234         principal = do_QueryInterface(owner, &rv);
00235         NS_ASSERTION(principal, "Channel's owner is not a principal");
00236         if (!principal)
00237             return NS_ERROR_FAILURE;
00238 
00239         //-- Don't run if the script principal is different from the principal
00240         //   of the context, unless the script has the system principal.
00241         nsCOMPtr<nsIPrincipal> objectPrincipal;
00242         rv = securityManager->GetObjectPrincipal(
00243                                 (JSContext*)scriptContext->GetNativeContext(),
00244                                 globalJSObject,
00245                                 getter_AddRefs(objectPrincipal));
00246         if (NS_FAILED(rv))
00247             return rv;
00248 
00249         nsCOMPtr<nsIPrincipal> systemPrincipal;
00250         securityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
00251         if (principal != systemPrincipal) {
00252             rv = securityManager->CheckSameOriginPrincipal(principal,
00253                                                            objectPrincipal);
00254             if (NS_SUCCEEDED(rv)) {
00255                 useSandbox = PR_FALSE;
00256             }
00257         } else {
00258             useSandbox = PR_FALSE;
00259         }
00260     }
00261 
00262     nsString result;
00263     PRBool isUndefined;
00264 
00265     // Finally, we have everything needed to evaluate the expression.
00266 
00267     if (useSandbox) {
00268         // No owner from channel, or we have a principal
00269         // mismatch. Evaluate the javascript URL in a sandbox to
00270         // prevent it from accessing data it doesn't have permissions
00271         // to access.
00272 
00273         // First check to make sure it's OK to evaluate this script to
00274         // start with.  For example, script could be disabled.
00275         nsCOMPtr<nsIPrincipal> enabledCheckPrincipal = principal;
00276         if (!enabledCheckPrincipal) {
00277             // We just need a principal that's not the system principal and
00278             // isn't whitelisted by CanExecuteScripts.  An about:blank
00279             // principal will do nicely.
00280             nsCOMPtr<nsIURI> uri;
00281             rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
00282             NS_ENSURE_SUCCESS(rv, rv);
00283             rv = securityManager->
00284                 GetCodebasePrincipal(uri,
00285                                      getter_AddRefs(enabledCheckPrincipal));
00286             NS_ENSURE_SUCCESS(rv, rv);
00287         }
00288 
00289         JSContext *cx = (JSContext*)scriptContext->GetNativeContext();
00290 
00291         PRBool ok;
00292         rv = securityManager->CanExecuteScripts(cx, enabledCheckPrincipal,
00293                                                 &ok);
00294         if (NS_FAILED(rv)) {
00295             return rv;
00296         }
00297 
00298         if (!ok) {
00299             // Treat this as returning undefined from the script.  That's what
00300             // nsJSContext does.
00301             return NS_ERROR_DOM_RETVAL_UNDEFINED;
00302         }
00303 
00304         nsIXPConnect *xpc = nsContentUtils::XPConnect();
00305         nsCOMPtr<nsIXPConnect_MOZILLA_1_8_BRANCH2> xpc_18 =
00306             do_QueryInterface(xpc);
00307 
00308         nsCOMPtr<nsIXPConnectJSObjectHolder> sandbox;
00309         rv = xpc_18->CreateSandbox(cx, principal, getter_AddRefs(sandbox));
00310         NS_ENSURE_SUCCESS(rv, rv);
00311 
00312         jsval rval = JSVAL_VOID;
00313         nsAutoGCRoot root(&rval, &rv);
00314         if (NS_FAILED(rv)) {
00315             return rv;
00316         }
00317 
00318         // Push our JSContext on the context stack so the JS_ValueToString call
00319         // (and JS_ReportPendingException, if relevant) will use the principal
00320         // of cx.  Note that we do this as late as possible to make popping
00321         // simpler.
00322         nsCOMPtr<nsIJSContextStack> stack =
00323             do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
00324         if (NS_SUCCEEDED(rv)) {
00325             rv = stack->Push(cx);
00326         }
00327         if (NS_FAILED(rv)) {
00328             return rv;
00329         }
00330 
00331         rv = xpc_18->EvalInSandboxObject2(NS_ConvertUTF8toUTF16(script), cx,
00332                                           sandbox, PR_TRUE, &rval);
00333 
00334         // Propagate and report exceptions that happened in the
00335         // sandbox.
00336         if (JS_IsExceptionPending(cx)) {
00337             JS_ReportPendingException(cx);
00338             isUndefined = PR_TRUE;
00339         } else {
00340             isUndefined = rval == JSVAL_VOID;
00341         }
00342 
00343         if (!isUndefined && NS_SUCCEEDED(rv)) {
00344             NS_ASSERTION(JSVAL_IS_STRING(rval), "evalInSandbox is broken");
00345             result = nsDependentJSString(JSVAL_TO_STRING(rval));
00346         }
00347 
00348         stack->Pop(nsnull);
00349     } else {
00350         // No need to use the sandbox, evaluate the script directly in
00351         // the given scope.
00352         rv = scriptContext->EvaluateString(NS_ConvertUTF8toUTF16(script),
00353                                            globalJSObject, // obj
00354                                            principal,
00355                                            url.get(),      // url
00356                                            1,              // line no
00357                                            nsnull,
00358                                            &result,
00359                                            &isUndefined);
00360     }
00361 
00362     if (NS_FAILED(rv)) {
00363         rv = NS_ERROR_MALFORMED_URI;
00364     }
00365     else if (isUndefined) {
00366         rv = NS_ERROR_DOM_RETVAL_UNDEFINED;
00367     }
00368     else {
00369         char *bytes;
00370         PRUint32 bytesLen;
00371         NS_NAMED_LITERAL_CSTRING(isoCharset, "ISO-8859-1");
00372         NS_NAMED_LITERAL_CSTRING(utf8Charset, "UTF-8");
00373         const nsCString *charset;
00374         if (IsISO88591(result)) {
00375             // For compatibility, if the result is ISO-8859-1, we use
00376             // ISO-8859-1, so that people can compatibly create images
00377             // using javascript: URLs.
00378             bytes = ToNewCString(result);
00379             bytesLen = result.Length();
00380             charset = &isoCharset;
00381         }
00382         else {
00383             bytes = ToNewUTF8String(result, &bytesLen);
00384             charset = &utf8Charset;
00385         }
00386         aChannel->SetContentCharset(*charset);
00387         if (bytes) {
00388             rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream),
00389                                        bytes, bytesLen);
00390             if (mInnerStream) {
00391                 nsCOMPtr<nsIStringInputStream> sis
00392                     = do_QueryInterface(mInnerStream);
00393                 sis->AdoptData(bytes, bytesLen); // Previous call was |ShareData|
00394             }
00395         }
00396         else
00397             rv = NS_ERROR_OUT_OF_MEMORY;
00398     }
00399 
00400     return rv;
00401 }
00402 
00403 nsresult nsJSThunk::BringUpConsole(nsIDOMWindow *aDomWindow)
00404 {
00405     nsresult rv;
00406 
00407     // First, get the Window Mediator service.
00408     nsCOMPtr<nsIWindowMediator> windowMediator =
00409         do_GetService(kWindowMediatorCID, &rv);
00410 
00411     if (NS_FAILED(rv)) return rv;
00412 
00413     // Next, find out whether there's a console already open.
00414     nsCOMPtr<nsIDOMWindowInternal> console;
00415     rv = windowMediator->GetMostRecentWindow(NS_LITERAL_STRING("global:console").get(),
00416                                              getter_AddRefs(console));
00417     if (NS_FAILED(rv)) return rv;
00418 
00419     if (console) {
00420         // If the console is already open, bring it to the top.
00421         rv = console->Focus();
00422     } else {
00423         nsCOMPtr<nsIJSConsoleService> jsconsole;
00424 
00425         jsconsole = do_GetService("@mozilla.org/embedcomp/jsconsole-service;1", &rv);
00426         if (NS_FAILED(rv) || !jsconsole) return rv;
00427         jsconsole->Open(aDomWindow);
00428     }
00429     return rv;
00430 }
00431 
00433 
00434 class nsJSChannel : public nsIChannel
00435 {
00436 public:
00437     nsJSChannel();
00438 
00439     NS_DECL_ISUPPORTS
00440     NS_DECL_NSIREQUEST
00441     NS_DECL_NSICHANNEL
00442 
00443     nsresult Init(nsIURI *aURI);
00444 
00445 protected:
00446     virtual ~nsJSChannel();
00447 
00448     nsresult StopAll();
00449 
00450     nsresult InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener,
00451                           nsISupports *aContext, nsIInputStream **aResult);
00452 
00453 protected:
00454     nsCOMPtr<nsIChannel>    mStreamChannel;
00455 
00456     nsLoadFlags             mLoadFlags;
00457 
00458     nsRefPtr<nsJSThunk>     mIOThunk;
00459     PRPackedBool            mIsActive;
00460     PRPackedBool            mWasCanceled;
00461 };
00462 
00463 nsJSChannel::nsJSChannel() :
00464     mLoadFlags(LOAD_NORMAL),
00465     mIsActive(PR_FALSE),
00466     mWasCanceled(PR_FALSE)
00467 {
00468 }
00469 
00470 nsJSChannel::~nsJSChannel()
00471 {
00472 }
00473 
00474 nsresult nsJSChannel::StopAll()
00475 {
00476     nsresult rv = NS_ERROR_UNEXPECTED;
00477     nsCOMPtr<nsIWebNavigation> webNav;
00478     NS_QueryNotificationCallbacks(mStreamChannel, webNav);
00479 
00480     NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!");
00481     if (webNav) {
00482         rv = webNav->Stop(nsIWebNavigation::STOP_ALL);
00483     }
00484 
00485     return rv;
00486 }
00487 
00488 nsresult nsJSChannel::Init(nsIURI *aURI)
00489 {
00490     nsresult rv;
00491 
00492     // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
00493     mIOThunk = new nsJSThunk();
00494     if (!mIOThunk)
00495         return NS_ERROR_OUT_OF_MEMORY;
00496 
00497     // Create a stock input stream channel...
00498     // Remember, until AsyncOpen is called, the script will not be evaluated
00499     // and the underlying Input Stream will not be created...
00500     nsCOMPtr<nsIChannel> channel;
00501 
00502     // If the resultant script evaluation actually does return a value, we
00503     // treat it as html.
00504     rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk,
00505                                   NS_LITERAL_CSTRING("text/html"));
00506     if (NS_FAILED(rv)) return rv;
00507 
00508     rv = mIOThunk->Init(aURI);
00509     if (NS_SUCCEEDED(rv)) {
00510         mStreamChannel = channel;
00511     }
00512 
00513     return rv;
00514 }
00515 
00516 //
00517 // nsISupports implementation...
00518 //
00519 
00520 NS_IMPL_ADDREF(nsJSChannel)
00521 NS_IMPL_RELEASE(nsJSChannel)
00522 
00523 NS_INTERFACE_MAP_BEGIN(nsJSChannel)
00524     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
00525     NS_INTERFACE_MAP_ENTRY(nsIRequest)
00526     NS_INTERFACE_MAP_ENTRY(nsIChannel)
00527 NS_INTERFACE_MAP_END
00528 
00529 //
00530 // nsIRequest implementation...
00531 //
00532 
00533 NS_IMETHODIMP
00534 nsJSChannel::GetName(nsACString &aResult)
00535 {
00536     return mStreamChannel->GetName(aResult);
00537 }
00538 
00539 NS_IMETHODIMP
00540 nsJSChannel::IsPending(PRBool *aResult)
00541 {
00542     *aResult = mIsActive;
00543     return NS_OK;
00544 }
00545 
00546 NS_IMETHODIMP
00547 nsJSChannel::GetStatus(nsresult *aResult)
00548 {
00549     // We're always ok. Our status is independent of our underlying
00550     // stream's status.
00551     *aResult = NS_OK;
00552     return NS_OK;
00553 }
00554 
00555 NS_IMETHODIMP
00556 nsJSChannel::Cancel(nsresult aStatus)
00557 {
00558     // If we're canceled just record the fact that we were canceled,
00559     // the underlying stream will be canceled later, if needed. And we
00560     // don't care about the reason for the canceling, i.e. ignore
00561     // aStatus.
00562 
00563     mWasCanceled = PR_TRUE;
00564 
00565     return NS_OK;
00566 }
00567 
00568 NS_IMETHODIMP
00569 nsJSChannel::Suspend()
00570 {
00571     return mStreamChannel->Suspend();
00572 }
00573 
00574 NS_IMETHODIMP
00575 nsJSChannel::Resume()
00576 {
00577     return mStreamChannel->Resume();
00578 }
00579 
00580 //
00581 // nsIChannel implementation
00582 //
00583 
00584 NS_IMETHODIMP
00585 nsJSChannel::GetOriginalURI(nsIURI * *aURI)
00586 {
00587     return mStreamChannel->GetOriginalURI(aURI);
00588 }
00589 
00590 NS_IMETHODIMP
00591 nsJSChannel::SetOriginalURI(nsIURI *aURI)
00592 {
00593     return mStreamChannel->SetOriginalURI(aURI);
00594 }
00595 
00596 NS_IMETHODIMP
00597 nsJSChannel::GetURI(nsIURI * *aURI)
00598 {
00599     return mStreamChannel->GetURI(aURI);
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsJSChannel::Open(nsIInputStream **aResult)
00604 {
00605     return InternalOpen(PR_FALSE, nsnull, nsnull, aResult);
00606 }
00607 
00608 NS_IMETHODIMP
00609 nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
00610 {
00611     return InternalOpen(PR_TRUE, aListener, aContext, nsnull);
00612 }
00613 
00614 nsresult
00615 nsJSChannel::InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener,
00616                           nsISupports *aContext, nsIInputStream **aResult)
00617 {
00618     nsCOMPtr<nsILoadGroup> loadGroup;
00619 
00620     // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
00621     // notifications (and hence nsIWebProgressListener notifications) from
00622     // being dispatched.  This is required since we suppress LOAD_DOCUMENT_URI,
00623     // which means that the DocLoader would not generate document start and
00624     // stop notifications (see bug 257875).
00625     PRUint32 oldLoadFlags = mLoadFlags;
00626     mLoadFlags |= LOAD_BACKGROUND;
00627 
00628     // Add the javascript channel to its loadgroup so that we know if
00629     // network loads were canceled or not...
00630     mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00631     if (loadGroup) {
00632         loadGroup->AddRequest(this, aContext);
00633     }
00634 
00635     // Synchronously execute the script...
00636     // mIsActive is used to indicate the the request is 'busy' during the
00637     // the script evaluation phase.  This means that IsPending() will 
00638     // indicate the the request is busy while the script is executing...
00639     mIsActive = PR_TRUE;
00640     nsresult rv = mIOThunk->EvaluateScript(mStreamChannel);
00641 
00642     // Remove the javascript channel from its loadgroup...
00643     if (loadGroup) {
00644         loadGroup->RemoveRequest(this, aContext, rv);
00645     }
00646 
00647     // Reset load flags to their original value...
00648     mLoadFlags = oldLoadFlags;
00649 
00650     // We're no longer active, it's now up to the stream channel to do
00651     // the loading, if needed.
00652     mIsActive = PR_FALSE;
00653 
00654     if (NS_SUCCEEDED(rv) && !mWasCanceled) {
00655         // EvaluateScript() succeeded, and we were not canceled, that
00656         // means there's data to parse as a result of evaluating the
00657         // script.
00658 
00659         // Get the stream channels load flags (!= mLoadFlags).
00660         nsLoadFlags loadFlags;
00661         mStreamChannel->GetLoadFlags(&loadFlags);
00662 
00663         if (loadFlags & LOAD_DOCUMENT_URI) {
00664             // We're loaded as the document channel. If we go on,
00665             // we'll blow away the current document. Make sure that's
00666             // ok. If so, stop all pending network loads.
00667 
00668             nsCOMPtr<nsIDocShell> docShell;
00669             NS_QueryNotificationCallbacks(mStreamChannel, docShell);
00670             if (docShell) {
00671                 nsCOMPtr<nsIContentViewer> cv;
00672                 docShell->GetContentViewer(getter_AddRefs(cv));
00673 
00674                 if (cv) {
00675                     PRBool okToUnload;
00676 
00677                     if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) &&
00678                         !okToUnload) {
00679                         // The user didn't want to unload the current
00680                         // page, translate this into an undefined
00681                         // return from the javascript: URL...
00682                         rv = NS_ERROR_DOM_RETVAL_UNDEFINED;
00683                     }
00684                 }
00685             }
00686 
00687             if (NS_SUCCEEDED(rv)) {
00688                 rv = StopAll();
00689             }
00690         }
00691 
00692         if (NS_SUCCEEDED(rv)) {
00693             // This will add mStreamChannel to the load group.
00694 
00695             if (aIsAsync) {
00696                 rv = mStreamChannel->AsyncOpen(aListener, aContext);
00697             } else {
00698                 rv = mStreamChannel->Open(aResult);
00699             }
00700         }
00701     }
00702 
00703     if (NS_FAILED(rv)) {
00704         // Propagate the failure down to the underlying channel...
00705         mStreamChannel->Cancel(rv);
00706     }
00707 
00708     return rv;
00709 }
00710 
00711 NS_IMETHODIMP
00712 nsJSChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
00713 {
00714     *aLoadFlags = mLoadFlags;
00715 
00716     return NS_OK;
00717 }
00718 
00719 NS_IMETHODIMP
00720 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
00721 {
00722     // Since the javascript channel is never the actual channel that
00723     // any data is loaded through, don't ever set the
00724     // LOAD_DOCUMENT_URI flag on it, since that could lead to two
00725     // 'document channels' in the loadgroup if a javascript: URL is
00726     // loaded while a document is being loaded in the same window.
00727 
00728     mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
00729 
00730     // ... but the underlying stream channel should get this bit, if
00731     // set, since that'll be the real document channel if the
00732     // javascript: URL generated data.
00733 
00734     return mStreamChannel->SetLoadFlags(aLoadFlags);
00735 }
00736 
00737 NS_IMETHODIMP
00738 nsJSChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00739 {
00740     return mStreamChannel->GetLoadGroup(aLoadGroup);
00741 }
00742 
00743 NS_IMETHODIMP
00744 nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00745 {
00746     return mStreamChannel->SetLoadGroup(aLoadGroup);
00747 }
00748 
00749 NS_IMETHODIMP
00750 nsJSChannel::GetOwner(nsISupports* *aOwner)
00751 {
00752     return mStreamChannel->GetOwner(aOwner);
00753 }
00754 
00755 NS_IMETHODIMP
00756 nsJSChannel::SetOwner(nsISupports* aOwner)
00757 {
00758     return mStreamChannel->SetOwner(aOwner);
00759 }
00760 
00761 NS_IMETHODIMP
00762 nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
00763 {
00764     return mStreamChannel->GetNotificationCallbacks(aCallbacks);
00765 }
00766 
00767 NS_IMETHODIMP
00768 nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
00769 {
00770     return mStreamChannel->SetNotificationCallbacks(aCallbacks);
00771 }
00772 
00773 NS_IMETHODIMP 
00774 nsJSChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00775 {
00776     return mStreamChannel->GetSecurityInfo(aSecurityInfo);
00777 }
00778 
00779 NS_IMETHODIMP
00780 nsJSChannel::GetContentType(nsACString &aContentType)
00781 {
00782     return mStreamChannel->GetContentType(aContentType);
00783 }
00784 
00785 NS_IMETHODIMP
00786 nsJSChannel::SetContentType(const nsACString &aContentType)
00787 {
00788     return mStreamChannel->SetContentType(aContentType);
00789 }
00790 
00791 NS_IMETHODIMP
00792 nsJSChannel::GetContentCharset(nsACString &aContentCharset)
00793 {
00794     return mStreamChannel->GetContentCharset(aContentCharset);
00795 }
00796 
00797 NS_IMETHODIMP
00798 nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
00799 {
00800     return mStreamChannel->SetContentCharset(aContentCharset);
00801 }
00802 
00803 NS_IMETHODIMP
00804 nsJSChannel::GetContentLength(PRInt32 *aContentLength)
00805 {
00806     return mStreamChannel->GetContentLength(aContentLength);
00807 }
00808 
00809 NS_IMETHODIMP
00810 nsJSChannel::SetContentLength(PRInt32 aContentLength)
00811 {
00812     return mStreamChannel->SetContentLength(aContentLength);
00813 }
00814 
00815 
00817 
00818 nsJSProtocolHandler::nsJSProtocolHandler()
00819 {
00820 }
00821 
00822 nsresult
00823 nsJSProtocolHandler::Init()
00824 {
00825     return NS_OK;
00826 }
00827 
00828 nsJSProtocolHandler::~nsJSProtocolHandler()
00829 {
00830 }
00831 
00832 NS_IMPL_ISUPPORTS1(nsJSProtocolHandler, nsIProtocolHandler)
00833 
00834 NS_METHOD
00835 nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00836 {
00837     if (aOuter)
00838         return NS_ERROR_NO_AGGREGATION;
00839 
00840     nsJSProtocolHandler* ph = new nsJSProtocolHandler();
00841     if (!ph)
00842         return NS_ERROR_OUT_OF_MEMORY;
00843     NS_ADDREF(ph);
00844     nsresult rv = ph->Init();
00845     if (NS_SUCCEEDED(rv)) {
00846         rv = ph->QueryInterface(aIID, aResult);
00847     }
00848     NS_RELEASE(ph);
00849     return rv;
00850 }
00851 
00852 nsresult 
00853 nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString &aSpec, const char *aCharset, 
00854                                     nsACString &aUTF8Spec)
00855 {
00856   aUTF8Spec.Truncate();
00857 
00858   nsresult rv;
00859   
00860   if (!mTextToSubURI) {
00861     mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
00862     NS_ENSURE_SUCCESS(rv, rv);
00863   }
00864   nsAutoString uStr;
00865   rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, uStr);
00866   NS_ENSURE_SUCCESS(rv, rv);
00867 
00868   if (!IsASCII(uStr))
00869     NS_EscapeURL(NS_ConvertUCS2toUTF8(uStr), esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec);
00870 
00871   return NS_OK;
00872 }
00873 
00875 // nsIProtocolHandler methods:
00876 
00877 NS_IMETHODIMP
00878 nsJSProtocolHandler::GetScheme(nsACString &result)
00879 {
00880     result = "javascript";
00881     return NS_OK;
00882 }
00883 
00884 NS_IMETHODIMP
00885 nsJSProtocolHandler::GetDefaultPort(PRInt32 *result)
00886 {
00887     *result = -1;        // no port for javascript: URLs
00888     return NS_OK;
00889 }
00890 
00891 NS_IMETHODIMP
00892 nsJSProtocolHandler::GetProtocolFlags(PRUint32 *result)
00893 {
00894     *result = URI_NORELATIVE | URI_NOAUTH;
00895     return NS_OK;
00896 }
00897 
00898 NS_IMETHODIMP
00899 nsJSProtocolHandler::NewURI(const nsACString &aSpec,
00900                             const char *aCharset, // ignore charset info
00901                             nsIURI *aBaseURI,
00902                             nsIURI **result)
00903 {
00904     nsresult rv;
00905 
00906     // javascript: URLs (currently) have no additional structure beyond that
00907     // provided by standard URLs, so there is no "outer" object given to
00908     // CreateInstance.
00909 
00910     nsIURI* url;
00911     rv = CallCreateInstance(kSimpleURICID, &url);
00912 
00913     if (NS_FAILED(rv))
00914         return rv;
00915 
00916     if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset))
00917       rv = url->SetSpec(aSpec);
00918     else {
00919       nsCAutoString utf8Spec;
00920       rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
00921       if (NS_SUCCEEDED(rv)) {
00922         if (utf8Spec.IsEmpty())
00923           rv = url->SetSpec(aSpec);
00924         else
00925           rv = url->SetSpec(utf8Spec);
00926       }
00927     }
00928 
00929     if (NS_FAILED(rv)) {
00930         NS_RELEASE(url);
00931         return rv;
00932     }
00933 
00934     *result = url;
00935     return rv;
00936 }
00937 
00938 NS_IMETHODIMP
00939 nsJSProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
00940 {
00941     nsresult rv;
00942     nsJSChannel * channel;
00943 
00944     NS_ENSURE_ARG_POINTER(uri);
00945 
00946     channel = new nsJSChannel();
00947     if (!channel) {
00948         return NS_ERROR_OUT_OF_MEMORY;
00949     }
00950     NS_ADDREF(channel);
00951 
00952     rv = channel->Init(uri);
00953     if (NS_SUCCEEDED(rv)) {
00954         *result = channel;
00955         NS_ADDREF(*result);
00956     }
00957     NS_RELEASE(channel);
00958     return rv;
00959 }
00960 
00961 NS_IMETHODIMP 
00962 nsJSProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
00963 {
00964     // don't override anything.  
00965     *_retval = PR_FALSE;
00966     return NS_OK;
00967 }
00968