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