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
)
164 , mLoadFlags(nsIRequest::LOAD_NORMAL
)
169 gLog
= PR_NewLogModule("nsCachedChromeChannel");
172 LOG(("nsCachedChromeChannel[%p]: created", this));
176 nsCachedChromeChannel::~nsCachedChromeChannel()
178 LOG(("nsCachedChromeChannel[%p]: destroyed", this));
183 nsCachedChromeChannel::GetOriginalURI(nsIURI
* *aOriginalURI
)
185 *aOriginalURI
= mOriginalURI
;
186 NS_ADDREF(*aOriginalURI
);
191 nsCachedChromeChannel::SetOriginalURI(nsIURI
* aOriginalURI
)
193 NS_ENSURE_ARG_POINTER(aOriginalURI
);
194 mOriginalURI
= aOriginalURI
;
199 nsCachedChromeChannel::GetURI(nsIURI
* *aURI
)
207 nsCachedChromeChannel::Open(nsIInputStream
**_retval
)
209 // NS_NOTREACHED("don't do that");
211 return NS_ERROR_FAILURE
;
215 nsCachedChromeChannel::AsyncOpen(nsIStreamListener
*listener
, nsISupports
*ctxt
)
217 NS_ENSURE_ARG_POINTER(listener
);
221 // Fire OnStartRequest and OnStopRequest, which will cause the XUL
222 // document to get embedded.
223 LOG(("nsCachedChromeChannel[%p]: posting load event for %p",
226 nsCOMPtr
<nsIRunnable
> event
=
227 NS_NEW_RUNNABLE_METHOD(nsCachedChromeChannel
, this, HandleLoadEvent
);
229 // Queue an event to ourselves to let the stack unwind before
230 // calling OnStartRequest(). This allows embedding to occur
231 // before we fire OnStopRequest().
232 rv
= NS_DispatchToCurrentThread(event
);
237 mListener
= listener
;
240 LOG(("nsCachedChromeChannel[%p]: adding self to load group %p",
241 this, mLoadGroup
.get()));
243 (void) mLoadGroup
->AddRequest(this, nsnull
);
249 nsCachedChromeChannel::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
251 return NS_ERROR_NOT_IMPLEMENTED
;
255 nsCachedChromeChannel::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
257 *aLoadFlags
= mLoadFlags
;
262 nsCachedChromeChannel::SetLoadFlags(nsLoadFlags aLoadFlags
)
264 mLoadFlags
= aLoadFlags
;
269 nsCachedChromeChannel::GetOwner(nsISupports
* *aOwner
)
272 NS_IF_ADDREF(*aOwner
);
277 nsCachedChromeChannel::SetOwner(nsISupports
* aOwner
)
284 nsCachedChromeChannel::GetLoadGroup(nsILoadGroup
* *aLoadGroup
)
286 *aLoadGroup
= mLoadGroup
;
287 NS_IF_ADDREF(*aLoadGroup
);
292 nsCachedChromeChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
294 mLoadGroup
= aLoadGroup
;
299 nsCachedChromeChannel::GetNotificationCallbacks(nsIInterfaceRequestor
* *aNotificationCallbacks
)
301 *aNotificationCallbacks
= nsnull
;
302 return NS_ERROR_FAILURE
;
306 nsCachedChromeChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aNotificationCallbacks
)
308 return NS_OK
; // ignored
312 nsCachedChromeChannel::GetContentType(nsACString
&aContentType
)
314 aContentType
.AssignLiteral("mozilla.application/cached-xul");
319 nsCachedChromeChannel::SetContentType(const nsACString
&aContentType
)
321 // Do not allow the content-type to be changed.
322 NS_NOTREACHED("don't do that");
323 return NS_ERROR_FAILURE
;
327 nsCachedChromeChannel::GetContentCharset(nsACString
&aContentCharset
)
329 aContentCharset
.Truncate();
334 nsCachedChromeChannel::SetContentCharset(const nsACString
&aContentCharset
)
336 // Do not allow the content charset to be changed.
337 NS_NOTREACHED("don't do that");
338 return NS_ERROR_FAILURE
;
342 nsCachedChromeChannel::GetContentLength(PRInt32
*aContentLength
)
344 NS_NOTREACHED("don't do that");
346 return NS_ERROR_FAILURE
;
350 nsCachedChromeChannel::SetContentLength(PRInt32 aContentLength
)
352 NS_NOTREACHED("nsCachedChromeChannel::SetContentLength");
353 return NS_ERROR_NOT_IMPLEMENTED
;
357 nsCachedChromeChannel::HandleLoadEvent()
359 // Fire the OnStartRequest() for the cached chrome channel, then
360 // trigger the OnStopRequest()...
362 // If the load has been cancelled, then just bail now. We won't
363 // send On[Start|Stop]Request().
364 if (NS_FAILED(mStatus
))
367 LOG(("nsCachedChromeChannel[%p]: firing OnStartRequest for %p",
368 this, mListener
.get()));
370 mListener
->OnStartRequest(this, mContext
);
372 LOG(("nsCachedChromeChannel[%p]: firing OnStopRequest for %p",
373 this, mListener
.get()));
375 mListener
->OnStopRequest(this, mContext
, mStatus
);
378 LOG(("nsCachedChromeChannel[%p]: removing self from load group %p",
379 this, mLoadGroup
.get()));
380 mLoadGroup
->RemoveRequest(this, nsnull
, mStatus
);
387 ////////////////////////////////////////////////////////////////////////////////
389 NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler
,
391 nsISupportsWeakReference
)
393 ////////////////////////////////////////////////////////////////////////////////
394 // nsIProtocolHandler methods:
397 nsChromeProtocolHandler::GetScheme(nsACString
&result
)
399 result
.AssignLiteral("chrome");
404 nsChromeProtocolHandler::GetDefaultPort(PRInt32
*result
)
406 *result
= -1; // no port for chrome: URLs
411 nsChromeProtocolHandler::AllowPort(PRInt32 port
, const char *scheme
, PRBool
*_retval
)
413 // don't override anything.
419 nsChromeProtocolHandler::GetProtocolFlags(PRUint32
*result
)
421 *result
= URI_STD
| URI_IS_UI_RESOURCE
| URI_IS_LOCAL_RESOURCE
;
426 nsChromeProtocolHandler::NewURI(const nsACString
&aSpec
,
427 const char *aCharset
,
433 // Chrome: URLs (currently) have no additional structure beyond that provided
434 // by standard URLs, so there is no "outer" given to CreateInstance
436 nsCOMPtr
<nsIStandardURL
> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID
, &rv
));
437 NS_ENSURE_SUCCESS(rv
, rv
);
439 rv
= surl
->Init(nsIStandardURL::URLTYPE_STANDARD
, -1, aSpec
, aCharset
, aBaseURI
);
443 nsCOMPtr
<nsIURL
> url(do_QueryInterface(surl
, &rv
));
444 NS_ENSURE_SUCCESS(rv
, rv
);
446 // Canonify the "chrome:" URL; e.g., so that we collapse
447 // "chrome://navigator/content/" and "chrome://navigator/content"
448 // and "chrome://navigator/content/navigator.xul".
450 rv
= nsChromeRegistry::Canonify(url
);
454 surl
->SetMutable(PR_FALSE
);
456 NS_ADDREF(*result
= url
);
461 nsChromeProtocolHandler::NewChannel(nsIURI
* aURI
,
462 nsIChannel
* *aResult
)
466 NS_ENSURE_ARG_POINTER(aURI
);
467 NS_PRECONDITION(aResult
, "Null out param");
470 // Check that the uri we got is already canonified
472 nsCOMPtr
<nsIURI
> debugClone
;
473 debug_rv
= aURI
->Clone(getter_AddRefs(debugClone
));
474 if (NS_SUCCEEDED(debug_rv
)) {
475 nsCOMPtr
<nsIURL
> debugURL (do_QueryInterface(debugClone
));
476 debug_rv
= nsChromeRegistry::Canonify(debugURL
);
477 if (NS_SUCCEEDED(debug_rv
)) {
479 debug_rv
= aURI
->Equals(debugURL
, &same
);
480 if (NS_SUCCEEDED(debug_rv
)) {
481 NS_ASSERTION(same
, "Non-canonified chrome uri passed to nsChromeProtocolHandler::NewChannel!");
487 nsCOMPtr
<nsIChannel
> result
;
490 // Check the prototype cache to see if we've already got the
491 // document in the cache.
492 nsCOMPtr
<nsIXULPrototypeCache
> cache
493 (do_GetService(kXULPrototypeCacheCID
));
495 PRBool isCached
= PR_FALSE
;
497 isCached
= cache
->IsCached(aURI
);
499 NS_WARNING("Unable to obtain the XUL prototype cache!");
501 // Same comment as nsXULDocument::StartDocumentLoad and
502 // nsXULDocument::ResumeWalk
505 // We don't abort on failure here because there are too many valid
506 // cases that can return failure, and the null-ness of |proto| is enough
507 // to trigger the fail-safe parse-from-disk solution. Example failure cases
508 // (for reference) include:
510 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
512 // other: the FastLoad cache file, XUL.mfl, could not be found, probably
513 // due to being accessed before a profile has been selected (e.g.
514 // loading chrome for the profile manager itself). This must be
518 // ...in which case, we'll create a dummy stream that'll just
520 result
= new nsCachedChromeChannel(aURI
);
522 return NS_ERROR_OUT_OF_MEMORY
;
526 // Miss. Resolve the chrome URL using the registry and do a
527 // normal necko load.
528 //nsXPIDLCString oldSpec;
529 //aURI->GetSpec(getter_Copies(oldSpec));
530 //printf("*************************** %s\n", (const char*)oldSpec);
532 if (!nsChromeRegistry::gChromeRegistry
) {
533 // We don't actually want this ref, we just want the service to
534 // initialize if it hasn't already.
535 nsCOMPtr
<nsIChromeRegistry
> reg (do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
538 NS_ENSURE_TRUE(nsChromeRegistry::gChromeRegistry
, NS_ERROR_FAILURE
);
540 nsCOMPtr
<nsIURI
> resolvedURI
;
541 rv
= nsChromeRegistry::gChromeRegistry
->ConvertChromeURL(aURI
, getter_AddRefs(resolvedURI
));
546 printf("Couldn't convert chrome URL: %s\n", spec
.get());
551 nsCOMPtr
<nsIIOService
> ioServ (do_GetIOService(&rv
));
552 NS_ENSURE_SUCCESS(rv
, rv
);
554 rv
= ioServ
->NewChannelFromURI(resolvedURI
, getter_AddRefs(result
));
555 if (NS_FAILED(rv
)) return rv
;
557 // Make sure that the channel remembers where it was
558 // originally loaded from.
559 rv
= result
->SetOriginalURI(aURI
);
560 if (NS_FAILED(rv
)) return rv
;
562 // Get a system principal for content files and set the owner
563 // property of the result
564 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
566 rv
= url
->GetPath(path
);
567 if (StringBeginsWith(path
, NS_LITERAL_CSTRING("/content/")))
569 nsCOMPtr
<nsIScriptSecurityManager
> securityManager
=
570 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
, &rv
);
571 if (NS_FAILED(rv
)) return rv
;
573 nsCOMPtr
<nsIPrincipal
> principal
;
574 rv
= securityManager
->GetSystemPrincipal(getter_AddRefs(principal
));
575 if (NS_FAILED(rv
)) return rv
;
577 nsCOMPtr
<nsISupports
> owner
= do_QueryInterface(principal
);
578 result
->SetOwner(owner
);
582 // Track FastLoad file dependencies.
584 // This is harder than it ought to be! While an nsResChannel "is-a"
585 // nsIFileChannel, an nsJARChannel is not. Once you unravel the jar:
586 // URI, you may have a resource: URL -- but without a channel for it,
587 // you can't get the URI that it yields through substitution!
589 // XXXbe fix nsResChannel.cpp to move the substitution code into a new
591 nsCOMPtr
<nsIFastLoadService
> fastLoadServ(do_GetFastLoadService());
593 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
;
594 fastLoadServ
->GetOutputStream(getter_AddRefs(objectOutput
));
596 nsCOMPtr
<nsIFile
> file
;
598 nsCOMPtr
<nsIURI
> uri
;
599 result
->GetURI(getter_AddRefs(uri
));
600 uri
= NS_GetInnermostURI(uri
);
602 // Here we have a URL of the form resource:/chrome/A.jar
603 // or file:/some/path/to/A.jar.
604 nsCOMPtr
<nsIFileURL
> fileURL(do_QueryInterface(uri
));
606 fileURL
->GetFile(getter_AddRefs(file
));
609 rv
= fastLoadServ
->AddDependency(file
);
611 cache
->AbortFastLoads();
623 ////////////////////////////////////////////////////////////////////////////////