michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=4 sw=4 et tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "jsapi.h" michael@0: #include "jswrapper.h" michael@0: #include "nsCRT.h" michael@0: #include "nsError.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsJSProtocolHandler.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIWindowMediator.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "prprf.h" michael@0: #include "nsEscape.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIScriptChannel.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: #include "nsIWritablePropertyBag2.h" michael@0: #include "nsIContentSecurityPolicy.h" michael@0: #include "nsSandboxFlags.h" michael@0: #include "mozilla/dom/ScriptSettings.h" michael@0: michael@0: using mozilla::dom::AutoEntryScript; michael@0: michael@0: static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID); michael@0: michael@0: class nsJSThunk : public nsIInputStream michael@0: { michael@0: public: michael@0: nsJSThunk(); michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream) michael@0: michael@0: nsresult Init(nsIURI* uri); michael@0: nsresult EvaluateScript(nsIChannel *aChannel, michael@0: PopupControlState aPopupState, michael@0: uint32_t aExecutionPolicy, michael@0: nsPIDOMWindow *aOriginalInnerWindow); michael@0: michael@0: protected: michael@0: virtual ~nsJSThunk(); michael@0: michael@0: nsCOMPtr mInnerStream; michael@0: nsCString mScript; michael@0: nsCString mURL; michael@0: }; michael@0: michael@0: // michael@0: // nsISupports implementation... michael@0: // michael@0: NS_IMPL_ISUPPORTS(nsJSThunk, nsIInputStream) michael@0: michael@0: michael@0: nsJSThunk::nsJSThunk() michael@0: { michael@0: } michael@0: michael@0: nsJSThunk::~nsJSThunk() michael@0: { michael@0: } michael@0: michael@0: nsresult nsJSThunk::Init(nsIURI* uri) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(uri); michael@0: michael@0: // Get the script string to evaluate... michael@0: nsresult rv = uri->GetPath(mScript); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Get the url. michael@0: rv = uri->GetSpec(mURL); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: IsISO88591(const nsString& aString) michael@0: { michael@0: for (nsString::const_char_iterator c = aString.BeginReading(), michael@0: c_end = aString.EndReading(); michael@0: c < c_end; ++c) { michael@0: if (*c > 255) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static michael@0: nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel) michael@0: { michael@0: // Get the global object owner from the channel michael@0: nsCOMPtr docShell; michael@0: NS_QueryNotificationCallbacks(aChannel, docShell); michael@0: if (!docShell) { michael@0: NS_WARNING("Unable to get a docShell from the channel!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // So far so good: get the script global from its docshell michael@0: nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject(); michael@0: michael@0: NS_ASSERTION(global, michael@0: "Unable to get an nsIScriptGlobalObject from the " michael@0: "docShell!"); michael@0: return global; michael@0: } michael@0: michael@0: nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel, michael@0: PopupControlState aPopupState, michael@0: uint32_t aExecutionPolicy, michael@0: nsPIDOMWindow *aOriginalInnerWindow) michael@0: { michael@0: if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) { michael@0: // Nothing to do here. michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: michael@0: NS_ENSURE_ARG_POINTER(aChannel); michael@0: michael@0: // Get principal of code for execution michael@0: nsCOMPtr owner; michael@0: aChannel->GetOwner(getter_AddRefs(owner)); michael@0: nsCOMPtr principal = do_QueryInterface(owner); michael@0: if (!principal) { michael@0: // No execution without a principal! michael@0: NS_ASSERTION(!owner, "Non-principal owner?"); michael@0: NS_WARNING("No principal to execute JS with"); michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: // CSP check: javascript: URIs disabled unless "inline" scripts are michael@0: // allowed. michael@0: nsCOMPtr csp; michael@0: rv = principal->GetCsp(getter_AddRefs(csp)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (csp) { michael@0: bool allowsInline = true; michael@0: bool reportViolations = false; michael@0: rv = csp->GetAllowsInlineScript(&reportViolations, &allowsInline); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (reportViolations) { michael@0: // gather information to log with violation report michael@0: nsCOMPtr uri; michael@0: principal->GetURI(getter_AddRefs(uri)); michael@0: nsAutoCString asciiSpec; michael@0: uri->GetAsciiSpec(asciiSpec); michael@0: csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT, michael@0: NS_ConvertUTF8toUTF16(asciiSpec), michael@0: NS_ConvertUTF8toUTF16(mURL), michael@0: 0, michael@0: EmptyString(), michael@0: EmptyString()); michael@0: } michael@0: michael@0: //return early if inline scripts are not allowed michael@0: if (!allowsInline) { michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: } michael@0: michael@0: // Get the global object we should be running on. michael@0: nsIScriptGlobalObject* global = GetGlobalObject(aChannel); michael@0: if (!global) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Sandboxed document check: javascript: URI's are disabled michael@0: // in a sandboxed document unless 'allow-scripts' was specified. michael@0: nsIDocument* doc = aOriginalInnerWindow->GetExtantDoc(); michael@0: if (doc && (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS)) { michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: michael@0: // Push our popup control state michael@0: nsAutoPopupStatePusher popupStatePusher(aPopupState); michael@0: michael@0: // Make sure we still have the same inner window as we used to. michael@0: nsCOMPtr win = do_QueryInterface(global); michael@0: nsPIDOMWindow *innerWin = win->GetCurrentInnerWindow(); michael@0: michael@0: if (innerWin != aOriginalInnerWindow) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr innerGlobal = do_QueryInterface(innerWin); michael@0: michael@0: nsCOMPtr domWindow(do_QueryInterface(global, &rv)); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // So far so good: get the script context from its owner. michael@0: nsCOMPtr scriptContext = global->GetContext(); michael@0: if (!scriptContext) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoCString script(mScript); michael@0: // Unescape the script michael@0: NS_UnescapeURL(script); michael@0: michael@0: michael@0: nsCOMPtr securityManager; michael@0: securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: bool useSandbox = michael@0: (aExecutionPolicy == nsIScriptChannel::EXECUTE_IN_SANDBOX); michael@0: michael@0: // New script entry point required, due to the "Create a script" step of michael@0: // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol michael@0: AutoEntryScript entryScript(innerGlobal, true, michael@0: scriptContext->GetNativeContext()); michael@0: JSContext* cx = entryScript.cx(); michael@0: JS::Rooted globalJSObject(cx, innerGlobal->GetGlobalJSObject()); michael@0: NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED); michael@0: michael@0: if (!useSandbox) { michael@0: //-- Don't outside a sandbox unless the script principal subsumes the michael@0: // principal of the context. michael@0: nsIPrincipal* objectPrincipal = nsContentUtils::GetObjectPrincipal(globalJSObject); michael@0: michael@0: bool subsumes; michael@0: rv = principal->Subsumes(objectPrincipal, &subsumes); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: useSandbox = !subsumes; michael@0: } michael@0: michael@0: JS::Rooted v (cx, JS::UndefinedValue()); michael@0: // Finally, we have everything needed to evaluate the expression. michael@0: if (useSandbox) { michael@0: // We were asked to use a sandbox, or the channel owner isn't allowed michael@0: // to execute in this context. Evaluate the javascript URL in a michael@0: // sandbox to prevent it from accessing data it doesn't have michael@0: // permissions to access. michael@0: michael@0: // First check to make sure it's OK to evaluate this script to michael@0: // start with. For example, script could be disabled. michael@0: if (!securityManager->ScriptAllowed(globalJSObject)) { michael@0: // Treat this as returning undefined from the script. That's what michael@0: // nsJSContext does. michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: michael@0: nsIXPConnect *xpc = nsContentUtils::XPConnect(); michael@0: michael@0: nsCOMPtr sandbox; michael@0: // Important: Use a null principal here michael@0: rv = xpc->CreateSandbox(cx, nullptr, getter_AddRefs(sandbox)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // The nsXPConnect sandbox API gives us a wrapper to the sandbox for michael@0: // our current compartment. Because our current context doesn't necessarily michael@0: // subsume that of the sandbox, we want to unwrap and enter the sandbox's michael@0: // compartment. It's a shame that the APIs here are so clunkly. :-( michael@0: JS::Rooted sandboxObj(cx, sandbox->GetJSObject()); michael@0: NS_ENSURE_STATE(sandboxObj); michael@0: sandboxObj = js::UncheckedUnwrap(sandboxObj); michael@0: JSAutoCompartment ac(cx, sandboxObj); michael@0: michael@0: // Push our JSContext on the context stack so the EvalInSandboxObject call (and michael@0: // JS_ReportPendingException, if relevant) will use the principal of cx. michael@0: nsCxPusher pusher; michael@0: pusher.Push(cx); michael@0: rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), michael@0: /* filename = */ nullptr, cx, michael@0: sandboxObj, true, &v); michael@0: michael@0: // Propagate and report exceptions that happened in the michael@0: // sandbox. michael@0: if (JS_IsExceptionPending(cx)) { michael@0: JS_ReportPendingException(cx); michael@0: } michael@0: } else { michael@0: // No need to use the sandbox, evaluate the script directly in michael@0: // the given scope. michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(mURL.get(), 1) michael@0: .setVersion(JSVERSION_DEFAULT); michael@0: nsJSUtils::EvaluateOptions evalOptions; michael@0: evalOptions.setCoerceToString(true); michael@0: rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script), michael@0: globalJSObject, options, evalOptions, &v); michael@0: michael@0: // If there's an error on cx as a result of that call, report michael@0: // it now -- either we're just running under the event loop, michael@0: // so we shouldn't propagate JS exceptions out of here, or we michael@0: // can't be sure that our caller is JS (and if it's not we'll michael@0: // lose the error), or it might be JS that then proceeds to michael@0: // cause an error of its own (which will also make us lose michael@0: // this error). michael@0: ::JS_ReportPendingException(cx); michael@0: } michael@0: michael@0: // If we took the sandbox path above, v might be in the sandbox michael@0: // compartment. michael@0: if (!JS_WrapValue(cx, &v)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) { michael@0: return NS_ERROR_MALFORMED_URI; michael@0: } else if (v.isUndefined()) { michael@0: return NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } else { michael@0: nsDependentJSString result; michael@0: if (!result.init(cx, v)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: char *bytes; michael@0: uint32_t bytesLen; michael@0: NS_NAMED_LITERAL_CSTRING(isoCharset, "ISO-8859-1"); michael@0: NS_NAMED_LITERAL_CSTRING(utf8Charset, "UTF-8"); michael@0: const nsCString *charset; michael@0: if (IsISO88591(result)) { michael@0: // For compatibility, if the result is ISO-8859-1, we use michael@0: // ISO-8859-1, so that people can compatibly create images michael@0: // using javascript: URLs. michael@0: bytes = ToNewCString(result); michael@0: bytesLen = result.Length(); michael@0: charset = &isoCharset; michael@0: } michael@0: else { michael@0: bytes = ToNewUTF8String(result, &bytesLen); michael@0: charset = &utf8Charset; michael@0: } michael@0: aChannel->SetContentCharset(*charset); michael@0: if (bytes) michael@0: rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream), michael@0: bytes, bytesLen, michael@0: NS_ASSIGNMENT_ADOPT); michael@0: else michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class nsJSChannel : public nsIChannel, michael@0: public nsIStreamListener, michael@0: public nsIScriptChannel, michael@0: public nsIPropertyBag2 michael@0: { michael@0: public: michael@0: nsJSChannel(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIREQUEST michael@0: NS_DECL_NSICHANNEL michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSISCRIPTCHANNEL michael@0: NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag) michael@0: NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag) michael@0: michael@0: nsresult Init(nsIURI *aURI); michael@0: michael@0: // Actually evaluate the script. michael@0: void EvaluateScript(); michael@0: michael@0: protected: michael@0: virtual ~nsJSChannel(); michael@0: michael@0: nsresult StopAll(); michael@0: michael@0: void NotifyListener(); michael@0: michael@0: void CleanupStrongRefs(); michael@0: michael@0: protected: michael@0: nsCOMPtr mStreamChannel; michael@0: nsCOMPtr mPropertyBag; michael@0: nsCOMPtr mListener; // Our final listener michael@0: nsCOMPtr mContext; // The context passed to AsyncOpen michael@0: nsCOMPtr mOriginalInnerWindow; // The inner window our load michael@0: // started against. michael@0: // If we blocked onload on a document in AsyncOpen, this is the document we michael@0: // did it on. michael@0: nsCOMPtr mDocumentOnloadBlockedOn; michael@0: michael@0: nsresult mStatus; // Our status michael@0: michael@0: nsLoadFlags mLoadFlags; michael@0: nsLoadFlags mActualLoadFlags; // See AsyncOpen michael@0: michael@0: nsRefPtr mIOThunk; michael@0: PopupControlState mPopupState; michael@0: uint32_t mExecutionPolicy; michael@0: bool mIsAsync; michael@0: bool mIsActive; michael@0: bool mOpenedStreamChannel; michael@0: }; michael@0: michael@0: nsJSChannel::nsJSChannel() : michael@0: mStatus(NS_OK), michael@0: mLoadFlags(LOAD_NORMAL), michael@0: mActualLoadFlags(LOAD_NORMAL), michael@0: mPopupState(openOverridden), michael@0: mExecutionPolicy(EXECUTE_IN_SANDBOX), michael@0: mIsAsync(true), michael@0: mIsActive(false), michael@0: mOpenedStreamChannel(false) michael@0: { michael@0: } michael@0: michael@0: nsJSChannel::~nsJSChannel() michael@0: { michael@0: } michael@0: michael@0: nsresult nsJSChannel::StopAll() michael@0: { michael@0: nsresult rv = NS_ERROR_UNEXPECTED; michael@0: nsCOMPtr webNav; michael@0: NS_QueryNotificationCallbacks(mStreamChannel, webNav); michael@0: michael@0: NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!"); michael@0: if (webNav) { michael@0: rv = webNav->Stop(nsIWebNavigation::STOP_ALL); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsJSChannel::Init(nsIURI *aURI) michael@0: { michael@0: nsRefPtr jsURI; michael@0: nsresult rv = aURI->QueryInterface(kJSURICID, michael@0: getter_AddRefs(jsURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Create the nsIStreamIO layer used by the nsIStreamIOChannel. michael@0: mIOThunk = new nsJSThunk(); michael@0: if (!mIOThunk) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Create a stock input stream channel... michael@0: // Remember, until AsyncOpen is called, the script will not be evaluated michael@0: // and the underlying Input Stream will not be created... michael@0: nsCOMPtr channel; michael@0: michael@0: // If the resultant script evaluation actually does return a value, we michael@0: // treat it as html. michael@0: rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk, michael@0: NS_LITERAL_CSTRING("text/html")); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mIOThunk->Init(aURI); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mStreamChannel = channel; michael@0: mPropertyBag = do_QueryInterface(channel); michael@0: nsCOMPtr writableBag = michael@0: do_QueryInterface(channel); michael@0: if (writableBag && jsURI->GetBaseURI()) { michael@0: writableBag->SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), michael@0: jsURI->GetBaseURI()); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // michael@0: // nsISupports implementation... michael@0: // michael@0: michael@0: NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver, michael@0: nsIStreamListener, nsIScriptChannel, nsIPropertyBag, michael@0: nsIPropertyBag2) michael@0: michael@0: // michael@0: // nsIRequest implementation... michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetName(nsACString &aResult) michael@0: { michael@0: return mStreamChannel->GetName(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::IsPending(bool *aResult) michael@0: { michael@0: *aResult = mIsActive; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetStatus(nsresult *aResult) michael@0: { michael@0: if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) { michael@0: return mStreamChannel->GetStatus(aResult); michael@0: } michael@0: michael@0: *aResult = mStatus; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::Cancel(nsresult aStatus) michael@0: { michael@0: mStatus = aStatus; michael@0: michael@0: if (mOpenedStreamChannel) { michael@0: mStreamChannel->Cancel(aStatus); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::Suspend() michael@0: { michael@0: return mStreamChannel->Suspend(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::Resume() michael@0: { michael@0: return mStreamChannel->Resume(); michael@0: } michael@0: michael@0: // michael@0: // nsIChannel implementation michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetOriginalURI(nsIURI * *aURI) michael@0: { michael@0: return mStreamChannel->GetOriginalURI(aURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetOriginalURI(nsIURI *aURI) michael@0: { michael@0: return mStreamChannel->SetOriginalURI(aURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetURI(nsIURI * *aURI) michael@0: { michael@0: return mStreamChannel->GetURI(aURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::Open(nsIInputStream **aResult) michael@0: { michael@0: nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, michael@0: mExecutionPolicy, michael@0: mOriginalInnerWindow); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return mStreamChannel->Open(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) michael@0: { michael@0: NS_ENSURE_ARG(aListener); michael@0: michael@0: // First make sure that we have a usable inner window; we'll want to make michael@0: // sure that we execute against that inner and no other. michael@0: nsIScriptGlobalObject* global = GetGlobalObject(this); michael@0: if (!global) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsCOMPtr win(do_QueryInterface(global)); michael@0: NS_ASSERTION(win, "Our global is not a window??"); michael@0: michael@0: // Make sure we create a new inner window if one doesn't already exist (see michael@0: // bug 306630). michael@0: mOriginalInnerWindow = win->EnsureInnerWindow(); michael@0: if (!mOriginalInnerWindow) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: mListener = aListener; michael@0: mContext = aContext; michael@0: michael@0: mIsActive = true; michael@0: michael@0: // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer michael@0: // notifications (and hence nsIWebProgressListener notifications) from michael@0: // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI, michael@0: // which means that the DocLoader would not generate document start and michael@0: // stop notifications (see bug 257875). michael@0: mActualLoadFlags = mLoadFlags; michael@0: mLoadFlags |= LOAD_BACKGROUND; michael@0: michael@0: // Add the javascript channel to its loadgroup so that we know if michael@0: // network loads were canceled or not... michael@0: nsCOMPtr loadGroup; michael@0: mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); michael@0: if (loadGroup) { michael@0: nsresult rv = loadGroup->AddRequest(this, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: mIsActive = false; michael@0: CleanupStrongRefs(); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc(); michael@0: if (mDocumentOnloadBlockedOn) { michael@0: // If we're a document channel, we need to actually block onload on our michael@0: // _parent_ document. This is because we don't actually set our michael@0: // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the michael@0: // document channel will claim to not be busy, and our parent's onload michael@0: // could fire too early. michael@0: nsLoadFlags loadFlags; michael@0: mStreamChannel->GetLoadFlags(&loadFlags); michael@0: if (loadFlags & LOAD_DOCUMENT_URI) { michael@0: mDocumentOnloadBlockedOn = michael@0: mDocumentOnloadBlockedOn->GetParentDocument(); michael@0: } michael@0: } michael@0: if (mDocumentOnloadBlockedOn) { michael@0: mDocumentOnloadBlockedOn->BlockOnload(); michael@0: } michael@0: michael@0: michael@0: mPopupState = win->GetPopupControlState(); michael@0: michael@0: void (nsJSChannel::*method)(); michael@0: if (mIsAsync) { michael@0: // post an event to do the rest michael@0: method = &nsJSChannel::EvaluateScript; michael@0: } else { michael@0: EvaluateScript(); michael@0: if (mOpenedStreamChannel) { michael@0: // That will handle notifying things michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_"); michael@0: michael@0: // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't michael@0: // have any content resulting from the execution and NS_BINDING_ABORTED michael@0: // if something we did causes our own load to be stopped. Return michael@0: // success in those cases, and error out in all others. michael@0: if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED && michael@0: mStatus != NS_BINDING_ABORTED) { michael@0: // Note that calling EvaluateScript() handled removing us from the michael@0: // loadgroup and marking us as not active anymore. michael@0: CleanupStrongRefs(); michael@0: return mStatus; michael@0: } michael@0: michael@0: // We're returning success from asyncOpen(), but we didn't open a michael@0: // stream channel. We'll have to notify ourselves, but make sure to do michael@0: // it asynchronously. michael@0: method = &nsJSChannel::NotifyListener; michael@0: } michael@0: michael@0: nsCOMPtr ev = NS_NewRunnableMethod(this, method); michael@0: nsresult rv = NS_DispatchToCurrentThread(ev); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: loadGroup->RemoveRequest(this, nullptr, rv); michael@0: mIsActive = false; michael@0: CleanupStrongRefs(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsJSChannel::EvaluateScript() michael@0: { michael@0: // Synchronously execute the script... michael@0: // mIsActive is used to indicate the the request is 'busy' during the michael@0: // the script evaluation phase. This means that IsPending() will michael@0: // indicate the the request is busy while the script is executing... michael@0: michael@0: // Note that we want to be in the loadgroup and pending while we evaluate michael@0: // the script, so that we find out if the loadgroup gets canceled by the michael@0: // script execution (in which case we shouldn't pump out data even if the michael@0: // script returns it). michael@0: michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, michael@0: mExecutionPolicy, michael@0: mOriginalInnerWindow); michael@0: michael@0: // Note that evaluation may have canceled us, so recheck mStatus again michael@0: if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) { michael@0: mStatus = rv; michael@0: } michael@0: } michael@0: michael@0: // Remove the javascript channel from its loadgroup... michael@0: nsCOMPtr loadGroup; michael@0: mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); michael@0: if (loadGroup) { michael@0: loadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: } michael@0: michael@0: // Reset load flags to their original value... michael@0: mLoadFlags = mActualLoadFlags; michael@0: michael@0: // We're no longer active, it's now up to the stream channel to do michael@0: // the loading, if needed. michael@0: mIsActive = false; michael@0: michael@0: if (NS_FAILED(mStatus)) { michael@0: if (mIsAsync) { michael@0: NotifyListener(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // EvaluateScript() succeeded, and we were not canceled, that michael@0: // means there's data to parse as a result of evaluating the michael@0: // script. michael@0: michael@0: // Get the stream channels load flags (!= mLoadFlags). michael@0: nsLoadFlags loadFlags; michael@0: mStreamChannel->GetLoadFlags(&loadFlags); michael@0: michael@0: uint32_t disposition; michael@0: if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition))) michael@0: disposition = nsIChannel::DISPOSITION_INLINE; michael@0: if (loadFlags & LOAD_DOCUMENT_URI && disposition != nsIChannel::DISPOSITION_ATTACHMENT) { michael@0: // We're loaded as the document channel and not expecting to download michael@0: // the result. If we go on, we'll blow away the current document. Make michael@0: // sure that's ok. If so, stop all pending network loads. michael@0: michael@0: nsCOMPtr docShell; michael@0: NS_QueryNotificationCallbacks(mStreamChannel, docShell); michael@0: if (docShell) { michael@0: nsCOMPtr cv; michael@0: docShell->GetContentViewer(getter_AddRefs(cv)); michael@0: michael@0: if (cv) { michael@0: bool okToUnload; michael@0: michael@0: if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && michael@0: !okToUnload) { michael@0: // The user didn't want to unload the current michael@0: // page, translate this into an undefined michael@0: // return from the javascript: URL... michael@0: mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: mStatus = StopAll(); michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(mStatus)) { michael@0: if (mIsAsync) { michael@0: NotifyListener(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: mStatus = mStreamChannel->AsyncOpen(this, mContext); michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: // mStreamChannel will call OnStartRequest and OnStopRequest on michael@0: // us, so we'll be sure to call them on our listener. michael@0: mOpenedStreamChannel = true; michael@0: michael@0: // Now readd ourselves to the loadgroup so we can receive michael@0: // cancellation notifications. michael@0: mIsActive = true; michael@0: if (loadGroup) { michael@0: mStatus = loadGroup->AddRequest(this, nullptr); michael@0: michael@0: // If AddRequest failed, that's OK. The key is to make sure we get michael@0: // cancelled if needed, and that call just canceled us if it michael@0: // failed. We'll still get notified by the stream channel when it michael@0: // finishes. michael@0: } michael@0: michael@0: } else if (mIsAsync) { michael@0: NotifyListener(); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsJSChannel::NotifyListener() michael@0: { michael@0: mListener->OnStartRequest(this, mContext); michael@0: mListener->OnStopRequest(this, mContext, mStatus); michael@0: michael@0: CleanupStrongRefs(); michael@0: } michael@0: michael@0: void michael@0: nsJSChannel::CleanupStrongRefs() michael@0: { michael@0: mListener = nullptr; michael@0: mContext = nullptr; michael@0: mOriginalInnerWindow = nullptr; michael@0: if (mDocumentOnloadBlockedOn) { michael@0: mDocumentOnloadBlockedOn->UnblockOnload(false); michael@0: mDocumentOnloadBlockedOn = nullptr; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) michael@0: { michael@0: *aLoadFlags = mLoadFlags; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags) michael@0: { michael@0: // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is michael@0: // actually right. michael@0: bool bogusLoadBackground = false; michael@0: if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) && michael@0: (aLoadFlags & LOAD_BACKGROUND)) { michael@0: // We're getting a LOAD_BACKGROUND, but it's probably just our own fake michael@0: // flag being mirrored to us. The one exception is if our loadgroup is michael@0: // LOAD_BACKGROUND. michael@0: bool loadGroupIsBackground = false; michael@0: nsCOMPtr loadGroup; michael@0: mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); michael@0: if (loadGroup) { michael@0: nsLoadFlags loadGroupFlags; michael@0: loadGroup->GetLoadFlags(&loadGroupFlags); michael@0: loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0); michael@0: } michael@0: bogusLoadBackground = !loadGroupIsBackground; michael@0: } michael@0: michael@0: // Classifying a javascript: URI doesn't help us, and requires michael@0: // NSS to boot, which we don't have in content processes. See michael@0: // https://bugzilla.mozilla.org/show_bug.cgi?id=617838. michael@0: aLoadFlags &= ~LOAD_CLASSIFY_URI; michael@0: michael@0: // Since the javascript channel is never the actual channel that michael@0: // any data is loaded through, don't ever set the michael@0: // LOAD_DOCUMENT_URI flag on it, since that could lead to two michael@0: // 'document channels' in the loadgroup if a javascript: URL is michael@0: // loaded while a document is being loaded in the same window. michael@0: michael@0: // XXXbz this, and a whole lot of other hackery, could go away if we'd just michael@0: // cancel the current document load on javascript: load start like IE does. michael@0: michael@0: mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI; michael@0: michael@0: if (bogusLoadBackground) { michael@0: aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND; michael@0: } michael@0: michael@0: mActualLoadFlags = aLoadFlags; michael@0: michael@0: // ... but the underlying stream channel should get this bit, if michael@0: // set, since that'll be the real document channel if the michael@0: // javascript: URL generated data. michael@0: michael@0: return mStreamChannel->SetLoadFlags(aLoadFlags); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) michael@0: { michael@0: return mStreamChannel->GetLoadGroup(aLoadGroup); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) michael@0: { michael@0: if (aLoadGroup) { michael@0: bool streamPending; michael@0: nsresult rv = mStreamChannel->IsPending(&streamPending); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (streamPending) { michael@0: nsCOMPtr curLoadGroup; michael@0: mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup)); michael@0: michael@0: if (aLoadGroup != curLoadGroup) { michael@0: // Move the stream channel to our new loadgroup. Make sure to michael@0: // add it before removing it, so that we don't trigger onload michael@0: // by accident. michael@0: aLoadGroup->AddRequest(mStreamChannel, nullptr); michael@0: if (curLoadGroup) { michael@0: curLoadGroup->RemoveRequest(mStreamChannel, nullptr, michael@0: NS_BINDING_RETARGETED); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return mStreamChannel->SetLoadGroup(aLoadGroup); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetOwner(nsISupports* *aOwner) michael@0: { michael@0: return mStreamChannel->GetOwner(aOwner); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetOwner(nsISupports* aOwner) michael@0: { michael@0: return mStreamChannel->SetOwner(aOwner); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) michael@0: { michael@0: return mStreamChannel->GetNotificationCallbacks(aCallbacks); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) michael@0: { michael@0: return mStreamChannel->SetNotificationCallbacks(aCallbacks); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) michael@0: { michael@0: return mStreamChannel->GetSecurityInfo(aSecurityInfo); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentType(nsACString &aContentType) michael@0: { michael@0: return mStreamChannel->GetContentType(aContentType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetContentType(const nsACString &aContentType) michael@0: { michael@0: return mStreamChannel->SetContentType(aContentType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentCharset(nsACString &aContentCharset) michael@0: { michael@0: return mStreamChannel->GetContentCharset(aContentCharset); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetContentCharset(const nsACString &aContentCharset) michael@0: { michael@0: return mStreamChannel->SetContentCharset(aContentCharset); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentDisposition(uint32_t *aContentDisposition) michael@0: { michael@0: return mStreamChannel->GetContentDisposition(aContentDisposition); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetContentDisposition(uint32_t aContentDisposition) michael@0: { michael@0: return mStreamChannel->SetContentDisposition(aContentDisposition); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) michael@0: { michael@0: return mStreamChannel->GetContentDispositionFilename(aContentDispositionFilename); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) michael@0: { michael@0: return mStreamChannel->SetContentDispositionFilename(aContentDispositionFilename); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) michael@0: { michael@0: return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetContentLength(int64_t *aContentLength) michael@0: { michael@0: return mStreamChannel->GetContentLength(aContentLength); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetContentLength(int64_t aContentLength) michael@0: { michael@0: return mStreamChannel->SetContentLength(aContentLength); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::OnStartRequest(nsIRequest* aRequest, michael@0: nsISupports* aContext) michael@0: { michael@0: NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); michael@0: michael@0: return mListener->OnStartRequest(this, aContext); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::OnDataAvailable(nsIRequest* aRequest, michael@0: nsISupports* aContext, michael@0: nsIInputStream* aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); michael@0: michael@0: return mListener->OnDataAvailable(this, aContext, aInputStream, aOffset, michael@0: aCount); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::OnStopRequest(nsIRequest* aRequest, michael@0: nsISupports* aContext, michael@0: nsresult aStatus) michael@0: { michael@0: NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr listener = mListener; michael@0: michael@0: CleanupStrongRefs(); michael@0: michael@0: // Make sure aStatus matches what GetStatus() returns michael@0: if (NS_FAILED(mStatus)) { michael@0: aStatus = mStatus; michael@0: } michael@0: michael@0: nsresult rv = listener->OnStopRequest(this, aContext, aStatus); michael@0: michael@0: nsCOMPtr loadGroup; michael@0: mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); michael@0: if (loadGroup) { michael@0: loadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: } michael@0: michael@0: mIsActive = false; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetExecutionPolicy(uint32_t aPolicy) michael@0: { michael@0: NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL); michael@0: michael@0: mExecutionPolicy = aPolicy; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy) michael@0: { michael@0: *aPolicy = mExecutionPolicy; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::SetExecuteAsync(bool aIsAsync) michael@0: { michael@0: if (!mIsActive) { michael@0: mIsAsync = aIsAsync; michael@0: } michael@0: // else ignore this call michael@0: NS_WARN_IF_FALSE(!mIsActive, "Calling SetExecuteAsync on active channel?"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSChannel::GetExecuteAsync(bool* aIsAsync) michael@0: { michael@0: *aIsAsync = mIsAsync; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsJSProtocolHandler::nsJSProtocolHandler() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsJSProtocolHandler::Init() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsJSProtocolHandler::~nsJSProtocolHandler() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler) michael@0: michael@0: nsresult michael@0: nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsJSProtocolHandler* ph = new nsJSProtocolHandler(); michael@0: if (!ph) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(ph); michael@0: nsresult rv = ph->Init(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = ph->QueryInterface(aIID, aResult); michael@0: } michael@0: NS_RELEASE(ph); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString &aSpec, const char *aCharset, michael@0: nsACString &aUTF8Spec) michael@0: { michael@0: aUTF8Spec.Truncate(); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (!mTextToSubURI) { michael@0: mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: nsAutoString uStr; michael@0: rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, uStr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!IsASCII(uStr)) michael@0: NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr), esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIProtocolHandler methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::GetScheme(nsACString &result) michael@0: { michael@0: result = "javascript"; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::GetDefaultPort(int32_t *result) michael@0: { michael@0: *result = -1; // no port for javascript: URLs michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::GetProtocolFlags(uint32_t *result) michael@0: { michael@0: *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT | michael@0: URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_OPENING_EXECUTES_SCRIPT; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::NewURI(const nsACString &aSpec, michael@0: const char *aCharset, michael@0: nsIURI *aBaseURI, michael@0: nsIURI **result) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // javascript: URLs (currently) have no additional structure beyond that michael@0: // provided by standard URLs, so there is no "outer" object given to michael@0: // CreateInstance. michael@0: michael@0: nsCOMPtr url = new nsJSURI(aBaseURI); michael@0: michael@0: if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) michael@0: rv = url->SetSpec(aSpec); michael@0: else { michael@0: nsAutoCString utf8Spec; michael@0: rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (utf8Spec.IsEmpty()) michael@0: rv = url->SetSpec(aSpec); michael@0: else michael@0: rv = url->SetSpec(utf8Spec); michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: url.forget(result); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) michael@0: { michael@0: nsresult rv; michael@0: nsJSChannel * channel; michael@0: michael@0: NS_ENSURE_ARG_POINTER(uri); michael@0: michael@0: channel = new nsJSChannel(); michael@0: if (!channel) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: NS_ADDREF(channel); michael@0: michael@0: rv = channel->Init(uri); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *result = channel; michael@0: NS_ADDREF(*result); michael@0: } michael@0: NS_RELEASE(channel); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) michael@0: { michael@0: // don't override anything. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////// michael@0: // nsJSURI implementation michael@0: static NS_DEFINE_CID(kThisSimpleURIImplementationCID, michael@0: NS_THIS_SIMPLEURI_IMPLEMENTATION_CID); michael@0: michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsJSURI, nsSimpleURI) michael@0: NS_IMPL_RELEASE_INHERITED(nsJSURI, nsSimpleURI) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsJSURI) michael@0: if (aIID.Equals(kJSURICID)) michael@0: foundInterface = static_cast(this); michael@0: else if (aIID.Equals(kThisSimpleURIImplementationCID)) { michael@0: // Need to return explicitly here, because if we just set foundInterface michael@0: // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into michael@0: // nsSimplURI::QueryInterface and finding something for this CID. michael@0: *aInstancePtr = nullptr; michael@0: return NS_NOINTERFACE; michael@0: } michael@0: else michael@0: NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI) michael@0: michael@0: // nsISerializable methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSURI::Read(nsIObjectInputStream* aStream) michael@0: { michael@0: nsresult rv = nsSimpleURI::Read(aStream); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool haveBase; michael@0: rv = aStream->ReadBoolean(&haveBase); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (haveBase) { michael@0: nsCOMPtr supports; michael@0: rv = aStream->ReadObject(true, getter_AddRefs(supports)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mBaseURI = do_QueryInterface(supports); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSURI::Write(nsIObjectOutputStream* aStream) michael@0: { michael@0: nsresult rv = nsSimpleURI::Write(aStream); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->WriteBoolean(mBaseURI != nullptr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mBaseURI) { michael@0: rv = aStream->WriteObject(mBaseURI, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsSimpleURI methods: michael@0: /* virtual */ nsSimpleURI* michael@0: nsJSURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */) michael@0: { michael@0: nsCOMPtr baseClone; michael@0: if (mBaseURI) { michael@0: // Note: We preserve ref on *base* URI, regardless of ref handling mode. michael@0: nsresult rv = mBaseURI->Clone(getter_AddRefs(baseClone)); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return new nsJSURI(baseClone); michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsJSURI::EqualsInternal(nsIURI* aOther, michael@0: nsSimpleURI::RefHandlingEnum aRefHandlingMode, michael@0: bool* aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOther); michael@0: NS_PRECONDITION(aResult, "null pointer for outparam"); michael@0: michael@0: nsRefPtr otherJSURI; michael@0: nsresult rv = aOther->QueryInterface(kJSURICID, michael@0: getter_AddRefs(otherJSURI)); michael@0: if (NS_FAILED(rv)) { michael@0: *aResult = false; // aOther is not a nsJSURI --> not equal. michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Compare the member data that our base class knows about. michael@0: if (!nsSimpleURI::EqualsInternal(otherJSURI, aRefHandlingMode)) { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Compare the piece of additional member data that we add to base class. michael@0: nsIURI* otherBaseURI = otherJSURI->GetBaseURI(); michael@0: michael@0: if (mBaseURI) { michael@0: // (As noted in StartClone, we always honor refs on mBaseURI) michael@0: return mBaseURI->Equals(otherBaseURI, aResult); michael@0: } michael@0: michael@0: *aResult = !otherBaseURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsJSURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) michael@0: { michael@0: *aClassIDNoAlloc = kJSURICID; michael@0: return NS_OK; michael@0: } michael@0: