Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim: set ts=4 sw=4 et tw=78: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "nsCOMPtr.h" |
michael@0 | 8 | #include "nsAutoPtr.h" |
michael@0 | 9 | #include "jsapi.h" |
michael@0 | 10 | #include "jswrapper.h" |
michael@0 | 11 | #include "nsCRT.h" |
michael@0 | 12 | #include "nsError.h" |
michael@0 | 13 | #include "nsXPIDLString.h" |
michael@0 | 14 | #include "nsReadableUtils.h" |
michael@0 | 15 | #include "nsJSProtocolHandler.h" |
michael@0 | 16 | #include "nsStringStream.h" |
michael@0 | 17 | #include "nsNetUtil.h" |
michael@0 | 18 | |
michael@0 | 19 | #include "nsIComponentManager.h" |
michael@0 | 20 | #include "nsIServiceManager.h" |
michael@0 | 21 | #include "nsIURI.h" |
michael@0 | 22 | #include "nsIScriptContext.h" |
michael@0 | 23 | #include "nsIScriptGlobalObject.h" |
michael@0 | 24 | #include "nsIPrincipal.h" |
michael@0 | 25 | #include "nsIScriptSecurityManager.h" |
michael@0 | 26 | #include "nsIInterfaceRequestor.h" |
michael@0 | 27 | #include "nsIInterfaceRequestorUtils.h" |
michael@0 | 28 | #include "nsIWindowMediator.h" |
michael@0 | 29 | #include "nsPIDOMWindow.h" |
michael@0 | 30 | #include "nsIConsoleService.h" |
michael@0 | 31 | #include "nsXPIDLString.h" |
michael@0 | 32 | #include "prprf.h" |
michael@0 | 33 | #include "nsEscape.h" |
michael@0 | 34 | #include "nsIWebNavigation.h" |
michael@0 | 35 | #include "nsIDocShell.h" |
michael@0 | 36 | #include "nsIContentViewer.h" |
michael@0 | 37 | #include "nsIXPConnect.h" |
michael@0 | 38 | #include "nsContentUtils.h" |
michael@0 | 39 | #include "nsCxPusher.h" |
michael@0 | 40 | #include "nsJSUtils.h" |
michael@0 | 41 | #include "nsThreadUtils.h" |
michael@0 | 42 | #include "nsIScriptChannel.h" |
michael@0 | 43 | #include "nsIDocument.h" |
michael@0 | 44 | #include "nsIObjectInputStream.h" |
michael@0 | 45 | #include "nsIObjectOutputStream.h" |
michael@0 | 46 | #include "nsIWritablePropertyBag2.h" |
michael@0 | 47 | #include "nsIContentSecurityPolicy.h" |
michael@0 | 48 | #include "nsSandboxFlags.h" |
michael@0 | 49 | #include "mozilla/dom/ScriptSettings.h" |
michael@0 | 50 | |
michael@0 | 51 | using mozilla::dom::AutoEntryScript; |
michael@0 | 52 | |
michael@0 | 53 | static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID); |
michael@0 | 54 | |
michael@0 | 55 | class nsJSThunk : public nsIInputStream |
michael@0 | 56 | { |
michael@0 | 57 | public: |
michael@0 | 58 | nsJSThunk(); |
michael@0 | 59 | |
michael@0 | 60 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 61 | NS_FORWARD_SAFE_NSIINPUTSTREAM(mInnerStream) |
michael@0 | 62 | |
michael@0 | 63 | nsresult Init(nsIURI* uri); |
michael@0 | 64 | nsresult EvaluateScript(nsIChannel *aChannel, |
michael@0 | 65 | PopupControlState aPopupState, |
michael@0 | 66 | uint32_t aExecutionPolicy, |
michael@0 | 67 | nsPIDOMWindow *aOriginalInnerWindow); |
michael@0 | 68 | |
michael@0 | 69 | protected: |
michael@0 | 70 | virtual ~nsJSThunk(); |
michael@0 | 71 | |
michael@0 | 72 | nsCOMPtr<nsIInputStream> mInnerStream; |
michael@0 | 73 | nsCString mScript; |
michael@0 | 74 | nsCString mURL; |
michael@0 | 75 | }; |
michael@0 | 76 | |
michael@0 | 77 | // |
michael@0 | 78 | // nsISupports implementation... |
michael@0 | 79 | // |
michael@0 | 80 | NS_IMPL_ISUPPORTS(nsJSThunk, nsIInputStream) |
michael@0 | 81 | |
michael@0 | 82 | |
michael@0 | 83 | nsJSThunk::nsJSThunk() |
michael@0 | 84 | { |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | nsJSThunk::~nsJSThunk() |
michael@0 | 88 | { |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | nsresult nsJSThunk::Init(nsIURI* uri) |
michael@0 | 92 | { |
michael@0 | 93 | NS_ENSURE_ARG_POINTER(uri); |
michael@0 | 94 | |
michael@0 | 95 | // Get the script string to evaluate... |
michael@0 | 96 | nsresult rv = uri->GetPath(mScript); |
michael@0 | 97 | if (NS_FAILED(rv)) return rv; |
michael@0 | 98 | |
michael@0 | 99 | // Get the url. |
michael@0 | 100 | rv = uri->GetSpec(mURL); |
michael@0 | 101 | if (NS_FAILED(rv)) return rv; |
michael@0 | 102 | |
michael@0 | 103 | return NS_OK; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | static bool |
michael@0 | 107 | IsISO88591(const nsString& aString) |
michael@0 | 108 | { |
michael@0 | 109 | for (nsString::const_char_iterator c = aString.BeginReading(), |
michael@0 | 110 | c_end = aString.EndReading(); |
michael@0 | 111 | c < c_end; ++c) { |
michael@0 | 112 | if (*c > 255) |
michael@0 | 113 | return false; |
michael@0 | 114 | } |
michael@0 | 115 | return true; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | static |
michael@0 | 119 | nsIScriptGlobalObject* GetGlobalObject(nsIChannel* aChannel) |
michael@0 | 120 | { |
michael@0 | 121 | // Get the global object owner from the channel |
michael@0 | 122 | nsCOMPtr<nsIDocShell> docShell; |
michael@0 | 123 | NS_QueryNotificationCallbacks(aChannel, docShell); |
michael@0 | 124 | if (!docShell) { |
michael@0 | 125 | NS_WARNING("Unable to get a docShell from the channel!"); |
michael@0 | 126 | return nullptr; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | // So far so good: get the script global from its docshell |
michael@0 | 130 | nsIScriptGlobalObject* global = docShell->GetScriptGlobalObject(); |
michael@0 | 131 | |
michael@0 | 132 | NS_ASSERTION(global, |
michael@0 | 133 | "Unable to get an nsIScriptGlobalObject from the " |
michael@0 | 134 | "docShell!"); |
michael@0 | 135 | return global; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel, |
michael@0 | 139 | PopupControlState aPopupState, |
michael@0 | 140 | uint32_t aExecutionPolicy, |
michael@0 | 141 | nsPIDOMWindow *aOriginalInnerWindow) |
michael@0 | 142 | { |
michael@0 | 143 | if (aExecutionPolicy == nsIScriptChannel::NO_EXECUTION) { |
michael@0 | 144 | // Nothing to do here. |
michael@0 | 145 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | NS_ENSURE_ARG_POINTER(aChannel); |
michael@0 | 149 | |
michael@0 | 150 | // Get principal of code for execution |
michael@0 | 151 | nsCOMPtr<nsISupports> owner; |
michael@0 | 152 | aChannel->GetOwner(getter_AddRefs(owner)); |
michael@0 | 153 | nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner); |
michael@0 | 154 | if (!principal) { |
michael@0 | 155 | // No execution without a principal! |
michael@0 | 156 | NS_ASSERTION(!owner, "Non-principal owner?"); |
michael@0 | 157 | NS_WARNING("No principal to execute JS with"); |
michael@0 | 158 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | nsresult rv; |
michael@0 | 162 | |
michael@0 | 163 | // CSP check: javascript: URIs disabled unless "inline" scripts are |
michael@0 | 164 | // allowed. |
michael@0 | 165 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
michael@0 | 166 | rv = principal->GetCsp(getter_AddRefs(csp)); |
michael@0 | 167 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 168 | if (csp) { |
michael@0 | 169 | bool allowsInline = true; |
michael@0 | 170 | bool reportViolations = false; |
michael@0 | 171 | rv = csp->GetAllowsInlineScript(&reportViolations, &allowsInline); |
michael@0 | 172 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 173 | |
michael@0 | 174 | if (reportViolations) { |
michael@0 | 175 | // gather information to log with violation report |
michael@0 | 176 | nsCOMPtr<nsIURI> uri; |
michael@0 | 177 | principal->GetURI(getter_AddRefs(uri)); |
michael@0 | 178 | nsAutoCString asciiSpec; |
michael@0 | 179 | uri->GetAsciiSpec(asciiSpec); |
michael@0 | 180 | csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT, |
michael@0 | 181 | NS_ConvertUTF8toUTF16(asciiSpec), |
michael@0 | 182 | NS_ConvertUTF8toUTF16(mURL), |
michael@0 | 183 | 0, |
michael@0 | 184 | EmptyString(), |
michael@0 | 185 | EmptyString()); |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | //return early if inline scripts are not allowed |
michael@0 | 189 | if (!allowsInline) { |
michael@0 | 190 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 191 | } |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | // Get the global object we should be running on. |
michael@0 | 195 | nsIScriptGlobalObject* global = GetGlobalObject(aChannel); |
michael@0 | 196 | if (!global) { |
michael@0 | 197 | return NS_ERROR_FAILURE; |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | // Sandboxed document check: javascript: URI's are disabled |
michael@0 | 201 | // in a sandboxed document unless 'allow-scripts' was specified. |
michael@0 | 202 | nsIDocument* doc = aOriginalInnerWindow->GetExtantDoc(); |
michael@0 | 203 | if (doc && (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS)) { |
michael@0 | 204 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | // Push our popup control state |
michael@0 | 208 | nsAutoPopupStatePusher popupStatePusher(aPopupState); |
michael@0 | 209 | |
michael@0 | 210 | // Make sure we still have the same inner window as we used to. |
michael@0 | 211 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global); |
michael@0 | 212 | nsPIDOMWindow *innerWin = win->GetCurrentInnerWindow(); |
michael@0 | 213 | |
michael@0 | 214 | if (innerWin != aOriginalInnerWindow) { |
michael@0 | 215 | return NS_ERROR_UNEXPECTED; |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | nsCOMPtr<nsIScriptGlobalObject> innerGlobal = do_QueryInterface(innerWin); |
michael@0 | 219 | |
michael@0 | 220 | nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(global, &rv)); |
michael@0 | 221 | if (NS_FAILED(rv)) { |
michael@0 | 222 | return NS_ERROR_FAILURE; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | // So far so good: get the script context from its owner. |
michael@0 | 226 | nsCOMPtr<nsIScriptContext> scriptContext = global->GetContext(); |
michael@0 | 227 | if (!scriptContext) |
michael@0 | 228 | return NS_ERROR_FAILURE; |
michael@0 | 229 | |
michael@0 | 230 | nsAutoCString script(mScript); |
michael@0 | 231 | // Unescape the script |
michael@0 | 232 | NS_UnescapeURL(script); |
michael@0 | 233 | |
michael@0 | 234 | |
michael@0 | 235 | nsCOMPtr<nsIScriptSecurityManager> securityManager; |
michael@0 | 236 | securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); |
michael@0 | 237 | if (NS_FAILED(rv)) |
michael@0 | 238 | return rv; |
michael@0 | 239 | |
michael@0 | 240 | bool useSandbox = |
michael@0 | 241 | (aExecutionPolicy == nsIScriptChannel::EXECUTE_IN_SANDBOX); |
michael@0 | 242 | |
michael@0 | 243 | // New script entry point required, due to the "Create a script" step of |
michael@0 | 244 | // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol |
michael@0 | 245 | AutoEntryScript entryScript(innerGlobal, true, |
michael@0 | 246 | scriptContext->GetNativeContext()); |
michael@0 | 247 | JSContext* cx = entryScript.cx(); |
michael@0 | 248 | JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject()); |
michael@0 | 249 | NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED); |
michael@0 | 250 | |
michael@0 | 251 | if (!useSandbox) { |
michael@0 | 252 | //-- Don't outside a sandbox unless the script principal subsumes the |
michael@0 | 253 | // principal of the context. |
michael@0 | 254 | nsIPrincipal* objectPrincipal = nsContentUtils::GetObjectPrincipal(globalJSObject); |
michael@0 | 255 | |
michael@0 | 256 | bool subsumes; |
michael@0 | 257 | rv = principal->Subsumes(objectPrincipal, &subsumes); |
michael@0 | 258 | if (NS_FAILED(rv)) |
michael@0 | 259 | return rv; |
michael@0 | 260 | |
michael@0 | 261 | useSandbox = !subsumes; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | JS::Rooted<JS::Value> v (cx, JS::UndefinedValue()); |
michael@0 | 265 | // Finally, we have everything needed to evaluate the expression. |
michael@0 | 266 | if (useSandbox) { |
michael@0 | 267 | // We were asked to use a sandbox, or the channel owner isn't allowed |
michael@0 | 268 | // to execute in this context. Evaluate the javascript URL in a |
michael@0 | 269 | // sandbox to prevent it from accessing data it doesn't have |
michael@0 | 270 | // permissions to access. |
michael@0 | 271 | |
michael@0 | 272 | // First check to make sure it's OK to evaluate this script to |
michael@0 | 273 | // start with. For example, script could be disabled. |
michael@0 | 274 | if (!securityManager->ScriptAllowed(globalJSObject)) { |
michael@0 | 275 | // Treat this as returning undefined from the script. That's what |
michael@0 | 276 | // nsJSContext does. |
michael@0 | 277 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | nsIXPConnect *xpc = nsContentUtils::XPConnect(); |
michael@0 | 281 | |
michael@0 | 282 | nsCOMPtr<nsIXPConnectJSObjectHolder> sandbox; |
michael@0 | 283 | // Important: Use a null principal here |
michael@0 | 284 | rv = xpc->CreateSandbox(cx, nullptr, getter_AddRefs(sandbox)); |
michael@0 | 285 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 286 | |
michael@0 | 287 | // The nsXPConnect sandbox API gives us a wrapper to the sandbox for |
michael@0 | 288 | // our current compartment. Because our current context doesn't necessarily |
michael@0 | 289 | // subsume that of the sandbox, we want to unwrap and enter the sandbox's |
michael@0 | 290 | // compartment. It's a shame that the APIs here are so clunkly. :-( |
michael@0 | 291 | JS::Rooted<JSObject*> sandboxObj(cx, sandbox->GetJSObject()); |
michael@0 | 292 | NS_ENSURE_STATE(sandboxObj); |
michael@0 | 293 | sandboxObj = js::UncheckedUnwrap(sandboxObj); |
michael@0 | 294 | JSAutoCompartment ac(cx, sandboxObj); |
michael@0 | 295 | |
michael@0 | 296 | // Push our JSContext on the context stack so the EvalInSandboxObject call (and |
michael@0 | 297 | // JS_ReportPendingException, if relevant) will use the principal of cx. |
michael@0 | 298 | nsCxPusher pusher; |
michael@0 | 299 | pusher.Push(cx); |
michael@0 | 300 | rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), |
michael@0 | 301 | /* filename = */ nullptr, cx, |
michael@0 | 302 | sandboxObj, true, &v); |
michael@0 | 303 | |
michael@0 | 304 | // Propagate and report exceptions that happened in the |
michael@0 | 305 | // sandbox. |
michael@0 | 306 | if (JS_IsExceptionPending(cx)) { |
michael@0 | 307 | JS_ReportPendingException(cx); |
michael@0 | 308 | } |
michael@0 | 309 | } else { |
michael@0 | 310 | // No need to use the sandbox, evaluate the script directly in |
michael@0 | 311 | // the given scope. |
michael@0 | 312 | JS::CompileOptions options(cx); |
michael@0 | 313 | options.setFileAndLine(mURL.get(), 1) |
michael@0 | 314 | .setVersion(JSVERSION_DEFAULT); |
michael@0 | 315 | nsJSUtils::EvaluateOptions evalOptions; |
michael@0 | 316 | evalOptions.setCoerceToString(true); |
michael@0 | 317 | rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script), |
michael@0 | 318 | globalJSObject, options, evalOptions, &v); |
michael@0 | 319 | |
michael@0 | 320 | // If there's an error on cx as a result of that call, report |
michael@0 | 321 | // it now -- either we're just running under the event loop, |
michael@0 | 322 | // so we shouldn't propagate JS exceptions out of here, or we |
michael@0 | 323 | // can't be sure that our caller is JS (and if it's not we'll |
michael@0 | 324 | // lose the error), or it might be JS that then proceeds to |
michael@0 | 325 | // cause an error of its own (which will also make us lose |
michael@0 | 326 | // this error). |
michael@0 | 327 | ::JS_ReportPendingException(cx); |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | // If we took the sandbox path above, v might be in the sandbox |
michael@0 | 331 | // compartment. |
michael@0 | 332 | if (!JS_WrapValue(cx, &v)) { |
michael@0 | 333 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) { |
michael@0 | 337 | return NS_ERROR_MALFORMED_URI; |
michael@0 | 338 | } else if (v.isUndefined()) { |
michael@0 | 339 | return NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 340 | } else { |
michael@0 | 341 | nsDependentJSString result; |
michael@0 | 342 | if (!result.init(cx, v)) { |
michael@0 | 343 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | char *bytes; |
michael@0 | 347 | uint32_t bytesLen; |
michael@0 | 348 | NS_NAMED_LITERAL_CSTRING(isoCharset, "ISO-8859-1"); |
michael@0 | 349 | NS_NAMED_LITERAL_CSTRING(utf8Charset, "UTF-8"); |
michael@0 | 350 | const nsCString *charset; |
michael@0 | 351 | if (IsISO88591(result)) { |
michael@0 | 352 | // For compatibility, if the result is ISO-8859-1, we use |
michael@0 | 353 | // ISO-8859-1, so that people can compatibly create images |
michael@0 | 354 | // using javascript: URLs. |
michael@0 | 355 | bytes = ToNewCString(result); |
michael@0 | 356 | bytesLen = result.Length(); |
michael@0 | 357 | charset = &isoCharset; |
michael@0 | 358 | } |
michael@0 | 359 | else { |
michael@0 | 360 | bytes = ToNewUTF8String(result, &bytesLen); |
michael@0 | 361 | charset = &utf8Charset; |
michael@0 | 362 | } |
michael@0 | 363 | aChannel->SetContentCharset(*charset); |
michael@0 | 364 | if (bytes) |
michael@0 | 365 | rv = NS_NewByteInputStream(getter_AddRefs(mInnerStream), |
michael@0 | 366 | bytes, bytesLen, |
michael@0 | 367 | NS_ASSIGNMENT_ADOPT); |
michael@0 | 368 | else |
michael@0 | 369 | rv = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | return rv; |
michael@0 | 373 | } |
michael@0 | 374 | |
michael@0 | 375 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 376 | |
michael@0 | 377 | class nsJSChannel : public nsIChannel, |
michael@0 | 378 | public nsIStreamListener, |
michael@0 | 379 | public nsIScriptChannel, |
michael@0 | 380 | public nsIPropertyBag2 |
michael@0 | 381 | { |
michael@0 | 382 | public: |
michael@0 | 383 | nsJSChannel(); |
michael@0 | 384 | |
michael@0 | 385 | NS_DECL_ISUPPORTS |
michael@0 | 386 | NS_DECL_NSIREQUEST |
michael@0 | 387 | NS_DECL_NSICHANNEL |
michael@0 | 388 | NS_DECL_NSIREQUESTOBSERVER |
michael@0 | 389 | NS_DECL_NSISTREAMLISTENER |
michael@0 | 390 | NS_DECL_NSISCRIPTCHANNEL |
michael@0 | 391 | NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag) |
michael@0 | 392 | NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag) |
michael@0 | 393 | |
michael@0 | 394 | nsresult Init(nsIURI *aURI); |
michael@0 | 395 | |
michael@0 | 396 | // Actually evaluate the script. |
michael@0 | 397 | void EvaluateScript(); |
michael@0 | 398 | |
michael@0 | 399 | protected: |
michael@0 | 400 | virtual ~nsJSChannel(); |
michael@0 | 401 | |
michael@0 | 402 | nsresult StopAll(); |
michael@0 | 403 | |
michael@0 | 404 | void NotifyListener(); |
michael@0 | 405 | |
michael@0 | 406 | void CleanupStrongRefs(); |
michael@0 | 407 | |
michael@0 | 408 | protected: |
michael@0 | 409 | nsCOMPtr<nsIChannel> mStreamChannel; |
michael@0 | 410 | nsCOMPtr<nsIPropertyBag2> mPropertyBag; |
michael@0 | 411 | nsCOMPtr<nsIStreamListener> mListener; // Our final listener |
michael@0 | 412 | nsCOMPtr<nsISupports> mContext; // The context passed to AsyncOpen |
michael@0 | 413 | nsCOMPtr<nsPIDOMWindow> mOriginalInnerWindow; // The inner window our load |
michael@0 | 414 | // started against. |
michael@0 | 415 | // If we blocked onload on a document in AsyncOpen, this is the document we |
michael@0 | 416 | // did it on. |
michael@0 | 417 | nsCOMPtr<nsIDocument> mDocumentOnloadBlockedOn; |
michael@0 | 418 | |
michael@0 | 419 | nsresult mStatus; // Our status |
michael@0 | 420 | |
michael@0 | 421 | nsLoadFlags mLoadFlags; |
michael@0 | 422 | nsLoadFlags mActualLoadFlags; // See AsyncOpen |
michael@0 | 423 | |
michael@0 | 424 | nsRefPtr<nsJSThunk> mIOThunk; |
michael@0 | 425 | PopupControlState mPopupState; |
michael@0 | 426 | uint32_t mExecutionPolicy; |
michael@0 | 427 | bool mIsAsync; |
michael@0 | 428 | bool mIsActive; |
michael@0 | 429 | bool mOpenedStreamChannel; |
michael@0 | 430 | }; |
michael@0 | 431 | |
michael@0 | 432 | nsJSChannel::nsJSChannel() : |
michael@0 | 433 | mStatus(NS_OK), |
michael@0 | 434 | mLoadFlags(LOAD_NORMAL), |
michael@0 | 435 | mActualLoadFlags(LOAD_NORMAL), |
michael@0 | 436 | mPopupState(openOverridden), |
michael@0 | 437 | mExecutionPolicy(EXECUTE_IN_SANDBOX), |
michael@0 | 438 | mIsAsync(true), |
michael@0 | 439 | mIsActive(false), |
michael@0 | 440 | mOpenedStreamChannel(false) |
michael@0 | 441 | { |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | nsJSChannel::~nsJSChannel() |
michael@0 | 445 | { |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | nsresult nsJSChannel::StopAll() |
michael@0 | 449 | { |
michael@0 | 450 | nsresult rv = NS_ERROR_UNEXPECTED; |
michael@0 | 451 | nsCOMPtr<nsIWebNavigation> webNav; |
michael@0 | 452 | NS_QueryNotificationCallbacks(mStreamChannel, webNav); |
michael@0 | 453 | |
michael@0 | 454 | NS_ASSERTION(webNav, "Can't get nsIWebNavigation from channel!"); |
michael@0 | 455 | if (webNav) { |
michael@0 | 456 | rv = webNav->Stop(nsIWebNavigation::STOP_ALL); |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | return rv; |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | nsresult nsJSChannel::Init(nsIURI *aURI) |
michael@0 | 463 | { |
michael@0 | 464 | nsRefPtr<nsJSURI> jsURI; |
michael@0 | 465 | nsresult rv = aURI->QueryInterface(kJSURICID, |
michael@0 | 466 | getter_AddRefs(jsURI)); |
michael@0 | 467 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 468 | |
michael@0 | 469 | // Create the nsIStreamIO layer used by the nsIStreamIOChannel. |
michael@0 | 470 | mIOThunk = new nsJSThunk(); |
michael@0 | 471 | if (!mIOThunk) |
michael@0 | 472 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 473 | |
michael@0 | 474 | // Create a stock input stream channel... |
michael@0 | 475 | // Remember, until AsyncOpen is called, the script will not be evaluated |
michael@0 | 476 | // and the underlying Input Stream will not be created... |
michael@0 | 477 | nsCOMPtr<nsIChannel> channel; |
michael@0 | 478 | |
michael@0 | 479 | // If the resultant script evaluation actually does return a value, we |
michael@0 | 480 | // treat it as html. |
michael@0 | 481 | rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk, |
michael@0 | 482 | NS_LITERAL_CSTRING("text/html")); |
michael@0 | 483 | if (NS_FAILED(rv)) return rv; |
michael@0 | 484 | |
michael@0 | 485 | rv = mIOThunk->Init(aURI); |
michael@0 | 486 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 487 | mStreamChannel = channel; |
michael@0 | 488 | mPropertyBag = do_QueryInterface(channel); |
michael@0 | 489 | nsCOMPtr<nsIWritablePropertyBag2> writableBag = |
michael@0 | 490 | do_QueryInterface(channel); |
michael@0 | 491 | if (writableBag && jsURI->GetBaseURI()) { |
michael@0 | 492 | writableBag->SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), |
michael@0 | 493 | jsURI->GetBaseURI()); |
michael@0 | 494 | } |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | return rv; |
michael@0 | 498 | } |
michael@0 | 499 | |
michael@0 | 500 | // |
michael@0 | 501 | // nsISupports implementation... |
michael@0 | 502 | // |
michael@0 | 503 | |
michael@0 | 504 | NS_IMPL_ISUPPORTS(nsJSChannel, nsIChannel, nsIRequest, nsIRequestObserver, |
michael@0 | 505 | nsIStreamListener, nsIScriptChannel, nsIPropertyBag, |
michael@0 | 506 | nsIPropertyBag2) |
michael@0 | 507 | |
michael@0 | 508 | // |
michael@0 | 509 | // nsIRequest implementation... |
michael@0 | 510 | // |
michael@0 | 511 | |
michael@0 | 512 | NS_IMETHODIMP |
michael@0 | 513 | nsJSChannel::GetName(nsACString &aResult) |
michael@0 | 514 | { |
michael@0 | 515 | return mStreamChannel->GetName(aResult); |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | NS_IMETHODIMP |
michael@0 | 519 | nsJSChannel::IsPending(bool *aResult) |
michael@0 | 520 | { |
michael@0 | 521 | *aResult = mIsActive; |
michael@0 | 522 | return NS_OK; |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | NS_IMETHODIMP |
michael@0 | 526 | nsJSChannel::GetStatus(nsresult *aResult) |
michael@0 | 527 | { |
michael@0 | 528 | if (NS_SUCCEEDED(mStatus) && mOpenedStreamChannel) { |
michael@0 | 529 | return mStreamChannel->GetStatus(aResult); |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | *aResult = mStatus; |
michael@0 | 533 | |
michael@0 | 534 | return NS_OK; |
michael@0 | 535 | } |
michael@0 | 536 | |
michael@0 | 537 | NS_IMETHODIMP |
michael@0 | 538 | nsJSChannel::Cancel(nsresult aStatus) |
michael@0 | 539 | { |
michael@0 | 540 | mStatus = aStatus; |
michael@0 | 541 | |
michael@0 | 542 | if (mOpenedStreamChannel) { |
michael@0 | 543 | mStreamChannel->Cancel(aStatus); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | return NS_OK; |
michael@0 | 547 | } |
michael@0 | 548 | |
michael@0 | 549 | NS_IMETHODIMP |
michael@0 | 550 | nsJSChannel::Suspend() |
michael@0 | 551 | { |
michael@0 | 552 | return mStreamChannel->Suspend(); |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | NS_IMETHODIMP |
michael@0 | 556 | nsJSChannel::Resume() |
michael@0 | 557 | { |
michael@0 | 558 | return mStreamChannel->Resume(); |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | // |
michael@0 | 562 | // nsIChannel implementation |
michael@0 | 563 | // |
michael@0 | 564 | |
michael@0 | 565 | NS_IMETHODIMP |
michael@0 | 566 | nsJSChannel::GetOriginalURI(nsIURI * *aURI) |
michael@0 | 567 | { |
michael@0 | 568 | return mStreamChannel->GetOriginalURI(aURI); |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | NS_IMETHODIMP |
michael@0 | 572 | nsJSChannel::SetOriginalURI(nsIURI *aURI) |
michael@0 | 573 | { |
michael@0 | 574 | return mStreamChannel->SetOriginalURI(aURI); |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | NS_IMETHODIMP |
michael@0 | 578 | nsJSChannel::GetURI(nsIURI * *aURI) |
michael@0 | 579 | { |
michael@0 | 580 | return mStreamChannel->GetURI(aURI); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | NS_IMETHODIMP |
michael@0 | 584 | nsJSChannel::Open(nsIInputStream **aResult) |
michael@0 | 585 | { |
michael@0 | 586 | nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, |
michael@0 | 587 | mExecutionPolicy, |
michael@0 | 588 | mOriginalInnerWindow); |
michael@0 | 589 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 590 | |
michael@0 | 591 | return mStreamChannel->Open(aResult); |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | NS_IMETHODIMP |
michael@0 | 595 | nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) |
michael@0 | 596 | { |
michael@0 | 597 | NS_ENSURE_ARG(aListener); |
michael@0 | 598 | |
michael@0 | 599 | // First make sure that we have a usable inner window; we'll want to make |
michael@0 | 600 | // sure that we execute against that inner and no other. |
michael@0 | 601 | nsIScriptGlobalObject* global = GetGlobalObject(this); |
michael@0 | 602 | if (!global) { |
michael@0 | 603 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 604 | } |
michael@0 | 605 | |
michael@0 | 606 | nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global)); |
michael@0 | 607 | NS_ASSERTION(win, "Our global is not a window??"); |
michael@0 | 608 | |
michael@0 | 609 | // Make sure we create a new inner window if one doesn't already exist (see |
michael@0 | 610 | // bug 306630). |
michael@0 | 611 | mOriginalInnerWindow = win->EnsureInnerWindow(); |
michael@0 | 612 | if (!mOriginalInnerWindow) { |
michael@0 | 613 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | mListener = aListener; |
michael@0 | 617 | mContext = aContext; |
michael@0 | 618 | |
michael@0 | 619 | mIsActive = true; |
michael@0 | 620 | |
michael@0 | 621 | // Temporarily set the LOAD_BACKGROUND flag to suppress load group observer |
michael@0 | 622 | // notifications (and hence nsIWebProgressListener notifications) from |
michael@0 | 623 | // being dispatched. This is required since we suppress LOAD_DOCUMENT_URI, |
michael@0 | 624 | // which means that the DocLoader would not generate document start and |
michael@0 | 625 | // stop notifications (see bug 257875). |
michael@0 | 626 | mActualLoadFlags = mLoadFlags; |
michael@0 | 627 | mLoadFlags |= LOAD_BACKGROUND; |
michael@0 | 628 | |
michael@0 | 629 | // Add the javascript channel to its loadgroup so that we know if |
michael@0 | 630 | // network loads were canceled or not... |
michael@0 | 631 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 632 | mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 633 | if (loadGroup) { |
michael@0 | 634 | nsresult rv = loadGroup->AddRequest(this, nullptr); |
michael@0 | 635 | if (NS_FAILED(rv)) { |
michael@0 | 636 | mIsActive = false; |
michael@0 | 637 | CleanupStrongRefs(); |
michael@0 | 638 | return rv; |
michael@0 | 639 | } |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | mDocumentOnloadBlockedOn = mOriginalInnerWindow->GetExtantDoc(); |
michael@0 | 643 | if (mDocumentOnloadBlockedOn) { |
michael@0 | 644 | // If we're a document channel, we need to actually block onload on our |
michael@0 | 645 | // _parent_ document. This is because we don't actually set our |
michael@0 | 646 | // LOAD_DOCUMENT_URI flag, so a docloader we're loading in as the |
michael@0 | 647 | // document channel will claim to not be busy, and our parent's onload |
michael@0 | 648 | // could fire too early. |
michael@0 | 649 | nsLoadFlags loadFlags; |
michael@0 | 650 | mStreamChannel->GetLoadFlags(&loadFlags); |
michael@0 | 651 | if (loadFlags & LOAD_DOCUMENT_URI) { |
michael@0 | 652 | mDocumentOnloadBlockedOn = |
michael@0 | 653 | mDocumentOnloadBlockedOn->GetParentDocument(); |
michael@0 | 654 | } |
michael@0 | 655 | } |
michael@0 | 656 | if (mDocumentOnloadBlockedOn) { |
michael@0 | 657 | mDocumentOnloadBlockedOn->BlockOnload(); |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | |
michael@0 | 661 | mPopupState = win->GetPopupControlState(); |
michael@0 | 662 | |
michael@0 | 663 | void (nsJSChannel::*method)(); |
michael@0 | 664 | if (mIsAsync) { |
michael@0 | 665 | // post an event to do the rest |
michael@0 | 666 | method = &nsJSChannel::EvaluateScript; |
michael@0 | 667 | } else { |
michael@0 | 668 | EvaluateScript(); |
michael@0 | 669 | if (mOpenedStreamChannel) { |
michael@0 | 670 | // That will handle notifying things |
michael@0 | 671 | return NS_OK; |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_"); |
michael@0 | 675 | |
michael@0 | 676 | // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't |
michael@0 | 677 | // have any content resulting from the execution and NS_BINDING_ABORTED |
michael@0 | 678 | // if something we did causes our own load to be stopped. Return |
michael@0 | 679 | // success in those cases, and error out in all others. |
michael@0 | 680 | if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED && |
michael@0 | 681 | mStatus != NS_BINDING_ABORTED) { |
michael@0 | 682 | // Note that calling EvaluateScript() handled removing us from the |
michael@0 | 683 | // loadgroup and marking us as not active anymore. |
michael@0 | 684 | CleanupStrongRefs(); |
michael@0 | 685 | return mStatus; |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | // We're returning success from asyncOpen(), but we didn't open a |
michael@0 | 689 | // stream channel. We'll have to notify ourselves, but make sure to do |
michael@0 | 690 | // it asynchronously. |
michael@0 | 691 | method = &nsJSChannel::NotifyListener; |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, method); |
michael@0 | 695 | nsresult rv = NS_DispatchToCurrentThread(ev); |
michael@0 | 696 | |
michael@0 | 697 | if (NS_FAILED(rv)) { |
michael@0 | 698 | loadGroup->RemoveRequest(this, nullptr, rv); |
michael@0 | 699 | mIsActive = false; |
michael@0 | 700 | CleanupStrongRefs(); |
michael@0 | 701 | } |
michael@0 | 702 | return rv; |
michael@0 | 703 | } |
michael@0 | 704 | |
michael@0 | 705 | void |
michael@0 | 706 | nsJSChannel::EvaluateScript() |
michael@0 | 707 | { |
michael@0 | 708 | // Synchronously execute the script... |
michael@0 | 709 | // mIsActive is used to indicate the the request is 'busy' during the |
michael@0 | 710 | // the script evaluation phase. This means that IsPending() will |
michael@0 | 711 | // indicate the the request is busy while the script is executing... |
michael@0 | 712 | |
michael@0 | 713 | // Note that we want to be in the loadgroup and pending while we evaluate |
michael@0 | 714 | // the script, so that we find out if the loadgroup gets canceled by the |
michael@0 | 715 | // script execution (in which case we shouldn't pump out data even if the |
michael@0 | 716 | // script returns it). |
michael@0 | 717 | |
michael@0 | 718 | if (NS_SUCCEEDED(mStatus)) { |
michael@0 | 719 | nsresult rv = mIOThunk->EvaluateScript(mStreamChannel, mPopupState, |
michael@0 | 720 | mExecutionPolicy, |
michael@0 | 721 | mOriginalInnerWindow); |
michael@0 | 722 | |
michael@0 | 723 | // Note that evaluation may have canceled us, so recheck mStatus again |
michael@0 | 724 | if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) { |
michael@0 | 725 | mStatus = rv; |
michael@0 | 726 | } |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | // Remove the javascript channel from its loadgroup... |
michael@0 | 730 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 731 | mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 732 | if (loadGroup) { |
michael@0 | 733 | loadGroup->RemoveRequest(this, nullptr, mStatus); |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | // Reset load flags to their original value... |
michael@0 | 737 | mLoadFlags = mActualLoadFlags; |
michael@0 | 738 | |
michael@0 | 739 | // We're no longer active, it's now up to the stream channel to do |
michael@0 | 740 | // the loading, if needed. |
michael@0 | 741 | mIsActive = false; |
michael@0 | 742 | |
michael@0 | 743 | if (NS_FAILED(mStatus)) { |
michael@0 | 744 | if (mIsAsync) { |
michael@0 | 745 | NotifyListener(); |
michael@0 | 746 | } |
michael@0 | 747 | return; |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | // EvaluateScript() succeeded, and we were not canceled, that |
michael@0 | 751 | // means there's data to parse as a result of evaluating the |
michael@0 | 752 | // script. |
michael@0 | 753 | |
michael@0 | 754 | // Get the stream channels load flags (!= mLoadFlags). |
michael@0 | 755 | nsLoadFlags loadFlags; |
michael@0 | 756 | mStreamChannel->GetLoadFlags(&loadFlags); |
michael@0 | 757 | |
michael@0 | 758 | uint32_t disposition; |
michael@0 | 759 | if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition))) |
michael@0 | 760 | disposition = nsIChannel::DISPOSITION_INLINE; |
michael@0 | 761 | if (loadFlags & LOAD_DOCUMENT_URI && disposition != nsIChannel::DISPOSITION_ATTACHMENT) { |
michael@0 | 762 | // We're loaded as the document channel and not expecting to download |
michael@0 | 763 | // the result. If we go on, we'll blow away the current document. Make |
michael@0 | 764 | // sure that's ok. If so, stop all pending network loads. |
michael@0 | 765 | |
michael@0 | 766 | nsCOMPtr<nsIDocShell> docShell; |
michael@0 | 767 | NS_QueryNotificationCallbacks(mStreamChannel, docShell); |
michael@0 | 768 | if (docShell) { |
michael@0 | 769 | nsCOMPtr<nsIContentViewer> cv; |
michael@0 | 770 | docShell->GetContentViewer(getter_AddRefs(cv)); |
michael@0 | 771 | |
michael@0 | 772 | if (cv) { |
michael@0 | 773 | bool okToUnload; |
michael@0 | 774 | |
michael@0 | 775 | if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && |
michael@0 | 776 | !okToUnload) { |
michael@0 | 777 | // The user didn't want to unload the current |
michael@0 | 778 | // page, translate this into an undefined |
michael@0 | 779 | // return from the javascript: URL... |
michael@0 | 780 | mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED; |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | if (NS_SUCCEEDED(mStatus)) { |
michael@0 | 786 | mStatus = StopAll(); |
michael@0 | 787 | } |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | if (NS_FAILED(mStatus)) { |
michael@0 | 791 | if (mIsAsync) { |
michael@0 | 792 | NotifyListener(); |
michael@0 | 793 | } |
michael@0 | 794 | return; |
michael@0 | 795 | } |
michael@0 | 796 | |
michael@0 | 797 | mStatus = mStreamChannel->AsyncOpen(this, mContext); |
michael@0 | 798 | if (NS_SUCCEEDED(mStatus)) { |
michael@0 | 799 | // mStreamChannel will call OnStartRequest and OnStopRequest on |
michael@0 | 800 | // us, so we'll be sure to call them on our listener. |
michael@0 | 801 | mOpenedStreamChannel = true; |
michael@0 | 802 | |
michael@0 | 803 | // Now readd ourselves to the loadgroup so we can receive |
michael@0 | 804 | // cancellation notifications. |
michael@0 | 805 | mIsActive = true; |
michael@0 | 806 | if (loadGroup) { |
michael@0 | 807 | mStatus = loadGroup->AddRequest(this, nullptr); |
michael@0 | 808 | |
michael@0 | 809 | // If AddRequest failed, that's OK. The key is to make sure we get |
michael@0 | 810 | // cancelled if needed, and that call just canceled us if it |
michael@0 | 811 | // failed. We'll still get notified by the stream channel when it |
michael@0 | 812 | // finishes. |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | } else if (mIsAsync) { |
michael@0 | 816 | NotifyListener(); |
michael@0 | 817 | } |
michael@0 | 818 | |
michael@0 | 819 | return; |
michael@0 | 820 | } |
michael@0 | 821 | |
michael@0 | 822 | void |
michael@0 | 823 | nsJSChannel::NotifyListener() |
michael@0 | 824 | { |
michael@0 | 825 | mListener->OnStartRequest(this, mContext); |
michael@0 | 826 | mListener->OnStopRequest(this, mContext, mStatus); |
michael@0 | 827 | |
michael@0 | 828 | CleanupStrongRefs(); |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | void |
michael@0 | 832 | nsJSChannel::CleanupStrongRefs() |
michael@0 | 833 | { |
michael@0 | 834 | mListener = nullptr; |
michael@0 | 835 | mContext = nullptr; |
michael@0 | 836 | mOriginalInnerWindow = nullptr; |
michael@0 | 837 | if (mDocumentOnloadBlockedOn) { |
michael@0 | 838 | mDocumentOnloadBlockedOn->UnblockOnload(false); |
michael@0 | 839 | mDocumentOnloadBlockedOn = nullptr; |
michael@0 | 840 | } |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | NS_IMETHODIMP |
michael@0 | 844 | nsJSChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) |
michael@0 | 845 | { |
michael@0 | 846 | *aLoadFlags = mLoadFlags; |
michael@0 | 847 | |
michael@0 | 848 | return NS_OK; |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | NS_IMETHODIMP |
michael@0 | 852 | nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags) |
michael@0 | 853 | { |
michael@0 | 854 | // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is |
michael@0 | 855 | // actually right. |
michael@0 | 856 | bool bogusLoadBackground = false; |
michael@0 | 857 | if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) && |
michael@0 | 858 | (aLoadFlags & LOAD_BACKGROUND)) { |
michael@0 | 859 | // We're getting a LOAD_BACKGROUND, but it's probably just our own fake |
michael@0 | 860 | // flag being mirrored to us. The one exception is if our loadgroup is |
michael@0 | 861 | // LOAD_BACKGROUND. |
michael@0 | 862 | bool loadGroupIsBackground = false; |
michael@0 | 863 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 864 | mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 865 | if (loadGroup) { |
michael@0 | 866 | nsLoadFlags loadGroupFlags; |
michael@0 | 867 | loadGroup->GetLoadFlags(&loadGroupFlags); |
michael@0 | 868 | loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0); |
michael@0 | 869 | } |
michael@0 | 870 | bogusLoadBackground = !loadGroupIsBackground; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | // Classifying a javascript: URI doesn't help us, and requires |
michael@0 | 874 | // NSS to boot, which we don't have in content processes. See |
michael@0 | 875 | // https://bugzilla.mozilla.org/show_bug.cgi?id=617838. |
michael@0 | 876 | aLoadFlags &= ~LOAD_CLASSIFY_URI; |
michael@0 | 877 | |
michael@0 | 878 | // Since the javascript channel is never the actual channel that |
michael@0 | 879 | // any data is loaded through, don't ever set the |
michael@0 | 880 | // LOAD_DOCUMENT_URI flag on it, since that could lead to two |
michael@0 | 881 | // 'document channels' in the loadgroup if a javascript: URL is |
michael@0 | 882 | // loaded while a document is being loaded in the same window. |
michael@0 | 883 | |
michael@0 | 884 | // XXXbz this, and a whole lot of other hackery, could go away if we'd just |
michael@0 | 885 | // cancel the current document load on javascript: load start like IE does. |
michael@0 | 886 | |
michael@0 | 887 | mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI; |
michael@0 | 888 | |
michael@0 | 889 | if (bogusLoadBackground) { |
michael@0 | 890 | aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND; |
michael@0 | 891 | } |
michael@0 | 892 | |
michael@0 | 893 | mActualLoadFlags = aLoadFlags; |
michael@0 | 894 | |
michael@0 | 895 | // ... but the underlying stream channel should get this bit, if |
michael@0 | 896 | // set, since that'll be the real document channel if the |
michael@0 | 897 | // javascript: URL generated data. |
michael@0 | 898 | |
michael@0 | 899 | return mStreamChannel->SetLoadFlags(aLoadFlags); |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | NS_IMETHODIMP |
michael@0 | 903 | nsJSChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) |
michael@0 | 904 | { |
michael@0 | 905 | return mStreamChannel->GetLoadGroup(aLoadGroup); |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | NS_IMETHODIMP |
michael@0 | 909 | nsJSChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) |
michael@0 | 910 | { |
michael@0 | 911 | if (aLoadGroup) { |
michael@0 | 912 | bool streamPending; |
michael@0 | 913 | nsresult rv = mStreamChannel->IsPending(&streamPending); |
michael@0 | 914 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 915 | |
michael@0 | 916 | if (streamPending) { |
michael@0 | 917 | nsCOMPtr<nsILoadGroup> curLoadGroup; |
michael@0 | 918 | mStreamChannel->GetLoadGroup(getter_AddRefs(curLoadGroup)); |
michael@0 | 919 | |
michael@0 | 920 | if (aLoadGroup != curLoadGroup) { |
michael@0 | 921 | // Move the stream channel to our new loadgroup. Make sure to |
michael@0 | 922 | // add it before removing it, so that we don't trigger onload |
michael@0 | 923 | // by accident. |
michael@0 | 924 | aLoadGroup->AddRequest(mStreamChannel, nullptr); |
michael@0 | 925 | if (curLoadGroup) { |
michael@0 | 926 | curLoadGroup->RemoveRequest(mStreamChannel, nullptr, |
michael@0 | 927 | NS_BINDING_RETARGETED); |
michael@0 | 928 | } |
michael@0 | 929 | } |
michael@0 | 930 | } |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | return mStreamChannel->SetLoadGroup(aLoadGroup); |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | NS_IMETHODIMP |
michael@0 | 937 | nsJSChannel::GetOwner(nsISupports* *aOwner) |
michael@0 | 938 | { |
michael@0 | 939 | return mStreamChannel->GetOwner(aOwner); |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | NS_IMETHODIMP |
michael@0 | 943 | nsJSChannel::SetOwner(nsISupports* aOwner) |
michael@0 | 944 | { |
michael@0 | 945 | return mStreamChannel->SetOwner(aOwner); |
michael@0 | 946 | } |
michael@0 | 947 | |
michael@0 | 948 | NS_IMETHODIMP |
michael@0 | 949 | nsJSChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) |
michael@0 | 950 | { |
michael@0 | 951 | return mStreamChannel->GetNotificationCallbacks(aCallbacks); |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | NS_IMETHODIMP |
michael@0 | 955 | nsJSChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) |
michael@0 | 956 | { |
michael@0 | 957 | return mStreamChannel->SetNotificationCallbacks(aCallbacks); |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | NS_IMETHODIMP |
michael@0 | 961 | nsJSChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) |
michael@0 | 962 | { |
michael@0 | 963 | return mStreamChannel->GetSecurityInfo(aSecurityInfo); |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | NS_IMETHODIMP |
michael@0 | 967 | nsJSChannel::GetContentType(nsACString &aContentType) |
michael@0 | 968 | { |
michael@0 | 969 | return mStreamChannel->GetContentType(aContentType); |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | NS_IMETHODIMP |
michael@0 | 973 | nsJSChannel::SetContentType(const nsACString &aContentType) |
michael@0 | 974 | { |
michael@0 | 975 | return mStreamChannel->SetContentType(aContentType); |
michael@0 | 976 | } |
michael@0 | 977 | |
michael@0 | 978 | NS_IMETHODIMP |
michael@0 | 979 | nsJSChannel::GetContentCharset(nsACString &aContentCharset) |
michael@0 | 980 | { |
michael@0 | 981 | return mStreamChannel->GetContentCharset(aContentCharset); |
michael@0 | 982 | } |
michael@0 | 983 | |
michael@0 | 984 | NS_IMETHODIMP |
michael@0 | 985 | nsJSChannel::SetContentCharset(const nsACString &aContentCharset) |
michael@0 | 986 | { |
michael@0 | 987 | return mStreamChannel->SetContentCharset(aContentCharset); |
michael@0 | 988 | } |
michael@0 | 989 | |
michael@0 | 990 | NS_IMETHODIMP |
michael@0 | 991 | nsJSChannel::GetContentDisposition(uint32_t *aContentDisposition) |
michael@0 | 992 | { |
michael@0 | 993 | return mStreamChannel->GetContentDisposition(aContentDisposition); |
michael@0 | 994 | } |
michael@0 | 995 | |
michael@0 | 996 | NS_IMETHODIMP |
michael@0 | 997 | nsJSChannel::SetContentDisposition(uint32_t aContentDisposition) |
michael@0 | 998 | { |
michael@0 | 999 | return mStreamChannel->SetContentDisposition(aContentDisposition); |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | NS_IMETHODIMP |
michael@0 | 1003 | nsJSChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) |
michael@0 | 1004 | { |
michael@0 | 1005 | return mStreamChannel->GetContentDispositionFilename(aContentDispositionFilename); |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | NS_IMETHODIMP |
michael@0 | 1009 | nsJSChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) |
michael@0 | 1010 | { |
michael@0 | 1011 | return mStreamChannel->SetContentDispositionFilename(aContentDispositionFilename); |
michael@0 | 1012 | } |
michael@0 | 1013 | |
michael@0 | 1014 | NS_IMETHODIMP |
michael@0 | 1015 | nsJSChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) |
michael@0 | 1016 | { |
michael@0 | 1017 | return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader); |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | NS_IMETHODIMP |
michael@0 | 1021 | nsJSChannel::GetContentLength(int64_t *aContentLength) |
michael@0 | 1022 | { |
michael@0 | 1023 | return mStreamChannel->GetContentLength(aContentLength); |
michael@0 | 1024 | } |
michael@0 | 1025 | |
michael@0 | 1026 | NS_IMETHODIMP |
michael@0 | 1027 | nsJSChannel::SetContentLength(int64_t aContentLength) |
michael@0 | 1028 | { |
michael@0 | 1029 | return mStreamChannel->SetContentLength(aContentLength); |
michael@0 | 1030 | } |
michael@0 | 1031 | |
michael@0 | 1032 | NS_IMETHODIMP |
michael@0 | 1033 | nsJSChannel::OnStartRequest(nsIRequest* aRequest, |
michael@0 | 1034 | nsISupports* aContext) |
michael@0 | 1035 | { |
michael@0 | 1036 | NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); |
michael@0 | 1037 | |
michael@0 | 1038 | return mListener->OnStartRequest(this, aContext); |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | NS_IMETHODIMP |
michael@0 | 1042 | nsJSChannel::OnDataAvailable(nsIRequest* aRequest, |
michael@0 | 1043 | nsISupports* aContext, |
michael@0 | 1044 | nsIInputStream* aInputStream, |
michael@0 | 1045 | uint64_t aOffset, |
michael@0 | 1046 | uint32_t aCount) |
michael@0 | 1047 | { |
michael@0 | 1048 | NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); |
michael@0 | 1049 | |
michael@0 | 1050 | return mListener->OnDataAvailable(this, aContext, aInputStream, aOffset, |
michael@0 | 1051 | aCount); |
michael@0 | 1052 | } |
michael@0 | 1053 | |
michael@0 | 1054 | NS_IMETHODIMP |
michael@0 | 1055 | nsJSChannel::OnStopRequest(nsIRequest* aRequest, |
michael@0 | 1056 | nsISupports* aContext, |
michael@0 | 1057 | nsresult aStatus) |
michael@0 | 1058 | { |
michael@0 | 1059 | NS_ENSURE_TRUE(aRequest == mStreamChannel, NS_ERROR_UNEXPECTED); |
michael@0 | 1060 | |
michael@0 | 1061 | nsCOMPtr<nsIStreamListener> listener = mListener; |
michael@0 | 1062 | |
michael@0 | 1063 | CleanupStrongRefs(); |
michael@0 | 1064 | |
michael@0 | 1065 | // Make sure aStatus matches what GetStatus() returns |
michael@0 | 1066 | if (NS_FAILED(mStatus)) { |
michael@0 | 1067 | aStatus = mStatus; |
michael@0 | 1068 | } |
michael@0 | 1069 | |
michael@0 | 1070 | nsresult rv = listener->OnStopRequest(this, aContext, aStatus); |
michael@0 | 1071 | |
michael@0 | 1072 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 1073 | mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 1074 | if (loadGroup) { |
michael@0 | 1075 | loadGroup->RemoveRequest(this, nullptr, mStatus); |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | mIsActive = false; |
michael@0 | 1079 | |
michael@0 | 1080 | return rv; |
michael@0 | 1081 | } |
michael@0 | 1082 | |
michael@0 | 1083 | NS_IMETHODIMP |
michael@0 | 1084 | nsJSChannel::SetExecutionPolicy(uint32_t aPolicy) |
michael@0 | 1085 | { |
michael@0 | 1086 | NS_ENSURE_ARG(aPolicy <= EXECUTE_NORMAL); |
michael@0 | 1087 | |
michael@0 | 1088 | mExecutionPolicy = aPolicy; |
michael@0 | 1089 | return NS_OK; |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | NS_IMETHODIMP |
michael@0 | 1093 | nsJSChannel::GetExecutionPolicy(uint32_t* aPolicy) |
michael@0 | 1094 | { |
michael@0 | 1095 | *aPolicy = mExecutionPolicy; |
michael@0 | 1096 | return NS_OK; |
michael@0 | 1097 | } |
michael@0 | 1098 | |
michael@0 | 1099 | NS_IMETHODIMP |
michael@0 | 1100 | nsJSChannel::SetExecuteAsync(bool aIsAsync) |
michael@0 | 1101 | { |
michael@0 | 1102 | if (!mIsActive) { |
michael@0 | 1103 | mIsAsync = aIsAsync; |
michael@0 | 1104 | } |
michael@0 | 1105 | // else ignore this call |
michael@0 | 1106 | NS_WARN_IF_FALSE(!mIsActive, "Calling SetExecuteAsync on active channel?"); |
michael@0 | 1107 | |
michael@0 | 1108 | return NS_OK; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | NS_IMETHODIMP |
michael@0 | 1112 | nsJSChannel::GetExecuteAsync(bool* aIsAsync) |
michael@0 | 1113 | { |
michael@0 | 1114 | *aIsAsync = mIsAsync; |
michael@0 | 1115 | return NS_OK; |
michael@0 | 1116 | } |
michael@0 | 1117 | |
michael@0 | 1118 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 1119 | |
michael@0 | 1120 | nsJSProtocolHandler::nsJSProtocolHandler() |
michael@0 | 1121 | { |
michael@0 | 1122 | } |
michael@0 | 1123 | |
michael@0 | 1124 | nsresult |
michael@0 | 1125 | nsJSProtocolHandler::Init() |
michael@0 | 1126 | { |
michael@0 | 1127 | return NS_OK; |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | nsJSProtocolHandler::~nsJSProtocolHandler() |
michael@0 | 1131 | { |
michael@0 | 1132 | } |
michael@0 | 1133 | |
michael@0 | 1134 | NS_IMPL_ISUPPORTS(nsJSProtocolHandler, nsIProtocolHandler) |
michael@0 | 1135 | |
michael@0 | 1136 | nsresult |
michael@0 | 1137 | nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) |
michael@0 | 1138 | { |
michael@0 | 1139 | if (aOuter) |
michael@0 | 1140 | return NS_ERROR_NO_AGGREGATION; |
michael@0 | 1141 | |
michael@0 | 1142 | nsJSProtocolHandler* ph = new nsJSProtocolHandler(); |
michael@0 | 1143 | if (!ph) |
michael@0 | 1144 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1145 | NS_ADDREF(ph); |
michael@0 | 1146 | nsresult rv = ph->Init(); |
michael@0 | 1147 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1148 | rv = ph->QueryInterface(aIID, aResult); |
michael@0 | 1149 | } |
michael@0 | 1150 | NS_RELEASE(ph); |
michael@0 | 1151 | return rv; |
michael@0 | 1152 | } |
michael@0 | 1153 | |
michael@0 | 1154 | nsresult |
michael@0 | 1155 | nsJSProtocolHandler::EnsureUTF8Spec(const nsAFlatCString &aSpec, const char *aCharset, |
michael@0 | 1156 | nsACString &aUTF8Spec) |
michael@0 | 1157 | { |
michael@0 | 1158 | aUTF8Spec.Truncate(); |
michael@0 | 1159 | |
michael@0 | 1160 | nsresult rv; |
michael@0 | 1161 | |
michael@0 | 1162 | if (!mTextToSubURI) { |
michael@0 | 1163 | mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); |
michael@0 | 1164 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1165 | } |
michael@0 | 1166 | nsAutoString uStr; |
michael@0 | 1167 | rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, uStr); |
michael@0 | 1168 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1169 | |
michael@0 | 1170 | if (!IsASCII(uStr)) |
michael@0 | 1171 | NS_EscapeURL(NS_ConvertUTF16toUTF8(uStr), esc_AlwaysCopy | esc_OnlyNonASCII, aUTF8Spec); |
michael@0 | 1172 | |
michael@0 | 1173 | return NS_OK; |
michael@0 | 1174 | } |
michael@0 | 1175 | |
michael@0 | 1176 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 1177 | // nsIProtocolHandler methods: |
michael@0 | 1178 | |
michael@0 | 1179 | NS_IMETHODIMP |
michael@0 | 1180 | nsJSProtocolHandler::GetScheme(nsACString &result) |
michael@0 | 1181 | { |
michael@0 | 1182 | result = "javascript"; |
michael@0 | 1183 | return NS_OK; |
michael@0 | 1184 | } |
michael@0 | 1185 | |
michael@0 | 1186 | NS_IMETHODIMP |
michael@0 | 1187 | nsJSProtocolHandler::GetDefaultPort(int32_t *result) |
michael@0 | 1188 | { |
michael@0 | 1189 | *result = -1; // no port for javascript: URLs |
michael@0 | 1190 | return NS_OK; |
michael@0 | 1191 | } |
michael@0 | 1192 | |
michael@0 | 1193 | NS_IMETHODIMP |
michael@0 | 1194 | nsJSProtocolHandler::GetProtocolFlags(uint32_t *result) |
michael@0 | 1195 | { |
michael@0 | 1196 | *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT | |
michael@0 | 1197 | URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_OPENING_EXECUTES_SCRIPT; |
michael@0 | 1198 | return NS_OK; |
michael@0 | 1199 | } |
michael@0 | 1200 | |
michael@0 | 1201 | NS_IMETHODIMP |
michael@0 | 1202 | nsJSProtocolHandler::NewURI(const nsACString &aSpec, |
michael@0 | 1203 | const char *aCharset, |
michael@0 | 1204 | nsIURI *aBaseURI, |
michael@0 | 1205 | nsIURI **result) |
michael@0 | 1206 | { |
michael@0 | 1207 | nsresult rv; |
michael@0 | 1208 | |
michael@0 | 1209 | // javascript: URLs (currently) have no additional structure beyond that |
michael@0 | 1210 | // provided by standard URLs, so there is no "outer" object given to |
michael@0 | 1211 | // CreateInstance. |
michael@0 | 1212 | |
michael@0 | 1213 | nsCOMPtr<nsIURI> url = new nsJSURI(aBaseURI); |
michael@0 | 1214 | |
michael@0 | 1215 | if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) |
michael@0 | 1216 | rv = url->SetSpec(aSpec); |
michael@0 | 1217 | else { |
michael@0 | 1218 | nsAutoCString utf8Spec; |
michael@0 | 1219 | rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec); |
michael@0 | 1220 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1221 | if (utf8Spec.IsEmpty()) |
michael@0 | 1222 | rv = url->SetSpec(aSpec); |
michael@0 | 1223 | else |
michael@0 | 1224 | rv = url->SetSpec(utf8Spec); |
michael@0 | 1225 | } |
michael@0 | 1226 | } |
michael@0 | 1227 | |
michael@0 | 1228 | if (NS_FAILED(rv)) { |
michael@0 | 1229 | return rv; |
michael@0 | 1230 | } |
michael@0 | 1231 | |
michael@0 | 1232 | url.forget(result); |
michael@0 | 1233 | return rv; |
michael@0 | 1234 | } |
michael@0 | 1235 | |
michael@0 | 1236 | NS_IMETHODIMP |
michael@0 | 1237 | nsJSProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) |
michael@0 | 1238 | { |
michael@0 | 1239 | nsresult rv; |
michael@0 | 1240 | nsJSChannel * channel; |
michael@0 | 1241 | |
michael@0 | 1242 | NS_ENSURE_ARG_POINTER(uri); |
michael@0 | 1243 | |
michael@0 | 1244 | channel = new nsJSChannel(); |
michael@0 | 1245 | if (!channel) { |
michael@0 | 1246 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1247 | } |
michael@0 | 1248 | NS_ADDREF(channel); |
michael@0 | 1249 | |
michael@0 | 1250 | rv = channel->Init(uri); |
michael@0 | 1251 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1252 | *result = channel; |
michael@0 | 1253 | NS_ADDREF(*result); |
michael@0 | 1254 | } |
michael@0 | 1255 | NS_RELEASE(channel); |
michael@0 | 1256 | return rv; |
michael@0 | 1257 | } |
michael@0 | 1258 | |
michael@0 | 1259 | NS_IMETHODIMP |
michael@0 | 1260 | nsJSProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) |
michael@0 | 1261 | { |
michael@0 | 1262 | // don't override anything. |
michael@0 | 1263 | *_retval = false; |
michael@0 | 1264 | return NS_OK; |
michael@0 | 1265 | } |
michael@0 | 1266 | |
michael@0 | 1267 | //////////////////////////////////////////////////////////// |
michael@0 | 1268 | // nsJSURI implementation |
michael@0 | 1269 | static NS_DEFINE_CID(kThisSimpleURIImplementationCID, |
michael@0 | 1270 | NS_THIS_SIMPLEURI_IMPLEMENTATION_CID); |
michael@0 | 1271 | |
michael@0 | 1272 | |
michael@0 | 1273 | NS_IMPL_ADDREF_INHERITED(nsJSURI, nsSimpleURI) |
michael@0 | 1274 | NS_IMPL_RELEASE_INHERITED(nsJSURI, nsSimpleURI) |
michael@0 | 1275 | |
michael@0 | 1276 | NS_INTERFACE_MAP_BEGIN(nsJSURI) |
michael@0 | 1277 | if (aIID.Equals(kJSURICID)) |
michael@0 | 1278 | foundInterface = static_cast<nsIURI*>(this); |
michael@0 | 1279 | else if (aIID.Equals(kThisSimpleURIImplementationCID)) { |
michael@0 | 1280 | // Need to return explicitly here, because if we just set foundInterface |
michael@0 | 1281 | // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into |
michael@0 | 1282 | // nsSimplURI::QueryInterface and finding something for this CID. |
michael@0 | 1283 | *aInstancePtr = nullptr; |
michael@0 | 1284 | return NS_NOINTERFACE; |
michael@0 | 1285 | } |
michael@0 | 1286 | else |
michael@0 | 1287 | NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI) |
michael@0 | 1288 | |
michael@0 | 1289 | // nsISerializable methods: |
michael@0 | 1290 | |
michael@0 | 1291 | NS_IMETHODIMP |
michael@0 | 1292 | nsJSURI::Read(nsIObjectInputStream* aStream) |
michael@0 | 1293 | { |
michael@0 | 1294 | nsresult rv = nsSimpleURI::Read(aStream); |
michael@0 | 1295 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1296 | |
michael@0 | 1297 | bool haveBase; |
michael@0 | 1298 | rv = aStream->ReadBoolean(&haveBase); |
michael@0 | 1299 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1300 | |
michael@0 | 1301 | if (haveBase) { |
michael@0 | 1302 | nsCOMPtr<nsISupports> supports; |
michael@0 | 1303 | rv = aStream->ReadObject(true, getter_AddRefs(supports)); |
michael@0 | 1304 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1305 | mBaseURI = do_QueryInterface(supports); |
michael@0 | 1306 | } |
michael@0 | 1307 | |
michael@0 | 1308 | return NS_OK; |
michael@0 | 1309 | } |
michael@0 | 1310 | |
michael@0 | 1311 | NS_IMETHODIMP |
michael@0 | 1312 | nsJSURI::Write(nsIObjectOutputStream* aStream) |
michael@0 | 1313 | { |
michael@0 | 1314 | nsresult rv = nsSimpleURI::Write(aStream); |
michael@0 | 1315 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1316 | |
michael@0 | 1317 | rv = aStream->WriteBoolean(mBaseURI != nullptr); |
michael@0 | 1318 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1319 | |
michael@0 | 1320 | if (mBaseURI) { |
michael@0 | 1321 | rv = aStream->WriteObject(mBaseURI, true); |
michael@0 | 1322 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1323 | } |
michael@0 | 1324 | |
michael@0 | 1325 | return NS_OK; |
michael@0 | 1326 | } |
michael@0 | 1327 | |
michael@0 | 1328 | // nsSimpleURI methods: |
michael@0 | 1329 | /* virtual */ nsSimpleURI* |
michael@0 | 1330 | nsJSURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */) |
michael@0 | 1331 | { |
michael@0 | 1332 | nsCOMPtr<nsIURI> baseClone; |
michael@0 | 1333 | if (mBaseURI) { |
michael@0 | 1334 | // Note: We preserve ref on *base* URI, regardless of ref handling mode. |
michael@0 | 1335 | nsresult rv = mBaseURI->Clone(getter_AddRefs(baseClone)); |
michael@0 | 1336 | if (NS_FAILED(rv)) { |
michael@0 | 1337 | return nullptr; |
michael@0 | 1338 | } |
michael@0 | 1339 | } |
michael@0 | 1340 | |
michael@0 | 1341 | return new nsJSURI(baseClone); |
michael@0 | 1342 | } |
michael@0 | 1343 | |
michael@0 | 1344 | /* virtual */ nsresult |
michael@0 | 1345 | nsJSURI::EqualsInternal(nsIURI* aOther, |
michael@0 | 1346 | nsSimpleURI::RefHandlingEnum aRefHandlingMode, |
michael@0 | 1347 | bool* aResult) |
michael@0 | 1348 | { |
michael@0 | 1349 | NS_ENSURE_ARG_POINTER(aOther); |
michael@0 | 1350 | NS_PRECONDITION(aResult, "null pointer for outparam"); |
michael@0 | 1351 | |
michael@0 | 1352 | nsRefPtr<nsJSURI> otherJSURI; |
michael@0 | 1353 | nsresult rv = aOther->QueryInterface(kJSURICID, |
michael@0 | 1354 | getter_AddRefs(otherJSURI)); |
michael@0 | 1355 | if (NS_FAILED(rv)) { |
michael@0 | 1356 | *aResult = false; // aOther is not a nsJSURI --> not equal. |
michael@0 | 1357 | return NS_OK; |
michael@0 | 1358 | } |
michael@0 | 1359 | |
michael@0 | 1360 | // Compare the member data that our base class knows about. |
michael@0 | 1361 | if (!nsSimpleURI::EqualsInternal(otherJSURI, aRefHandlingMode)) { |
michael@0 | 1362 | *aResult = false; |
michael@0 | 1363 | return NS_OK; |
michael@0 | 1364 | } |
michael@0 | 1365 | |
michael@0 | 1366 | // Compare the piece of additional member data that we add to base class. |
michael@0 | 1367 | nsIURI* otherBaseURI = otherJSURI->GetBaseURI(); |
michael@0 | 1368 | |
michael@0 | 1369 | if (mBaseURI) { |
michael@0 | 1370 | // (As noted in StartClone, we always honor refs on mBaseURI) |
michael@0 | 1371 | return mBaseURI->Equals(otherBaseURI, aResult); |
michael@0 | 1372 | } |
michael@0 | 1373 | |
michael@0 | 1374 | *aResult = !otherBaseURI; |
michael@0 | 1375 | return NS_OK; |
michael@0 | 1376 | } |
michael@0 | 1377 | |
michael@0 | 1378 | NS_IMETHODIMP |
michael@0 | 1379 | nsJSURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) |
michael@0 | 1380 | { |
michael@0 | 1381 | *aClassIDNoAlloc = kJSURICID; |
michael@0 | 1382 | return NS_OK; |
michael@0 | 1383 | } |
michael@0 | 1384 |