dom/src/jsurl/nsJSProtocolHandler.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/src/jsurl/nsJSProtocolHandler.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1384 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=4 sw=4 et tw=78: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsCOMPtr.h"
    1.11 +#include "nsAutoPtr.h"
    1.12 +#include "jsapi.h"
    1.13 +#include "jswrapper.h"
    1.14 +#include "nsCRT.h"
    1.15 +#include "nsError.h"
    1.16 +#include "nsXPIDLString.h"
    1.17 +#include "nsReadableUtils.h"
    1.18 +#include "nsJSProtocolHandler.h"
    1.19 +#include "nsStringStream.h"
    1.20 +#include "nsNetUtil.h"
    1.21 +
    1.22 +#include "nsIComponentManager.h"
    1.23 +#include "nsIServiceManager.h"
    1.24 +#include "nsIURI.h"
    1.25 +#include "nsIScriptContext.h"
    1.26 +#include "nsIScriptGlobalObject.h"
    1.27 +#include "nsIPrincipal.h"
    1.28 +#include "nsIScriptSecurityManager.h"
    1.29 +#include "nsIInterfaceRequestor.h"
    1.30 +#include "nsIInterfaceRequestorUtils.h"
    1.31 +#include "nsIWindowMediator.h"
    1.32 +#include "nsPIDOMWindow.h"
    1.33 +#include "nsIConsoleService.h"
    1.34 +#include "nsXPIDLString.h"
    1.35 +#include "prprf.h"
    1.36 +#include "nsEscape.h"
    1.37 +#include "nsIWebNavigation.h"
    1.38 +#include "nsIDocShell.h"
    1.39 +#include "nsIContentViewer.h"
    1.40 +#include "nsIXPConnect.h"
    1.41 +#include "nsContentUtils.h"
    1.42 +#include "nsCxPusher.h"
    1.43 +#include "nsJSUtils.h"
    1.44 +#include "nsThreadUtils.h"
    1.45 +#include "nsIScriptChannel.h"
    1.46 +#include "nsIDocument.h"
    1.47 +#include "nsIObjectInputStream.h"
    1.48 +#include "nsIObjectOutputStream.h"
    1.49 +#include "nsIWritablePropertyBag2.h"
    1.50 +#include "nsIContentSecurityPolicy.h"
    1.51 +#include "nsSandboxFlags.h"
    1.52 +#include "mozilla/dom/ScriptSettings.h"
    1.53 +
    1.54 +using mozilla::dom::AutoEntryScript;
    1.55 +
    1.56 +static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID);
    1.57 +
    1.58 +class nsJSThunk : public nsIInputStream
    1.59 +{
    1.60 +public:
    1.61 +    nsJSThunk();
    1.62 +
    1.63 +    NS_DECL_THREADSAFE_ISUPPORTS
    1.64 +    NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream)
    1.65 +
    1.66 +    nsresult Init(nsIURI* uri);
    1.67 +    nsresult EvaluateScript(nsIChannel *aChannel,
    1.68 +                            PopupControlState aPopupState,
    1.69 +                            uint32_t aExecutionPolicy,
    1.70 +                            nsPIDOMWindow *aOriginalInnerWindow);
    1.71 +
    1.72 +protected:
    1.73 +    virtual ~nsJSThunk();
    1.74 +
    1.75 +    nsCOMPtr<nsIInputStream>    mInnerStream;
    1.76 +    nsCString                   mScript;
    1.77 +    nsCString                   mURL;
    1.78 +};
    1.79 +
    1.80 +//
    1.81 +// nsISupports implementation...
    1.82 +//
    1.83 +NS_IMPL_ISUPPORTS(nsJSThunk, nsIInputStream)
    1.84 +
    1.85 +
    1.86 +nsJSThunk::nsJSThunk()
    1.87 +{
    1.88 +}
    1.89 +
    1.90 +nsJSThunk::~nsJSThunk()
    1.91 +{
    1.92 +}
    1.93 +
    1.94 +nsresult nsJSThunk::Init(nsIURI* uri)
    1.95 +{
    1.96 +    NS_ENSURE_ARG_POINTER(uri);
    1.97 +
    1.98 +    // Get the script string to evaluate...
    1.99 +    nsresult rv = uri->GetPath(mScript);
   1.100 +    if (NS_FAILED(rv)) return rv;
   1.101 +
   1.102 +    // Get the url.
   1.103 +    rv = uri->GetSpec(mURL);
   1.104 +    if (NS_FAILED(rv)) return rv;
   1.105 +
   1.106 +    return NS_OK;
   1.107 +}
   1.108 +
   1.109 +static bool
   1.110 +IsISO88591(const nsString& aString)
   1.111 +{
   1.112 +    for (nsString::const_char_iterator c = aString.BeginReading(),
   1.113 +                                   c_end = aString.EndReading();
   1.114 +         c < c_end; ++c) {
   1.115 +        if (*c > 255)
   1.116 +            return false;
   1.117 +    }
   1.118 +    return true;
   1.119 +}
   1.120 +
   1.121 +static
   1.122 +nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel)
   1.123 +{
   1.124 +    // Get the global object owner from the channel
   1.125 +    nsCOMPtr<nsIDocShell> docShell;
   1.126 +    NS_QueryNotificationCallbacks(aChannel, docShell);
   1.127 +    if (!docShell) {
   1.128 +        NS_WARNING("Unable to get a docShell from the channel!");
   1.129 +        return nullptr;
   1.130 +    }
   1.131 +
   1.132 +    // So far so good: get the script global from its docshell
   1.133 +    nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject();
   1.134 +
   1.135 +    NS_ASSERTION(global,
   1.136 +                 "Unable to get an nsIScriptGlobalObject from the "
   1.137 +                 "docShell!");
   1.138 +    return global;
   1.139 +}
   1.140 +
   1.141 +nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
   1.142 +                                   PopupControlState aPopupState,
   1.143 +                                   uint32_t aExecutionPolicy,
   1.144 +                                   nsPIDOMWindow *aOriginalInnerWindow)
   1.145 +{
   1.146 +    if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) {
   1.147 +        // Nothing to do here.
   1.148 +        return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.149 +    }
   1.150 +    
   1.151 +    NS_ENSURE_ARG_POINTER(aChannel);
   1.152 +
   1.153 +    // Get principal of code for execution
   1.154 +    nsCOMPtr<nsISupports> owner;
   1.155 +    aChannel->GetOwner(getter_AddRefs(owner));
   1.156 +    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
   1.157 +    if (!principal) {
   1.158 +        // No execution without a principal!
   1.159 +        NS_ASSERTION(!owner, "Non-principal owner?");
   1.160 +        NS_WARNING("No principal to execute JS with");
   1.161 +        return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.162 +    }
   1.163 +
   1.164 +    nsresult rv;
   1.165 +
   1.166 +    // CSP check: javascript: URIs disabled unless "inline" scripts are
   1.167 +    // allowed.
   1.168 +    nsCOMPtr<nsIContentSecurityPolicy> csp;
   1.169 +    rv = principal->GetCsp(getter_AddRefs(csp));
   1.170 +    NS_ENSURE_SUCCESS(rv, rv);
   1.171 +    if (csp) {
   1.172 +        bool allowsInline = true;
   1.173 +        bool reportViolations = false;
   1.174 +        rv = csp->GetAllowsInlineScript(&reportViolations, &allowsInline);
   1.175 +        NS_ENSURE_SUCCESS(rv, rv);
   1.176 +
   1.177 +        if (reportViolations) {
   1.178 +            // gather information to log with violation report
   1.179 +            nsCOMPtr<nsIURI> uri;
   1.180 +            principal->GetURI(getter_AddRefs(uri));
   1.181 +            nsAutoCString asciiSpec;
   1.182 +            uri->GetAsciiSpec(asciiSpec);
   1.183 +            csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
   1.184 +                                     NS_ConvertUTF8toUTF16(asciiSpec),
   1.185 +                                     NS_ConvertUTF8toUTF16(mURL),
   1.186 +                                     0,
   1.187 +                                     EmptyString(),
   1.188 +                                     EmptyString());
   1.189 +        }
   1.190 +
   1.191 +        //return early if inline scripts are not allowed
   1.192 +        if (!allowsInline) {
   1.193 +          return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.194 +        }
   1.195 +    }
   1.196 +
   1.197 +    // Get the global object we should be running on.
   1.198 +    nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
   1.199 +    if (!global) {
   1.200 +        return NS_ERROR_FAILURE;
   1.201 +    }
   1.202 +
   1.203 +    // Sandboxed document check: javascript: URI's are disabled
   1.204 +    // in a sandboxed document unless 'allow-scripts' was specified.
   1.205 +    nsIDocument* doc = aOriginalInnerWindow->GetExtantDoc();
   1.206 +    if (doc && (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS)) {
   1.207 +        return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.208 +    }
   1.209 +
   1.210 +    // Push our popup control state
   1.211 +    nsAutoPopupStatePusher popupStatePusher(aPopupState);
   1.212 +
   1.213 +    // Make sure we still have the same inner window as we used to.
   1.214 +    nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
   1.215 +    nsPIDOMWindow *innerWin = win->GetCurrentInnerWindow();
   1.216 +
   1.217 +    if (innerWin != aOriginalInnerWindow) {
   1.218 +        return NS_ERROR_UNEXPECTED;
   1.219 +    }
   1.220 +
   1.221 +    nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin);
   1.222 +
   1.223 +    nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(global, &rv));
   1.224 +    if (NS_FAILED(rv)) {
   1.225 +        return NS_ERROR_FAILURE;
   1.226 +    }
   1.227 +
   1.228 +    // So far so good: get the script context from its owner.
   1.229 +    nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext();
   1.230 +    if (!scriptContext)
   1.231 +        return NS_ERROR_FAILURE;
   1.232 +
   1.233 +    nsAutoCString script(mScript);
   1.234 +    // Unescape the script
   1.235 +    NS_UnescapeURL(script);
   1.236 +
   1.237 +
   1.238 +    nsCOMPtr<nsIScriptSecurityManager> securityManager;
   1.239 +    securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   1.240 +    if (NS_FAILED(rv))
   1.241 +        return rv;
   1.242 +
   1.243 +    bool useSandbox =
   1.244 +        (aExecutionPolicy == nsIScriptChannel::EXECUTE_IN_SANDBOX);
   1.245 +
   1.246 +    // New script entry point required, due to the "Create a script" step of
   1.247 +    // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
   1.248 +    AutoEntryScript entryScript(innerGlobal, true,
   1.249 +                                scriptContext->GetNativeContext());
   1.250 +    JSContext* cx = entryScript.cx();
   1.251 +    JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
   1.252 +    NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
   1.253 +
   1.254 +    if (!useSandbox) {
   1.255 +        //-- Don't outside a sandbox unless the script principal subsumes the
   1.256 +        //   principal of the context.
   1.257 +        nsIPrincipal* objectPrincipal = nsContentUtils::GetObjectPrincipal(globalJSObject);
   1.258 +
   1.259 +        bool subsumes;
   1.260 +        rv = principal->Subsumes(objectPrincipal, &subsumes);
   1.261 +        if (NS_FAILED(rv))
   1.262 +            return rv;
   1.263 +
   1.264 +        useSandbox = !subsumes;
   1.265 +    }
   1.266 +
   1.267 +    JS::Rooted<JS::Value> v (cx, JS::UndefinedValue());
   1.268 +    // Finally, we have everything needed to evaluate the expression.
   1.269 +    if (useSandbox) {
   1.270 +        // We were asked to use a sandbox, or the channel owner isn't allowed
   1.271 +        // to execute in this context.  Evaluate the javascript URL in a
   1.272 +        // sandbox to prevent it from accessing data it doesn't have
   1.273 +        // permissions to access.
   1.274 +
   1.275 +        // First check to make sure it's OK to evaluate this script to
   1.276 +        // start with.  For example, script could be disabled.
   1.277 +        if (!securityManager->ScriptAllowed(globalJSObject)) {
   1.278 +            // Treat this as returning undefined from the script.  That's what
   1.279 +            // nsJSContext does.
   1.280 +            return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.281 +        }
   1.282 +
   1.283 +        nsIXPConnect *xpc = nsContentUtils::XPConnect();
   1.284 +
   1.285 +        nsCOMPtr<nsIXPConnectJSObjectHolder> sandbox;
   1.286 +        // Important: Use a null principal here
   1.287 +        rv = xpc->CreateSandbox(cx, nullptr, getter_AddRefs(sandbox));
   1.288 +        NS_ENSURE_SUCCESS(rv, rv);
   1.289 +
   1.290 +        // The nsXPConnect sandbox API gives us a wrapper to the sandbox for
   1.291 +        // our current compartment. Because our current context doesn't necessarily
   1.292 +        // subsume that of the sandbox, we want to unwrap and enter the sandbox's
   1.293 +        // compartment. It's a shame that the APIs here are so clunkly. :-(
   1.294 +        JS::Rooted<JSObject*> sandboxObj(cx, sandbox->GetJSObject());
   1.295 +        NS_ENSURE_STATE(sandboxObj);
   1.296 +        sandboxObj = js::UncheckedUnwrap(sandboxObj);
   1.297 +        JSAutoCompartment ac(cx, sandboxObj);
   1.298 +
   1.299 +        // Push our JSContext on the context stack so the EvalInSandboxObject call (and
   1.300 +        // JS_ReportPendingException, if relevant) will use the principal of cx.
   1.301 +        nsCxPusher pusher;
   1.302 +        pusher.Push(cx);
   1.303 +        rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script),
   1.304 +                                      /* filename = */ nullptr, cx,
   1.305 +                                      sandboxObj, true, &v);
   1.306 +
   1.307 +        // Propagate and report exceptions that happened in the
   1.308 +        // sandbox.
   1.309 +        if (JS_IsExceptionPending(cx)) {
   1.310 +            JS_ReportPendingException(cx);
   1.311 +        }
   1.312 +    } else {
   1.313 +        // No need to use the sandbox, evaluate the script directly in
   1.314 +        // the given scope.
   1.315 +        JS::CompileOptions options(cx);
   1.316 +        options.setFileAndLine(mURL.get(), 1)
   1.317 +               .setVersion(JSVERSION_DEFAULT);
   1.318 +        nsJSUtils::EvaluateOptions evalOptions;
   1.319 +        evalOptions.setCoerceToString(true);
   1.320 +        rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
   1.321 +                                       globalJSObject, options, evalOptions, &v);
   1.322 +
   1.323 +        // If there's an error on cx as a result of that call, report
   1.324 +        // it now -- either we're just running under the event loop,
   1.325 +        // so we shouldn't propagate JS exceptions out of here, or we
   1.326 +        // can't be sure that our caller is JS (and if it's not we'll
   1.327 +        // lose the error), or it might be JS that then proceeds to
   1.328 +        // cause an error of its own (which will also make us lose
   1.329 +        // this error).
   1.330 +        ::JS_ReportPendingException(cx);
   1.331 +    }
   1.332 +
   1.333 +    // If we took the sandbox path above, v might be in the sandbox
   1.334 +    // compartment.
   1.335 +    if (!JS_WrapValue(cx, &v)) {
   1.336 +        return NS_ERROR_OUT_OF_MEMORY;
   1.337 +    }
   1.338 +
   1.339 +    if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
   1.340 +        return NS_ERROR_MALFORMED_URI;
   1.341 +    } else if (v.isUndefined()) {
   1.342 +        return NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.343 +    } else {
   1.344 +        nsDependentJSString result;
   1.345 +        if (!result.init(cx, v)) {
   1.346 +            return NS_ERROR_OUT_OF_MEMORY;
   1.347 +        }
   1.348 +
   1.349 +        char *bytes;
   1.350 +        uint32_t bytesLen;
   1.351 +        NS_NAMED_LITERAL_CSTRING(isoCharset, "ISO-8859-1");
   1.352 +        NS_NAMED_LITERAL_CSTRING(utf8Charset, "UTF-8");
   1.353 +        const nsCString *charset;
   1.354 +        if (IsISO88591(result)) {
   1.355 +            // For compatibility, if the result is ISO-8859-1, we use
   1.356 +            // ISO-8859-1, so that people can compatibly create images
   1.357 +            // using javascript: URLs.
   1.358 +            bytes = ToNewCString(result);
   1.359 +            bytesLen = result.Length();
   1.360 +            charset = &isoCharset;
   1.361 +        }
   1.362 +        else {
   1.363 +            bytes = ToNewUTF8String(result, &bytesLen);
   1.364 +            charset = &utf8Charset;
   1.365 +        }
   1.366 +        aChannel->SetContentCharset(*charset);
   1.367 +        if (bytes)
   1.368 +            rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream),
   1.369 +                                       bytes, bytesLen,
   1.370 +                                       NS_ASSIGNMENT_ADOPT);
   1.371 +        else
   1.372 +            rv = NS_ERROR_OUT_OF_MEMORY;
   1.373 +    }
   1.374 +
   1.375 +    return rv;
   1.376 +}
   1.377 +
   1.378 +////////////////////////////////////////////////////////////////////////////////
   1.379 +
   1.380 +class nsJSChannel : public nsIChannel,
   1.381 +                    public nsIStreamListener,
   1.382 +                    public nsIScriptChannel,
   1.383 +                    public nsIPropertyBag2
   1.384 +{
   1.385 +public:
   1.386 +    nsJSChannel();
   1.387 +
   1.388 +    NS_DECL_ISUPPORTS
   1.389 +    NS_DECL_NSIREQUEST
   1.390 +    NS_DECL_NSICHANNEL
   1.391 +    NS_DECL_NSIREQUESTOBSERVER
   1.392 +    NS_DECL_NSISTREAMLISTENER
   1.393 +    NS_DECL_NSISCRIPTCHANNEL
   1.394 +    NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag)
   1.395 +    NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag)
   1.396 +
   1.397 +    nsresult Init(nsIURI *aURI);
   1.398 +
   1.399 +    // Actually evaluate the script.
   1.400 +    void EvaluateScript();
   1.401 +    
   1.402 +protected:
   1.403 +    virtual ~nsJSChannel();
   1.404 +
   1.405 +    nsresult StopAll();
   1.406 +
   1.407 +    void NotifyListener();
   1.408 +
   1.409 +    void CleanupStrongRefs();
   1.410 +    
   1.411 +protected:
   1.412 +    nsCOMPtr<nsIChannel>    mStreamChannel;
   1.413 +    nsCOMPtr<nsIPropertyBag2> mPropertyBag;
   1.414 +    nsCOMPtr<nsIStreamListener> mListener;  // Our final listener
   1.415 +    nsCOMPtr<nsISupports> mContext; // The context passed to AsyncOpen
   1.416 +    nsCOMPtr<nsPIDOMWindow> mOriginalInnerWindow;  // The inner window our load
   1.417 +                                                   // started against.
   1.418 +    // If we blocked onload on a document in AsyncOpen, this is the document we
   1.419 +    // did it on.
   1.420 +    nsCOMPtr<nsIDocument>   mDocumentOnloadBlockedOn;
   1.421 +
   1.422 +    nsresult mStatus; // Our status
   1.423 +
   1.424 +    nsLoadFlags             mLoadFlags;
   1.425 +    nsLoadFlags             mActualLoadFlags; // See AsyncOpen
   1.426 +
   1.427 +    nsRefPtr<nsJSThunk>     mIOThunk;
   1.428 +    PopupControlState       mPopupState;
   1.429 +    uint32_t                mExecutionPolicy;
   1.430 +    bool                    mIsAsync;
   1.431 +    bool                    mIsActive;
   1.432 +    bool                    mOpenedStreamChannel;
   1.433 +};
   1.434 +
   1.435 +nsJSChannel::nsJSChannel() :
   1.436 +    mStatus(NS_OK),
   1.437 +    mLoadFlags(LOAD_NORMAL),
   1.438 +    mActualLoadFlags(LOAD_NORMAL),
   1.439 +    mPopupState(openOverridden),
   1.440 +    mExecutionPolicy(EXECUTE_IN_SANDBOX),
   1.441 +    mIsAsync(true),
   1.442 +    mIsActive(false),
   1.443 +    mOpenedStreamChannel(false)
   1.444 +{
   1.445 +}
   1.446 +
   1.447 +nsJSChannel::~nsJSChannel()
   1.448 +{
   1.449 +}
   1.450 +
   1.451 +nsresult nsJSChannel::StopAll()
   1.452 +{
   1.453 +    nsresult rv = NS_ERROR_UNEXPECTED;
   1.454 +    nsCOMPtr<nsIWebNavigation> webNav;
   1.455 +    NS_QueryNotificationCallbacks(mStreamChannel, webNav);
   1.456 +
   1.457 +    NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!");
   1.458 +    if (webNav) {
   1.459 +        rv = webNav->Stop(nsIWebNavigation::STOP_ALL);
   1.460 +    }
   1.461 +
   1.462 +    return rv;
   1.463 +}
   1.464 +
   1.465 +nsresult nsJSChannel::Init(nsIURI *aURI)
   1.466 +{
   1.467 +    nsRefPtr<nsJSURI> jsURI;
   1.468 +    nsresult rv = aURI->QueryInterface(kJSURICID,
   1.469 +                                       getter_AddRefs(jsURI));
   1.470 +    NS_ENSURE_SUCCESS(rv, rv);
   1.471 +
   1.472 +    // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
   1.473 +    mIOThunk = new nsJSThunk();
   1.474 +    if (!mIOThunk)
   1.475 +        return NS_ERROR_OUT_OF_MEMORY;
   1.476 +
   1.477 +    // Create a stock input stream channel...
   1.478 +    // Remember, until AsyncOpen is called, the script will not be evaluated
   1.479 +    // and the underlying Input Stream will not be created...
   1.480 +    nsCOMPtr<nsIChannel> channel;
   1.481 +
   1.482 +    // If the resultant script evaluation actually does return a value, we
   1.483 +    // treat it as html.
   1.484 +    rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk,
   1.485 +                                  NS_LITERAL_CSTRING("text/html"));
   1.486 +    if (NS_FAILED(rv)) return rv;
   1.487 +
   1.488 +    rv = mIOThunk->Init(aURI);
   1.489 +    if (NS_SUCCEEDED(rv)) {
   1.490 +        mStreamChannel = channel;
   1.491 +        mPropertyBag = do_QueryInterface(channel);
   1.492 +        nsCOMPtr<nsIWritablePropertyBag2> writableBag =
   1.493 +            do_QueryInterface(channel);
   1.494 +        if (writableBag && jsURI->GetBaseURI()) {
   1.495 +            writableBag->SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
   1.496 +                                                jsURI->GetBaseURI());
   1.497 +        }
   1.498 +    }
   1.499 +
   1.500 +    return rv;
   1.501 +}
   1.502 +
   1.503 +//
   1.504 +// nsISupports implementation...
   1.505 +//
   1.506 +
   1.507 +NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver,
   1.508 +                  nsIStreamListener, nsIScriptChannel, nsIPropertyBag,
   1.509 +                  nsIPropertyBag2)
   1.510 +
   1.511 +//
   1.512 +// nsIRequest implementation...
   1.513 +//
   1.514 +
   1.515 +NS_IMETHODIMP
   1.516 +nsJSChannel::GetName(nsACString &aResult)
   1.517 +{
   1.518 +    return mStreamChannel->GetName(aResult);
   1.519 +}
   1.520 +
   1.521 +NS_IMETHODIMP
   1.522 +nsJSChannel::IsPending(bool *aResult)
   1.523 +{
   1.524 +    *aResult = mIsActive;
   1.525 +    return NS_OK;
   1.526 +}
   1.527 +
   1.528 +NS_IMETHODIMP
   1.529 +nsJSChannel::GetStatus(nsresult *aResult)
   1.530 +{
   1.531 +    if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) {
   1.532 +        return mStreamChannel->GetStatus(aResult);
   1.533 +    }
   1.534 +    
   1.535 +    *aResult = mStatus;
   1.536 +        
   1.537 +    return NS_OK;
   1.538 +}
   1.539 +
   1.540 +NS_IMETHODIMP
   1.541 +nsJSChannel::Cancel(nsresult aStatus)
   1.542 +{
   1.543 +    mStatus = aStatus;
   1.544 +
   1.545 +    if (mOpenedStreamChannel) {
   1.546 +        mStreamChannel->Cancel(aStatus);
   1.547 +    }
   1.548 +    
   1.549 +    return NS_OK;
   1.550 +}
   1.551 +
   1.552 +NS_IMETHODIMP
   1.553 +nsJSChannel::Suspend()
   1.554 +{
   1.555 +    return mStreamChannel->Suspend();
   1.556 +}
   1.557 +
   1.558 +NS_IMETHODIMP
   1.559 +nsJSChannel::Resume()
   1.560 +{
   1.561 +    return mStreamChannel->Resume();
   1.562 +}
   1.563 +
   1.564 +//
   1.565 +// nsIChannel implementation
   1.566 +//
   1.567 +
   1.568 +NS_IMETHODIMP
   1.569 +nsJSChannel::GetOriginalURI(nsIURI * *aURI)
   1.570 +{
   1.571 +    return mStreamChannel->GetOriginalURI(aURI);
   1.572 +}
   1.573 +
   1.574 +NS_IMETHODIMP
   1.575 +nsJSChannel::SetOriginalURI(nsIURI *aURI)
   1.576 +{
   1.577 +    return mStreamChannel->SetOriginalURI(aURI);
   1.578 +}
   1.579 +
   1.580 +NS_IMETHODIMP
   1.581 +nsJSChannel::GetURI(nsIURI * *aURI)
   1.582 +{
   1.583 +    return mStreamChannel->GetURI(aURI);
   1.584 +}
   1.585 +
   1.586 +NS_IMETHODIMP
   1.587 +nsJSChannel::Open(nsIInputStream **aResult)
   1.588 +{
   1.589 +    nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState,
   1.590 +                                           mExecutionPolicy,
   1.591 +                                           mOriginalInnerWindow);
   1.592 +    NS_ENSURE_SUCCESS(rv, rv);
   1.593 +
   1.594 +    return mStreamChannel->Open(aResult);
   1.595 +}
   1.596 +
   1.597 +NS_IMETHODIMP
   1.598 +nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
   1.599 +{
   1.600 +    NS_ENSURE_ARG(aListener);
   1.601 +
   1.602 +    // First make sure that we have a usable inner window; we'll want to make
   1.603 +    // sure that we execute against that inner and no other.
   1.604 +    nsIScriptGlobalObject* global = GetGlobalObject(this);
   1.605 +    if (!global) {
   1.606 +        return NS_ERROR_NOT_AVAILABLE;
   1.607 +    }
   1.608 +
   1.609 +    nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global));
   1.610 +    NS_ASSERTION(win, "Our global is not a window??");
   1.611 +
   1.612 +    // Make sure we create a new inner window if one doesn't already exist (see
   1.613 +    // bug 306630).
   1.614 +    mOriginalInnerWindow = win->EnsureInnerWindow();
   1.615 +    if (!mOriginalInnerWindow) {
   1.616 +        return NS_ERROR_NOT_AVAILABLE;
   1.617 +    }
   1.618 +    
   1.619 +    mListener = aListener;
   1.620 +    mContext = aContext;
   1.621 +
   1.622 +    mIsActive = true;
   1.623 +
   1.624 +    // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
   1.625 +    // notifications (and hence nsIWebProgressListener notifications) from
   1.626 +    // being dispatched.  This is required since we suppress LOAD_DOCUMENT_URI,
   1.627 +    // which means that the DocLoader would not generate document start and
   1.628 +    // stop notifications (see bug 257875).
   1.629 +    mActualLoadFlags = mLoadFlags;
   1.630 +    mLoadFlags |= LOAD_BACKGROUND;
   1.631 +
   1.632 +    // Add the javascript channel to its loadgroup so that we know if
   1.633 +    // network loads were canceled or not...
   1.634 +    nsCOMPtr<nsILoadGroup> loadGroup;
   1.635 +    mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.636 +    if (loadGroup) {
   1.637 +        nsresult rv = loadGroup->AddRequest(this, nullptr);
   1.638 +        if (NS_FAILED(rv)) {
   1.639 +            mIsActive = false;
   1.640 +            CleanupStrongRefs();
   1.641 +            return rv;
   1.642 +        }
   1.643 +    }
   1.644 +
   1.645 +    mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc();
   1.646 +    if (mDocumentOnloadBlockedOn) {
   1.647 +        // If we're a document channel, we need to actually block onload on our
   1.648 +        // _parent_ document.  This is because we don't actually set our
   1.649 +        // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the
   1.650 +        // document channel will claim to not be busy, and our parent's onload
   1.651 +        // could fire too early.
   1.652 +        nsLoadFlags loadFlags;
   1.653 +        mStreamChannel->GetLoadFlags(&loadFlags);
   1.654 +        if (loadFlags & LOAD_DOCUMENT_URI) {
   1.655 +            mDocumentOnloadBlockedOn =
   1.656 +                mDocumentOnloadBlockedOn->GetParentDocument();
   1.657 +        }
   1.658 +    }
   1.659 +    if (mDocumentOnloadBlockedOn) {
   1.660 +        mDocumentOnloadBlockedOn->BlockOnload();
   1.661 +    }
   1.662 +
   1.663 +
   1.664 +    mPopupState = win->GetPopupControlState();
   1.665 +
   1.666 +    void (nsJSChannel::*method)();
   1.667 +    if (mIsAsync) {
   1.668 +        // post an event to do the rest
   1.669 +        method = &nsJSChannel::EvaluateScript;
   1.670 +    } else {
   1.671 +        EvaluateScript();
   1.672 +        if (mOpenedStreamChannel) {
   1.673 +            // That will handle notifying things
   1.674 +            return NS_OK;
   1.675 +        }
   1.676 +
   1.677 +        NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_");
   1.678 +
   1.679 +        // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't
   1.680 +        // have any content resulting from the execution and NS_BINDING_ABORTED
   1.681 +        // if something we did causes our own load to be stopped.  Return
   1.682 +        // success in those cases, and error out in all others.
   1.683 +        if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED &&
   1.684 +            mStatus != NS_BINDING_ABORTED) {
   1.685 +            // Note that calling EvaluateScript() handled removing us from the
   1.686 +            // loadgroup and marking us as not active anymore.
   1.687 +            CleanupStrongRefs();
   1.688 +            return mStatus;
   1.689 +        }
   1.690 +
   1.691 +        // We're returning success from asyncOpen(), but we didn't open a
   1.692 +        // stream channel.  We'll have to notify ourselves, but make sure to do
   1.693 +        // it asynchronously.
   1.694 +        method = &nsJSChannel::NotifyListener;            
   1.695 +    }
   1.696 +
   1.697 +    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, method);
   1.698 +    nsresult rv = NS_DispatchToCurrentThread(ev);
   1.699 +
   1.700 +    if (NS_FAILED(rv)) {
   1.701 +        loadGroup->RemoveRequest(this, nullptr, rv);
   1.702 +        mIsActive = false;
   1.703 +        CleanupStrongRefs();
   1.704 +    }
   1.705 +    return rv;
   1.706 +}
   1.707 +
   1.708 +void
   1.709 +nsJSChannel::EvaluateScript()
   1.710 +{
   1.711 +    // Synchronously execute the script...
   1.712 +    // mIsActive is used to indicate the the request is 'busy' during the
   1.713 +    // the script evaluation phase.  This means that IsPending() will 
   1.714 +    // indicate the the request is busy while the script is executing...
   1.715 +
   1.716 +    // Note that we want to be in the loadgroup and pending while we evaluate
   1.717 +    // the script, so that we find out if the loadgroup gets canceled by the
   1.718 +    // script execution (in which case we shouldn't pump out data even if the
   1.719 +    // script returns it).
   1.720 +    
   1.721 +    if (NS_SUCCEEDED(mStatus)) {
   1.722 +        nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState,
   1.723 +                                               mExecutionPolicy,
   1.724 +                                               mOriginalInnerWindow);
   1.725 +
   1.726 +        // Note that evaluation may have canceled us, so recheck mStatus again
   1.727 +        if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) {
   1.728 +            mStatus = rv;
   1.729 +        }
   1.730 +    }
   1.731 +    
   1.732 +    // Remove the javascript channel from its loadgroup...
   1.733 +    nsCOMPtr<nsILoadGroup> loadGroup;
   1.734 +    mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.735 +    if (loadGroup) {
   1.736 +        loadGroup->RemoveRequest(this, nullptr, mStatus);
   1.737 +    }
   1.738 +
   1.739 +    // Reset load flags to their original value...
   1.740 +    mLoadFlags = mActualLoadFlags;
   1.741 +
   1.742 +    // We're no longer active, it's now up to the stream channel to do
   1.743 +    // the loading, if needed.
   1.744 +    mIsActive = false;
   1.745 +
   1.746 +    if (NS_FAILED(mStatus)) {
   1.747 +        if (mIsAsync) {
   1.748 +            NotifyListener();
   1.749 +        }
   1.750 +        return;
   1.751 +    }
   1.752 +    
   1.753 +    // EvaluateScript() succeeded, and we were not canceled, that
   1.754 +    // means there's data to parse as a result of evaluating the
   1.755 +    // script.
   1.756 +
   1.757 +    // Get the stream channels load flags (!= mLoadFlags).
   1.758 +    nsLoadFlags loadFlags;
   1.759 +    mStreamChannel->GetLoadFlags(&loadFlags);
   1.760 +
   1.761 +    uint32_t disposition;
   1.762 +    if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition)))
   1.763 +        disposition = nsIChannel::DISPOSITION_INLINE;
   1.764 +    if (loadFlags & LOAD_DOCUMENT_URI && disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
   1.765 +        // We're loaded as the document channel and not expecting to download
   1.766 +        // the result. If we go on, we'll blow away the current document. Make
   1.767 +        // sure that's ok. If so, stop all pending network loads.
   1.768 +
   1.769 +        nsCOMPtr<nsIDocShell> docShell;
   1.770 +        NS_QueryNotificationCallbacks(mStreamChannel, docShell);
   1.771 +        if (docShell) {
   1.772 +            nsCOMPtr<nsIContentViewer> cv;
   1.773 +            docShell->GetContentViewer(getter_AddRefs(cv));
   1.774 +
   1.775 +            if (cv) {
   1.776 +                bool okToUnload;
   1.777 +
   1.778 +                if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) &&
   1.779 +                    !okToUnload) {
   1.780 +                    // The user didn't want to unload the current
   1.781 +                    // page, translate this into an undefined
   1.782 +                    // return from the javascript: URL...
   1.783 +                    mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED;
   1.784 +                }
   1.785 +            }
   1.786 +        }
   1.787 +
   1.788 +        if (NS_SUCCEEDED(mStatus)) {
   1.789 +            mStatus = StopAll();
   1.790 +        }
   1.791 +    }
   1.792 +
   1.793 +    if (NS_FAILED(mStatus)) {
   1.794 +        if (mIsAsync) {
   1.795 +            NotifyListener();
   1.796 +        }
   1.797 +        return;
   1.798 +    }
   1.799 +    
   1.800 +    mStatus = mStreamChannel->AsyncOpen(this, mContext);
   1.801 +    if (NS_SUCCEEDED(mStatus)) {
   1.802 +        // mStreamChannel will call OnStartRequest and OnStopRequest on
   1.803 +        // us, so we'll be sure to call them on our listener.
   1.804 +        mOpenedStreamChannel = true;
   1.805 +
   1.806 +        // Now readd ourselves to the loadgroup so we can receive
   1.807 +        // cancellation notifications.
   1.808 +        mIsActive = true;
   1.809 +        if (loadGroup) {
   1.810 +            mStatus = loadGroup->AddRequest(this, nullptr);
   1.811 +
   1.812 +            // If AddRequest failed, that's OK.  The key is to make sure we get
   1.813 +            // cancelled if needed, and that call just canceled us if it
   1.814 +            // failed.  We'll still get notified by the stream channel when it
   1.815 +            // finishes.
   1.816 +        }
   1.817 +        
   1.818 +    } else if (mIsAsync) {
   1.819 +        NotifyListener();
   1.820 +    }
   1.821 +
   1.822 +    return;
   1.823 +}
   1.824 +
   1.825 +void
   1.826 +nsJSChannel::NotifyListener()
   1.827 +{
   1.828 +    mListener->OnStartRequest(this, mContext);
   1.829 +    mListener->OnStopRequest(this, mContext, mStatus);
   1.830 +
   1.831 +    CleanupStrongRefs();
   1.832 +}
   1.833 +
   1.834 +void
   1.835 +nsJSChannel::CleanupStrongRefs()
   1.836 +{
   1.837 +    mListener = nullptr;
   1.838 +    mContext = nullptr;
   1.839 +    mOriginalInnerWindow = nullptr;
   1.840 +    if (mDocumentOnloadBlockedOn) {
   1.841 +        mDocumentOnloadBlockedOn->UnblockOnload(false);
   1.842 +        mDocumentOnloadBlockedOn = nullptr;
   1.843 +    }
   1.844 +}
   1.845 +
   1.846 +NS_IMETHODIMP
   1.847 +nsJSChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
   1.848 +{
   1.849 +    *aLoadFlags = mLoadFlags;
   1.850 +
   1.851 +    return NS_OK;
   1.852 +}
   1.853 +
   1.854 +NS_IMETHODIMP
   1.855 +nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
   1.856 +{
   1.857 +    // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
   1.858 +    // actually right.
   1.859 +    bool bogusLoadBackground = false;
   1.860 +    if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) &&
   1.861 +        (aLoadFlags & LOAD_BACKGROUND)) {
   1.862 +        // We're getting a LOAD_BACKGROUND, but it's probably just our own fake
   1.863 +        // flag being mirrored to us.  The one exception is if our loadgroup is
   1.864 +        // LOAD_BACKGROUND.
   1.865 +        bool loadGroupIsBackground = false;
   1.866 +        nsCOMPtr<nsILoadGroup> loadGroup;
   1.867 +        mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.868 +        if (loadGroup) {
   1.869 +            nsLoadFlags loadGroupFlags;
   1.870 +            loadGroup->GetLoadFlags(&loadGroupFlags);
   1.871 +            loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0);
   1.872 +        }
   1.873 +        bogusLoadBackground = !loadGroupIsBackground;
   1.874 +    }
   1.875 +
   1.876 +    // Classifying a javascript: URI doesn't help us, and requires
   1.877 +    // NSS to boot, which we don't have in content processes.  See
   1.878 +    // https://bugzilla.mozilla.org/show_bug.cgi?id=617838.
   1.879 +    aLoadFlags &= ~LOAD_CLASSIFY_URI;
   1.880 +
   1.881 +    // Since the javascript channel is never the actual channel that
   1.882 +    // any data is loaded through, don't ever set the
   1.883 +    // LOAD_DOCUMENT_URI flag on it, since that could lead to two
   1.884 +    // 'document channels' in the loadgroup if a javascript: URL is
   1.885 +    // loaded while a document is being loaded in the same window.
   1.886 +
   1.887 +    // XXXbz this, and a whole lot of other hackery, could go away if we'd just
   1.888 +    // cancel the current document load on javascript: load start like IE does.
   1.889 +    
   1.890 +    mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
   1.891 +
   1.892 +    if (bogusLoadBackground) {
   1.893 +        aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND;
   1.894 +    }
   1.895 +
   1.896 +    mActualLoadFlags = aLoadFlags;
   1.897 +
   1.898 +    // ... but the underlying stream channel should get this bit, if
   1.899 +    // set, since that'll be the real document channel if the
   1.900 +    // javascript: URL generated data.
   1.901 +
   1.902 +    return mStreamChannel->SetLoadFlags(aLoadFlags);
   1.903 +}
   1.904 +
   1.905 +NS_IMETHODIMP
   1.906 +nsJSChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
   1.907 +{
   1.908 +    return mStreamChannel->GetLoadGroup(aLoadGroup);
   1.909 +}
   1.910 +
   1.911 +NS_IMETHODIMP
   1.912 +nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
   1.913 +{
   1.914 +    if (aLoadGroup) {
   1.915 +        bool streamPending;
   1.916 +        nsresult rv = mStreamChannel->IsPending(&streamPending);
   1.917 +        NS_ENSURE_SUCCESS(rv, rv);
   1.918 +
   1.919 +        if (streamPending) {
   1.920 +            nsCOMPtr<nsILoadGroup> curLoadGroup;
   1.921 +            mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup));
   1.922 +
   1.923 +            if (aLoadGroup != curLoadGroup) {
   1.924 +                // Move the stream channel to our new loadgroup.  Make sure to
   1.925 +                // add it before removing it, so that we don't trigger onload
   1.926 +                // by accident.
   1.927 +                aLoadGroup->AddRequest(mStreamChannel, nullptr);
   1.928 +                if (curLoadGroup) {
   1.929 +                    curLoadGroup->RemoveRequest(mStreamChannel, nullptr,
   1.930 +                                                NS_BINDING_RETARGETED);
   1.931 +                }
   1.932 +            }
   1.933 +        }
   1.934 +    }
   1.935 +    
   1.936 +    return mStreamChannel->SetLoadGroup(aLoadGroup);
   1.937 +}
   1.938 +
   1.939 +NS_IMETHODIMP
   1.940 +nsJSChannel::GetOwner(nsISupports* *aOwner)
   1.941 +{
   1.942 +    return mStreamChannel->GetOwner(aOwner);
   1.943 +}
   1.944 +
   1.945 +NS_IMETHODIMP
   1.946 +nsJSChannel::SetOwner(nsISupports* aOwner)
   1.947 +{
   1.948 +    return mStreamChannel->SetOwner(aOwner);
   1.949 +}
   1.950 +
   1.951 +NS_IMETHODIMP
   1.952 +nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
   1.953 +{
   1.954 +    return mStreamChannel->GetNotificationCallbacks(aCallbacks);
   1.955 +}
   1.956 +
   1.957 +NS_IMETHODIMP
   1.958 +nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
   1.959 +{
   1.960 +    return mStreamChannel->SetNotificationCallbacks(aCallbacks);
   1.961 +}
   1.962 +
   1.963 +NS_IMETHODIMP 
   1.964 +nsJSChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
   1.965 +{
   1.966 +    return mStreamChannel->GetSecurityInfo(aSecurityInfo);
   1.967 +}
   1.968 +
   1.969 +NS_IMETHODIMP
   1.970 +nsJSChannel::GetContentType(nsACString &aContentType)
   1.971 +{
   1.972 +    return mStreamChannel->GetContentType(aContentType);
   1.973 +}
   1.974 +
   1.975 +NS_IMETHODIMP
   1.976 +nsJSChannel::SetContentType(const nsACString &aContentType)
   1.977 +{
   1.978 +    return mStreamChannel->SetContentType(aContentType);
   1.979 +}
   1.980 +
   1.981 +NS_IMETHODIMP
   1.982 +nsJSChannel::GetContentCharset(nsACString &aContentCharset)
   1.983 +{
   1.984 +    return mStreamChannel->GetContentCharset(aContentCharset);
   1.985 +}
   1.986 +
   1.987 +NS_IMETHODIMP
   1.988 +nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
   1.989 +{
   1.990 +    return mStreamChannel->SetContentCharset(aContentCharset);
   1.991 +}
   1.992 +
   1.993 +NS_IMETHODIMP
   1.994 +nsJSChannel::GetContentDisposition(uint32_t *aContentDisposition)
   1.995 +{
   1.996 +    return mStreamChannel->GetContentDisposition(aContentDisposition);
   1.997 +}
   1.998 +
   1.999 +NS_IMETHODIMP
  1.1000 +nsJSChannel::SetContentDisposition(uint32_t aContentDisposition)
  1.1001 +{
  1.1002 +    return mStreamChannel->SetContentDisposition(aContentDisposition);
  1.1003 +}
  1.1004 +
  1.1005 +NS_IMETHODIMP
  1.1006 +nsJSChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
  1.1007 +{
  1.1008 +    return mStreamChannel->GetContentDispositionFilename(aContentDispositionFilename);
  1.1009 +}
  1.1010 +
  1.1011 +NS_IMETHODIMP
  1.1012 +nsJSChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
  1.1013 +{
  1.1014 +    return mStreamChannel->SetContentDispositionFilename(aContentDispositionFilename);
  1.1015 +}
  1.1016 +
  1.1017 +NS_IMETHODIMP
  1.1018 +nsJSChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
  1.1019 +{
  1.1020 +    return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader);
  1.1021 +}
  1.1022 +
  1.1023 +NS_IMETHODIMP
  1.1024 +nsJSChannel::GetContentLength(int64_t *aContentLength)
  1.1025 +{
  1.1026 +    return mStreamChannel->GetContentLength(aContentLength);
  1.1027 +}
  1.1028 +
  1.1029 +NS_IMETHODIMP
  1.1030 +nsJSChannel::SetContentLength(int64_t aContentLength)
  1.1031 +{
  1.1032 +    return mStreamChannel->SetContentLength(aContentLength);
  1.1033 +}
  1.1034 +
  1.1035 +NS_IMETHODIMP
  1.1036 +nsJSChannel::OnStartRequest(nsIRequest* aRequest,
  1.1037 +                            nsISupports* aContext)
  1.1038 +{
  1.1039 +    NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
  1.1040 +
  1.1041 +    return mListener->OnStartRequest(this, aContext);    
  1.1042 +}
  1.1043 +
  1.1044 +NS_IMETHODIMP
  1.1045 +nsJSChannel::OnDataAvailable(nsIRequest* aRequest,
  1.1046 +                             nsISupports* aContext, 
  1.1047 +                             nsIInputStream* aInputStream,
  1.1048 +                             uint64_t aOffset,
  1.1049 +                             uint32_t aCount)
  1.1050 +{
  1.1051 +    NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
  1.1052 +
  1.1053 +    return mListener->OnDataAvailable(this, aContext, aInputStream, aOffset,
  1.1054 +                                      aCount);
  1.1055 +}
  1.1056 +
  1.1057 +NS_IMETHODIMP
  1.1058 +nsJSChannel::OnStopRequest(nsIRequest* aRequest,
  1.1059 +                           nsISupports* aContext,
  1.1060 +                           nsresult aStatus)
  1.1061 +{
  1.1062 +    NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED);
  1.1063 +
  1.1064 +    nsCOMPtr<nsIStreamListener> listener = mListener;
  1.1065 +
  1.1066 +    CleanupStrongRefs();
  1.1067 +
  1.1068 +    // Make sure aStatus matches what GetStatus() returns
  1.1069 +    if (NS_FAILED(mStatus)) {
  1.1070 +        aStatus = mStatus;
  1.1071 +    }
  1.1072 +    
  1.1073 +    nsresult rv = listener->OnStopRequest(this, aContext, aStatus);
  1.1074 +
  1.1075 +    nsCOMPtr<nsILoadGroup> loadGroup;
  1.1076 +    mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
  1.1077 +    if (loadGroup) {
  1.1078 +        loadGroup->RemoveRequest(this, nullptr, mStatus);
  1.1079 +    }
  1.1080 +
  1.1081 +    mIsActive = false;
  1.1082 +
  1.1083 +    return rv;
  1.1084 +}
  1.1085 +
  1.1086 +NS_IMETHODIMP
  1.1087 +nsJSChannel::SetExecutionPolicy(uint32_t aPolicy)
  1.1088 +{
  1.1089 +    NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL);
  1.1090 +    
  1.1091 +    mExecutionPolicy = aPolicy;
  1.1092 +    return NS_OK;
  1.1093 +}
  1.1094 +
  1.1095 +NS_IMETHODIMP
  1.1096 +nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy)
  1.1097 +{
  1.1098 +    *aPolicy = mExecutionPolicy;
  1.1099 +    return NS_OK;
  1.1100 +}
  1.1101 +
  1.1102 +NS_IMETHODIMP
  1.1103 +nsJSChannel::SetExecuteAsync(bool aIsAsync)
  1.1104 +{
  1.1105 +    if (!mIsActive) {
  1.1106 +        mIsAsync = aIsAsync;
  1.1107 +    }
  1.1108 +    // else ignore this call
  1.1109 +    NS_WARN_IF_FALSE(!mIsActive, "Calling SetExecuteAsync on active channel?");
  1.1110 +
  1.1111 +    return NS_OK;
  1.1112 +}
  1.1113 +
  1.1114 +NS_IMETHODIMP
  1.1115 +nsJSChannel::GetExecuteAsync(bool* aIsAsync)
  1.1116 +{
  1.1117 +    *aIsAsync = mIsAsync;
  1.1118 +    return NS_OK;
  1.1119 +}
  1.1120 +
  1.1121 +////////////////////////////////////////////////////////////////////////////////
  1.1122 +
  1.1123 +nsJSProtocolHandler::nsJSProtocolHandler()
  1.1124 +{
  1.1125 +}
  1.1126 +
  1.1127 +nsresult
  1.1128 +nsJSProtocolHandler::Init()
  1.1129 +{
  1.1130 +    return NS_OK;
  1.1131 +}
  1.1132 +
  1.1133 +nsJSProtocolHandler::~nsJSProtocolHandler()
  1.1134 +{
  1.1135 +}
  1.1136 +
  1.1137 +NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler)
  1.1138 +
  1.1139 +nsresult
  1.1140 +nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
  1.1141 +{
  1.1142 +    if (aOuter)
  1.1143 +        return NS_ERROR_NO_AGGREGATION;
  1.1144 +
  1.1145 +    nsJSProtocolHandler* ph = new nsJSProtocolHandler();
  1.1146 +    if (!ph)
  1.1147 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1148 +    NS_ADDREF(ph);
  1.1149 +    nsresult rv = ph->Init();
  1.1150 +    if (NS_SUCCEEDED(rv)) {
  1.1151 +        rv = ph->QueryInterface(aIID, aResult);
  1.1152 +    }
  1.1153 +    NS_RELEASE(ph);
  1.1154 +    return rv;
  1.1155 +}
  1.1156 +
  1.1157 +nsresult 
  1.1158 +nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString &aSpec, const char *aCharset, 
  1.1159 +                                    nsACString &aUTF8Spec)
  1.1160 +{
  1.1161 +  aUTF8Spec.Truncate();
  1.1162 +
  1.1163 +  nsresult rv;
  1.1164 +  
  1.1165 +  if (!mTextToSubURI) {
  1.1166 +    mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
  1.1167 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1168 +  }
  1.1169 +  nsAutoString uStr;
  1.1170 +  rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, uStr);
  1.1171 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1172 +
  1.1173 +  if (!IsASCII(uStr))
  1.1174 +    NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr), esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec);
  1.1175 +
  1.1176 +  return NS_OK;
  1.1177 +}
  1.1178 +
  1.1179 +////////////////////////////////////////////////////////////////////////////////
  1.1180 +// nsIProtocolHandler methods:
  1.1181 +
  1.1182 +NS_IMETHODIMP
  1.1183 +nsJSProtocolHandler::GetScheme(nsACString &result)
  1.1184 +{
  1.1185 +    result = "javascript";
  1.1186 +    return NS_OK;
  1.1187 +}
  1.1188 +
  1.1189 +NS_IMETHODIMP
  1.1190 +nsJSProtocolHandler::GetDefaultPort(int32_t *result)
  1.1191 +{
  1.1192 +    *result = -1;        // no port for javascript: URLs
  1.1193 +    return NS_OK;
  1.1194 +}
  1.1195 +
  1.1196 +NS_IMETHODIMP
  1.1197 +nsJSProtocolHandler::GetProtocolFlags(uint32_t *result)
  1.1198 +{
  1.1199 +    *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
  1.1200 +        URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_OPENING_EXECUTES_SCRIPT;
  1.1201 +    return NS_OK;
  1.1202 +}
  1.1203 +
  1.1204 +NS_IMETHODIMP
  1.1205 +nsJSProtocolHandler::NewURI(const nsACString &aSpec,
  1.1206 +                            const char *aCharset,
  1.1207 +                            nsIURI *aBaseURI,
  1.1208 +                            nsIURI **result)
  1.1209 +{
  1.1210 +    nsresult rv;
  1.1211 +
  1.1212 +    // javascript: URLs (currently) have no additional structure beyond that
  1.1213 +    // provided by standard URLs, so there is no "outer" object given to
  1.1214 +    // CreateInstance.
  1.1215 +
  1.1216 +    nsCOMPtr<nsIURI> url = new nsJSURI(aBaseURI);
  1.1217 +
  1.1218 +    if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset))
  1.1219 +      rv = url->SetSpec(aSpec);
  1.1220 +    else {
  1.1221 +      nsAutoCString utf8Spec;
  1.1222 +      rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
  1.1223 +      if (NS_SUCCEEDED(rv)) {
  1.1224 +        if (utf8Spec.IsEmpty())
  1.1225 +          rv = url->SetSpec(aSpec);
  1.1226 +        else
  1.1227 +          rv = url->SetSpec(utf8Spec);
  1.1228 +      }
  1.1229 +    }
  1.1230 +
  1.1231 +    if (NS_FAILED(rv)) {
  1.1232 +        return rv;
  1.1233 +    }
  1.1234 +
  1.1235 +    url.forget(result);
  1.1236 +    return rv;
  1.1237 +}
  1.1238 +
  1.1239 +NS_IMETHODIMP
  1.1240 +nsJSProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
  1.1241 +{
  1.1242 +    nsresult rv;
  1.1243 +    nsJSChannel * channel;
  1.1244 +
  1.1245 +    NS_ENSURE_ARG_POINTER(uri);
  1.1246 +
  1.1247 +    channel = new nsJSChannel();
  1.1248 +    if (!channel) {
  1.1249 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1250 +    }
  1.1251 +    NS_ADDREF(channel);
  1.1252 +
  1.1253 +    rv = channel->Init(uri);
  1.1254 +    if (NS_SUCCEEDED(rv)) {
  1.1255 +        *result = channel;
  1.1256 +        NS_ADDREF(*result);
  1.1257 +    }
  1.1258 +    NS_RELEASE(channel);
  1.1259 +    return rv;
  1.1260 +}
  1.1261 +
  1.1262 +NS_IMETHODIMP 
  1.1263 +nsJSProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
  1.1264 +{
  1.1265 +    // don't override anything.  
  1.1266 +    *_retval = false;
  1.1267 +    return NS_OK;
  1.1268 +}
  1.1269 +
  1.1270 +////////////////////////////////////////////////////////////
  1.1271 +// nsJSURI implementation
  1.1272 +static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
  1.1273 +                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
  1.1274 +
  1.1275 +
  1.1276 +NS_IMPL_ADDREF_INHERITED(nsJSURI, nsSimpleURI)
  1.1277 +NS_IMPL_RELEASE_INHERITED(nsJSURI, nsSimpleURI)
  1.1278 +
  1.1279 +NS_INTERFACE_MAP_BEGIN(nsJSURI)
  1.1280 +  if (aIID.Equals(kJSURICID))
  1.1281 +      foundInterface = static_cast<nsIURI*>(this);
  1.1282 +  else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
  1.1283 +      // Need to return explicitly here, because if we just set foundInterface
  1.1284 +      // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
  1.1285 +      // nsSimplURI::QueryInterface and finding something for this CID.
  1.1286 +      *aInstancePtr = nullptr;
  1.1287 +      return NS_NOINTERFACE;
  1.1288 +  }
  1.1289 +  else
  1.1290 +NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
  1.1291 +
  1.1292 +// nsISerializable methods:
  1.1293 +
  1.1294 +NS_IMETHODIMP
  1.1295 +nsJSURI::Read(nsIObjectInputStream* aStream)
  1.1296 +{
  1.1297 +    nsresult rv = nsSimpleURI::Read(aStream);
  1.1298 +    if (NS_FAILED(rv)) return rv;
  1.1299 +
  1.1300 +    bool haveBase;
  1.1301 +    rv = aStream->ReadBoolean(&haveBase);
  1.1302 +    if (NS_FAILED(rv)) return rv;
  1.1303 +
  1.1304 +    if (haveBase) {
  1.1305 +        nsCOMPtr<nsISupports> supports;
  1.1306 +        rv = aStream->ReadObject(true, getter_AddRefs(supports));
  1.1307 +        if (NS_FAILED(rv)) return rv;
  1.1308 +        mBaseURI = do_QueryInterface(supports);
  1.1309 +    }
  1.1310 +
  1.1311 +    return NS_OK;
  1.1312 +}
  1.1313 +
  1.1314 +NS_IMETHODIMP
  1.1315 +nsJSURI::Write(nsIObjectOutputStream* aStream)
  1.1316 +{
  1.1317 +    nsresult rv = nsSimpleURI::Write(aStream);
  1.1318 +    if (NS_FAILED(rv)) return rv;
  1.1319 +
  1.1320 +    rv = aStream->WriteBoolean(mBaseURI != nullptr);
  1.1321 +    if (NS_FAILED(rv)) return rv;
  1.1322 +
  1.1323 +    if (mBaseURI) {
  1.1324 +        rv = aStream->WriteObject(mBaseURI, true);
  1.1325 +        if (NS_FAILED(rv)) return rv;
  1.1326 +    }
  1.1327 +
  1.1328 +    return NS_OK;
  1.1329 +}
  1.1330 +
  1.1331 +// nsSimpleURI methods:
  1.1332 +/* virtual */ nsSimpleURI*
  1.1333 +nsJSURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */)
  1.1334 +{
  1.1335 +    nsCOMPtr<nsIURI> baseClone;
  1.1336 +    if (mBaseURI) {
  1.1337 +      // Note: We preserve ref on *base* URI, regardless of ref handling mode.
  1.1338 +      nsresult rv = mBaseURI->Clone(getter_AddRefs(baseClone));
  1.1339 +      if (NS_FAILED(rv)) {
  1.1340 +        return nullptr;
  1.1341 +      }
  1.1342 +    }
  1.1343 +
  1.1344 +    return new nsJSURI(baseClone);
  1.1345 +}
  1.1346 +
  1.1347 +/* virtual */ nsresult
  1.1348 +nsJSURI::EqualsInternal(nsIURI* aOther,
  1.1349 +                        nsSimpleURI::RefHandlingEnum aRefHandlingMode,
  1.1350 +                        bool* aResult)
  1.1351 +{
  1.1352 +    NS_ENSURE_ARG_POINTER(aOther);
  1.1353 +    NS_PRECONDITION(aResult, "null pointer for outparam");
  1.1354 +
  1.1355 +    nsRefPtr<nsJSURI> otherJSURI;
  1.1356 +    nsresult rv = aOther->QueryInterface(kJSURICID,
  1.1357 +                                         getter_AddRefs(otherJSURI));
  1.1358 +    if (NS_FAILED(rv)) {
  1.1359 +        *aResult = false; // aOther is not a nsJSURI --> not equal.
  1.1360 +        return NS_OK;
  1.1361 +    }
  1.1362 +
  1.1363 +    // Compare the member data that our base class knows about.
  1.1364 +    if (!nsSimpleURI::EqualsInternal(otherJSURI, aRefHandlingMode)) {
  1.1365 +        *aResult = false;
  1.1366 +        return NS_OK;
  1.1367 +    }
  1.1368 +
  1.1369 +    // Compare the piece of additional member data that we add to base class.
  1.1370 +    nsIURI* otherBaseURI = otherJSURI->GetBaseURI();
  1.1371 +
  1.1372 +    if (mBaseURI) {
  1.1373 +        // (As noted in StartClone, we always honor refs on mBaseURI)
  1.1374 +        return mBaseURI->Equals(otherBaseURI, aResult);
  1.1375 +    }
  1.1376 +
  1.1377 +    *aResult = !otherBaseURI;
  1.1378 +    return NS_OK;
  1.1379 +}
  1.1380 +
  1.1381 +NS_IMETHODIMP 
  1.1382 +nsJSURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
  1.1383 +{
  1.1384 +    *aClassIDNoAlloc = kJSURICID;
  1.1385 +    return NS_OK;
  1.1386 +}
  1.1387 +

mercurial