1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is mozilla.org code.
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.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
41 A protocol handler for ``chrome:''
45 #include "nsAutoPtr.h"
46 #include "nsChromeProtocolHandler.h"
47 #include "nsChromeRegistry.h"
49 #include "nsContentCID.h"
51 #include "nsThreadUtils.h"
52 #include "nsIChannel.h"
53 #include "nsIChromeRegistry.h"
54 #include "nsIComponentManager.h"
55 #include "nsIFastLoadService.h"
57 #include "nsIFileURL.h"
58 #include "nsIFileChannel.h"
59 #include "nsIIOService.h"
60 #include "nsIJARChannel.h"
61 #include "nsIJARURI.h"
62 #include "nsILoadGroup.h"
63 #include "nsIObjectOutputStream.h"
64 #include "nsIScriptSecurityManager.h"
65 #include "nsIServiceManager.h"
66 #include "nsIStandardURL.h"
67 #include "nsIStreamListener.h"
68 #include "nsNetUtil.h"
69 #include "nsXPIDLString.h"
74 #include "nsIXULPrototypeCache.h"
77 //----------------------------------------------------------------------
79 static NS_DEFINE_CID(kXULPrototypeCacheCID
, NS_XULPROTOTYPECACHE_CID
);
81 //----------------------------------------------------------------------
83 // A channel that's used for loading cached chrome documents. Since a
84 // cached chrome document really doesn't have anything to do to load,
85 // this is just the puppeteer that pulls the webshell's strings at the
88 // Specifically, when AsyncOpen() is called, it adds the channel to
89 // the load group, and queues an asychronous event to fire the
90 // listener's OnStartRequest().
92 // After triggering OnStartRequest(), it then queues another event
93 // which will fire the listener's OnStopRequest() and remove the
94 // channel from the load group.
96 // Each is done asynchronously to allow the stack to unwind back to
97 // the main event loop. This avoids any weird re-entrancy that occurs
98 // if we try to immediately fire the On[Start|Stop]Request().
100 // For logging information, NSPR_LOG_MODULES=nsCachedChromeChannel:5
102 #define LOG(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
104 #define NS_CACHEDCHROMECHANNEL_IMPL_IID \
105 { 0x281371d3, 0x6bc2, 0x499f, \
106 { 0x8d, 0x70, 0xcb, 0xfc, 0x01, 0x1b, 0xa0, 0x43 } }
108 class nsCachedChromeChannel
: public nsIChannel
111 ~nsCachedChromeChannel();
113 nsCOMPtr
<nsIURI
> mURI
;
114 nsCOMPtr
<nsIURI
> mOriginalURI
;
115 nsCOMPtr
<nsILoadGroup
> mLoadGroup
;
116 nsCOMPtr
<nsIStreamListener
> mListener
;
117 nsCOMPtr
<nsISupports
> mContext
;
118 nsLoadFlags mLoadFlags
;
119 nsCOMPtr
<nsISupports
> mOwner
;
123 static PRLogModuleInfo
* gLog
;
126 void HandleLoadEvent();
129 nsCachedChromeChannel(nsIURI
* aURI
);
131 NS_DECLARE_STATIC_IID_ACCESSOR(NS_CACHEDCHROMECHANNEL_IMPL_IID
)
136 NS_IMETHOD
GetName(nsACString
&result
) { return mURI
->GetSpec(result
); }
137 NS_IMETHOD
IsPending(PRBool
*_retval
) { *_retval
= (mListener
!= nsnull
); return NS_OK
; }
138 NS_IMETHOD
GetStatus(nsresult
*status
) { *status
= mStatus
; return NS_OK
; }
139 NS_IMETHOD
Cancel(nsresult status
) { mStatus
= status
; return NS_OK
; }
140 NS_IMETHOD
Suspend(void) { return NS_OK
; } // XXX technically wrong
141 NS_IMETHOD
Resume(void) { return NS_OK
; } // XXX technically wrong
142 NS_IMETHOD
GetLoadGroup(nsILoadGroup
**);
143 NS_IMETHOD
SetLoadGroup(nsILoadGroup
*);
144 NS_IMETHOD
GetLoadFlags(nsLoadFlags
*);
145 NS_IMETHOD
SetLoadFlags(nsLoadFlags
);
151 NS_DEFINE_STATIC_IID_ACCESSOR(nsCachedChromeChannel
,
152 NS_CACHEDCHROMECHANNEL_IMPL_IID
)
155 PRLogModuleInfo
* nsCachedChromeChannel::gLog
;
158 NS_IMPL_ISUPPORTS3(nsCachedChromeChannel
, nsIChannel
, nsIRequest
,
159 nsCachedChromeChannel
)
161 nsCachedChromeChannel::nsCachedChromeChannel(nsIURI
* aURI
)
163 , mLoadFlags(nsIRequest::LOAD_NORMAL
)
168 gLog
= PR_NewLogModule("nsCachedChromeChannel");
171 LOG(("nsCachedChromeChannel[%p]: created", this));
175 nsCachedChromeChannel::~nsCachedChromeChannel()
177 LOG(("nsCachedChromeChannel[%p]: destroyed", this));
182 nsCachedChromeChannel::GetOriginalURI(nsIURI
* *aOriginalURI
)
184 *aOriginalURI
= mOriginalURI
? mOriginalURI
: mURI
;
185 NS_ADDREF(*aOriginalURI
);
190 nsCachedChromeChannel::SetOriginalURI(nsIURI
* aOriginalURI
)
192 mOriginalURI
= aOriginalURI
;
197 nsCachedChromeChannel::GetURI(nsIURI
* *aURI
)
205 nsCachedChromeChannel::Open(nsIInputStream
**_retval
)
207 // NS_NOTREACHED("don't do that");
209 return NS_ERROR_FAILURE
;
213 nsCachedChromeChannel::AsyncOpen(nsIStreamListener
*listener
, nsISupports
*ctxt
)
215 NS_ENSURE_ARG_POINTER(listener
);
219 // Fire OnStartRequest and OnStopRequest, which will cause the XUL
220 // document to get embedded.
221 LOG(("nsCachedChromeChannel[%p]: posting load event for %p",
224 nsCOMPtr
<nsIRunnable
> event
=
225 NS_NEW_RUNNABLE_METHOD(nsCachedChromeChannel
, this, HandleLoadEvent
);
227 // Queue an event to ourselves to let the stack unwind before
228 // calling OnStartRequest(). This allows embedding to occur
229 // before we fire OnStopRequest().
230 rv
= NS_DispatchToCurrentThread(event
);
235 mListener
= listener
;
238 LOG(("nsCachedChromeChannel[%p]: adding self to load group %p",
239 this, mLoadGroup
.get()));
241 (void) mLoadGroup
->AddRequest(this, nsnull
);
247 nsCachedChromeChannel::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
249 return NS_ERROR_NOT_IMPLEMENTED
;
253 nsCachedChromeChannel::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
255 *aLoadFlags
= mLoadFlags
;
260 nsCachedChromeChannel::SetLoadFlags(nsLoadFlags aLoadFlags
)
262 mLoadFlags
= aLoadFlags
;
267 nsCachedChromeChannel::GetOwner(nsISupports
* *aOwner
)
270 NS_IF_ADDREF(*aOwner
);
275 nsCachedChromeChannel::SetOwner(nsISupports
* aOwner
)
282 nsCachedChromeChannel::GetLoadGroup(nsILoadGroup
* *aLoadGroup
)
284 *aLoadGroup
= mLoadGroup
;
285 NS_IF_ADDREF(*aLoadGroup
);
290 nsCachedChromeChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
292 mLoadGroup
= aLoadGroup
;
297 nsCachedChromeChannel::GetNotificationCallbacks(nsIInterfaceRequestor
* *aNotificationCallbacks
)
299 *aNotificationCallbacks
= nsnull
;
300 return NS_ERROR_FAILURE
;
304 nsCachedChromeChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aNotificationCallbacks
)
306 return NS_OK
; // ignored
310 nsCachedChromeChannel::GetContentType(nsACString
&aContentType
)
312 aContentType
.AssignLiteral("mozilla.application/cached-xul");
317 nsCachedChromeChannel::SetContentType(const nsACString
&aContentType
)
319 // Do not allow the content-type to be changed.
320 NS_NOTREACHED("don't do that");
321 return NS_ERROR_FAILURE
;
325 nsCachedChromeChannel::GetContentCharset(nsACString
&aContentCharset
)
327 aContentCharset
.Truncate();
332 nsCachedChromeChannel::SetContentCharset(const nsACString
&aContentCharset
)
334 // Do not allow the content charset to be changed.
335 NS_NOTREACHED("don't do that");
336 return NS_ERROR_FAILURE
;
340 nsCachedChromeChannel::GetContentLength(PRInt32
*aContentLength
)
342 NS_NOTREACHED("don't do that");
344 return NS_ERROR_FAILURE
;
348 nsCachedChromeChannel::SetContentLength(PRInt32 aContentLength
)
350 NS_NOTREACHED("nsCachedChromeChannel::SetContentLength");
351 return NS_ERROR_NOT_IMPLEMENTED
;
355 nsCachedChromeChannel::HandleLoadEvent()
357 // Fire the OnStartRequest() for the cached chrome channel, then
358 // trigger the OnStopRequest()...
360 // If the load has been cancelled, then just bail now. We won't
361 // send On[Start|Stop]Request().
362 if (NS_FAILED(mStatus
))
365 LOG(("nsCachedChromeChannel[%p]: firing OnStartRequest for %p",
366 this, mListener
.get()));
368 mListener
->OnStartRequest(this, mContext
);
370 LOG(("nsCachedChromeChannel[%p]: firing OnStopRequest for %p",
371 this, mListener
.get()));
373 mListener
->OnStopRequest(this, mContext
, mStatus
);
376 LOG(("nsCachedChromeChannel[%p]: removing self from load group %p",
377 this, mLoadGroup
.get()));
378 mLoadGroup
->RemoveRequest(this, nsnull
, mStatus
);
385 ////////////////////////////////////////////////////////////////////////////////
387 NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler
,
389 nsISupportsWeakReference
)
391 ////////////////////////////////////////////////////////////////////////////////
392 // nsIProtocolHandler methods:
395 nsChromeProtocolHandler::GetScheme(nsACString
&result
)
397 result
.AssignLiteral("chrome");
402 nsChromeProtocolHandler::GetDefaultPort(PRInt32
*result
)
404 *result
= -1; // no port for chrome: URLs
409 nsChromeProtocolHandler::AllowPort(PRInt32 port
, const char *scheme
, PRBool
*_retval
)
411 // don't override anything.
417 nsChromeProtocolHandler::GetProtocolFlags(PRUint32
*result
)
419 *result
= URI_STD
| URI_IS_UI_RESOURCE
;
424 nsChromeProtocolHandler::NewURI(const nsACString
&aSpec
,
425 const char *aCharset
,
431 // Chrome: URLs (currently) have no additional structure beyond that provided
432 // by standard URLs, so there is no "outer" given to CreateInstance
434 nsCOMPtr
<nsIStandardURL
> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID
, &rv
));
435 NS_ENSURE_SUCCESS(rv
, rv
);
437 rv
= surl
->Init(nsIStandardURL::URLTYPE_STANDARD
, -1, aSpec
, aCharset
, aBaseURI
);
441 nsCOMPtr
<nsIURL
> url(do_QueryInterface(surl
, &rv
));
442 NS_ENSURE_SUCCESS(rv
, rv
);
444 // Canonify the "chrome:" URL; e.g., so that we collapse
445 // "chrome://navigator/content/" and "chrome://navigator/content"
446 // and "chrome://navigator/content/navigator.xul".
448 rv
= nsChromeRegistry::Canonify(url
);
452 surl
->SetMutable(PR_FALSE
);
454 NS_ADDREF(*result
= url
);
459 nsChromeProtocolHandler::NewChannel(nsIURI
* aURI
,
460 nsIChannel
* *aResult
)
464 NS_ENSURE_ARG_POINTER(aURI
);
465 NS_PRECONDITION(aResult
, "Null out param");
468 // Check that the uri we got is already canonified
470 nsCOMPtr
<nsIURI
> debugClone
;
471 debug_rv
= aURI
->Clone(getter_AddRefs(debugClone
));
472 if (NS_SUCCEEDED(debug_rv
)) {
473 nsCOMPtr
<nsIURL
> debugURL (do_QueryInterface(debugClone
));
474 debug_rv
= nsChromeRegistry::Canonify(debugURL
);
475 if (NS_SUCCEEDED(debug_rv
)) {
477 debug_rv
= aURI
->Equals(debugURL
, &same
);
478 if (NS_SUCCEEDED(debug_rv
)) {
479 NS_ASSERTION(same
, "Non-canonified chrome uri passed to nsChromeProtocolHandler::NewChannel!");
485 nsCOMPtr
<nsIChannel
> result
;
488 // Check the prototype cache to see if we've already got the
489 // document in the cache.
490 nsCOMPtr
<nsIXULPrototypeCache
> cache
491 (do_GetService(kXULPrototypeCacheCID
));
493 PRBool isCached
= PR_FALSE
;
495 isCached
= cache
->IsCached(aURI
);
497 NS_WARNING("Unable to obtain the XUL prototype cache!");
499 // Same comment as nsXULDocument::StartDocumentLoad and
500 // nsXULDocument::ResumeWalk
503 // We don't abort on failure here because there are too many valid
504 // cases that can return failure, and the null-ness of |proto| is enough
505 // to trigger the fail-safe parse-from-disk solution. Example failure cases
506 // (for reference) include:
508 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
510 // other: the FastLoad cache file, XUL.mfl, could not be found, probably
511 // due to being accessed before a profile has been selected (e.g.
512 // loading chrome for the profile manager itself). This must be
516 // ...in which case, we'll create a dummy stream that'll just
518 result
= new nsCachedChromeChannel(aURI
);
520 return NS_ERROR_OUT_OF_MEMORY
;
524 // Miss. Resolve the chrome URL using the registry and do a
525 // normal necko load.
526 //nsXPIDLCString oldSpec;
527 //aURI->GetSpec(getter_Copies(oldSpec));
528 //printf("*************************** %s\n", (const char*)oldSpec);
530 if (!nsChromeRegistry::gChromeRegistry
) {
531 // We don't actually want this ref, we just want the service to
532 // initialize if it hasn't already.
533 nsCOMPtr
<nsIChromeRegistry
> reg (do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
536 NS_ENSURE_TRUE(nsChromeRegistry::gChromeRegistry
, NS_ERROR_FAILURE
);
538 nsCOMPtr
<nsIURI
> resolvedURI
;
539 rv
= nsChromeRegistry::gChromeRegistry
->ConvertChromeURL(aURI
, getter_AddRefs(resolvedURI
));
544 printf("Couldn't convert chrome URL: %s\n", spec
.get());
549 nsCOMPtr
<nsIIOService
> ioServ (do_GetIOService(&rv
));
550 NS_ENSURE_SUCCESS(rv
, rv
);
552 rv
= ioServ
->NewChannelFromURI(resolvedURI
, getter_AddRefs(result
));
553 if (NS_FAILED(rv
)) return rv
;
555 // XXX Will be removed someday when we handle remote chrome.
556 nsCOMPtr
<nsIFileChannel
> fileChan
557 (do_QueryInterface(result
));
560 nsCOMPtr
<nsIFile
> file
;
561 fileChan
->GetFile(getter_AddRefs(file
));
563 PRBool exists
= PR_FALSE
;
564 file
->Exists(&exists
);
567 file
->GetNativePath(path
);
568 printf("Chrome file doesn't exist: %s\n", path
.get());
573 nsCOMPtr
<nsIJARChannel
> jarChan
574 (do_QueryInterface(result
));
576 nsRefPtr
<nsCachedChromeChannel
> cachedChannel
;
577 if (NS_FAILED(CallQueryInterface(result
.get(),
578 static_cast<nsCachedChromeChannel
**>(
579 getter_AddRefs(cachedChannel
))))) {
580 NS_WARNING("Remote chrome not allowed! Only file:, resource:, jar:, and cached chrome channels are valid.\n");
582 return NS_ERROR_FAILURE
;
587 // Make sure that the channel remembers where it was
588 // originally loaded from.
589 rv
= result
->SetOriginalURI(aURI
);
590 if (NS_FAILED(rv
)) return rv
;
592 // Get a system principal for content files and set the owner
593 // property of the result
594 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
596 rv
= url
->GetPath(path
);
597 if (StringBeginsWith(path
, NS_LITERAL_CSTRING("/content/")))
599 nsCOMPtr
<nsIScriptSecurityManager
> securityManager
=
600 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rv
);
601 if (NS_FAILED(rv
)) return rv
;
603 nsCOMPtr
<nsIPrincipal
> principal
;
604 rv
= securityManager
->GetSystemPrincipal(getter_AddRefs(principal
));
605 if (NS_FAILED(rv
)) return rv
;
607 nsCOMPtr
<nsISupports
> owner
= do_QueryInterface(principal
);
608 result
->SetOwner(owner
);
612 // Track FastLoad file dependencies.
614 // This is harder than it ought to be! While an nsResChannel "is-a"
615 // nsIFileChannel, an nsJARChannel is not. Once you unravel the jar:
616 // URI, you may have a resource: URL -- but without a channel for it,
617 // you can't get the URI that it yields through substitution!
619 // XXXbe fix nsResChannel.cpp to move the substitution code into a new
621 nsCOMPtr
<nsIFastLoadService
> fastLoadServ(do_GetFastLoadService());
623 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
;
624 fastLoadServ
->GetOutputStream(getter_AddRefs(objectOutput
));
626 nsCOMPtr
<nsIFile
> file
;
629 fileChan
->GetFile(getter_AddRefs(file
));
631 nsCOMPtr
<nsIURI
> uri
;
632 result
->GetURI(getter_AddRefs(uri
));
634 // Loop, jar URIs can nest (e.g. jar:jar:A.jar!B.jar!C.xml).
635 // Often, however, we have jar:resource:/chrome/A.jar!C.xml.
636 nsCOMPtr
<nsIJARURI
> jarURI
;
637 while ((jarURI
= do_QueryInterface(uri
)) != nsnull
)
638 jarURI
->GetJARFile(getter_AddRefs(uri
));
640 // Here we have a URL of the form resource:/chrome/A.jar
641 // or file:/some/path/to/A.jar.
642 nsCOMPtr
<nsIFileURL
> fileURL(do_QueryInterface(uri
));
644 fileURL
->GetFile(getter_AddRefs(file
));
648 rv
= fastLoadServ
->AddDependency(file
);
650 cache
->AbortFastLoads();
662 ////////////////////////////////////////////////////////////////////////////////