Bug 469739 - Add support for displaying Vista UAC shield icon; r=joe sr=vladimir
[wine-gecko.git] / chrome / src / nsChromeProtocolHandler.cpp
blob771dbec1adaf69a6b8e7308a72e18afcb53d5bd8
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
14 * License.
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.
23 * Contributor(s):
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"
48 #include "nsCOMPtr.h"
49 #include "nsContentCID.h"
50 #include "nsCRT.h"
51 #include "nsThreadUtils.h"
52 #include "nsIChannel.h"
53 #include "nsIChromeRegistry.h"
54 #include "nsIComponentManager.h"
55 #include "nsIFastLoadService.h"
56 #include "nsIFile.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"
70 #include "nsString.h"
71 #include "prlog.h"
73 #ifdef MOZ_XUL
74 #include "nsIXULPrototypeCache.h"
75 #endif
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
86 // right time.
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
110 protected:
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;
120 nsresult mStatus;
122 #ifdef PR_LOGGING
123 static PRLogModuleInfo* gLog;
124 #endif
126 void HandleLoadEvent();
128 public:
129 nsCachedChromeChannel(nsIURI* aURI);
131 NS_DECLARE_STATIC_IID_ACCESSOR(NS_CACHEDCHROMECHANNEL_IMPL_IID)
133 NS_DECL_ISUPPORTS
135 // nsIRequest
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);
147 // nsIChannel
148 NS_DECL_NSICHANNEL
151 NS_DEFINE_STATIC_IID_ACCESSOR(nsCachedChromeChannel,
152 NS_CACHEDCHROMECHANNEL_IMPL_IID)
154 #ifdef PR_LOGGING
155 PRLogModuleInfo* nsCachedChromeChannel::gLog;
156 #endif
158 NS_IMPL_ISUPPORTS3(nsCachedChromeChannel, nsIChannel, nsIRequest,
159 nsCachedChromeChannel)
161 nsCachedChromeChannel::nsCachedChromeChannel(nsIURI* aURI)
162 : mURI(aURI)
163 , mOriginalURI(aURI)
164 , mLoadFlags(nsIRequest::LOAD_NORMAL)
165 , mStatus(NS_OK)
167 #ifdef PR_LOGGING
168 if (! gLog)
169 gLog = PR_NewLogModule("nsCachedChromeChannel");
170 #endif
172 LOG(("nsCachedChromeChannel[%p]: created", this));
176 nsCachedChromeChannel::~nsCachedChromeChannel()
178 LOG(("nsCachedChromeChannel[%p]: destroyed", this));
182 NS_IMETHODIMP
183 nsCachedChromeChannel::GetOriginalURI(nsIURI* *aOriginalURI)
185 *aOriginalURI = mOriginalURI;
186 NS_ADDREF(*aOriginalURI);
187 return NS_OK;
190 NS_IMETHODIMP
191 nsCachedChromeChannel::SetOriginalURI(nsIURI* aOriginalURI)
193 NS_ENSURE_ARG_POINTER(aOriginalURI);
194 mOriginalURI = aOriginalURI;
195 return NS_OK;
198 NS_IMETHODIMP
199 nsCachedChromeChannel::GetURI(nsIURI* *aURI)
201 *aURI = mURI;
202 NS_ADDREF(*aURI);
203 return NS_OK;
206 NS_IMETHODIMP
207 nsCachedChromeChannel::Open(nsIInputStream **_retval)
209 // NS_NOTREACHED("don't do that");
210 *_retval = nsnull;
211 return NS_ERROR_FAILURE;
214 NS_IMETHODIMP
215 nsCachedChromeChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
217 NS_ENSURE_ARG_POINTER(listener);
219 nsresult rv;
221 // Fire OnStartRequest and OnStopRequest, which will cause the XUL
222 // document to get embedded.
223 LOG(("nsCachedChromeChannel[%p]: posting load event for %p",
224 this, listener));
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);
233 if (NS_FAILED(rv))
234 return rv;
236 mContext = ctxt;
237 mListener = listener;
239 if (mLoadGroup) {
240 LOG(("nsCachedChromeChannel[%p]: adding self to load group %p",
241 this, mLoadGroup.get()));
243 (void) mLoadGroup->AddRequest(this, nsnull);
245 return NS_OK;
248 NS_IMETHODIMP
249 nsCachedChromeChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
251 return NS_ERROR_NOT_IMPLEMENTED;
254 NS_IMETHODIMP
255 nsCachedChromeChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
257 *aLoadFlags = mLoadFlags;
258 return NS_OK;
261 NS_IMETHODIMP
262 nsCachedChromeChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
264 mLoadFlags = aLoadFlags;
265 return NS_OK;
268 NS_IMETHODIMP
269 nsCachedChromeChannel::GetOwner(nsISupports * *aOwner)
271 *aOwner = mOwner;
272 NS_IF_ADDREF(*aOwner);
273 return NS_OK;
276 NS_IMETHODIMP
277 nsCachedChromeChannel::SetOwner(nsISupports * aOwner)
279 mOwner = aOwner;
280 return NS_OK;
283 NS_IMETHODIMP
284 nsCachedChromeChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
286 *aLoadGroup = mLoadGroup;
287 NS_IF_ADDREF(*aLoadGroup);
288 return NS_OK;
291 NS_IMETHODIMP
292 nsCachedChromeChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
294 mLoadGroup = aLoadGroup;
295 return NS_OK;
298 NS_IMETHODIMP
299 nsCachedChromeChannel::GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks)
301 *aNotificationCallbacks = nsnull;
302 return NS_ERROR_FAILURE;
305 NS_IMETHODIMP
306 nsCachedChromeChannel::SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks)
308 return NS_OK; // ignored
311 NS_IMETHODIMP
312 nsCachedChromeChannel::GetContentType(nsACString &aContentType)
314 aContentType.AssignLiteral("mozilla.application/cached-xul");
315 return NS_OK;
318 NS_IMETHODIMP
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;
326 NS_IMETHODIMP
327 nsCachedChromeChannel::GetContentCharset(nsACString &aContentCharset)
329 aContentCharset.Truncate();
330 return NS_OK;
333 NS_IMETHODIMP
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;
341 NS_IMETHODIMP
342 nsCachedChromeChannel::GetContentLength(PRInt32 *aContentLength)
344 NS_NOTREACHED("don't do that");
345 *aContentLength = 0;
346 return NS_ERROR_FAILURE;
349 NS_IMETHODIMP
350 nsCachedChromeChannel::SetContentLength(PRInt32 aContentLength)
352 NS_NOTREACHED("nsCachedChromeChannel::SetContentLength");
353 return NS_ERROR_NOT_IMPLEMENTED;
356 void
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))
365 return;
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);
377 if (mLoadGroup) {
378 LOG(("nsCachedChromeChannel[%p]: removing self from load group %p",
379 this, mLoadGroup.get()));
380 mLoadGroup->RemoveRequest(this, nsnull, mStatus);
383 mListener = nsnull;
384 mContext = nsnull;
387 ////////////////////////////////////////////////////////////////////////////////
389 NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler,
390 nsIProtocolHandler,
391 nsISupportsWeakReference)
393 ////////////////////////////////////////////////////////////////////////////////
394 // nsIProtocolHandler methods:
396 NS_IMETHODIMP
397 nsChromeProtocolHandler::GetScheme(nsACString &result)
399 result.AssignLiteral("chrome");
400 return NS_OK;
403 NS_IMETHODIMP
404 nsChromeProtocolHandler::GetDefaultPort(PRInt32 *result)
406 *result = -1; // no port for chrome: URLs
407 return NS_OK;
410 NS_IMETHODIMP
411 nsChromeProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
413 // don't override anything.
414 *_retval = PR_FALSE;
415 return NS_OK;
418 NS_IMETHODIMP
419 nsChromeProtocolHandler::GetProtocolFlags(PRUint32 *result)
421 *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
422 return NS_OK;
425 NS_IMETHODIMP
426 nsChromeProtocolHandler::NewURI(const nsACString &aSpec,
427 const char *aCharset,
428 nsIURI *aBaseURI,
429 nsIURI **result)
431 nsresult rv;
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);
440 if (NS_FAILED(rv))
441 return rv;
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);
451 if (NS_FAILED(rv))
452 return rv;
454 surl->SetMutable(PR_FALSE);
456 NS_ADDREF(*result = url);
457 return NS_OK;
460 NS_IMETHODIMP
461 nsChromeProtocolHandler::NewChannel(nsIURI* aURI,
462 nsIChannel* *aResult)
464 nsresult rv;
466 NS_ENSURE_ARG_POINTER(aURI);
467 NS_PRECONDITION(aResult, "Null out param");
469 #ifdef DEBUG
470 // Check that the uri we got is already canonified
471 nsresult debug_rv;
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)) {
478 PRBool same;
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!");
485 #endif
487 nsCOMPtr<nsIChannel> result;
489 #ifdef MOZ_XUL
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;
496 if (cache)
497 isCached = cache->IsCached(aURI);
498 else
499 NS_WARNING("Unable to obtain the XUL prototype cache!");
501 // Same comment as nsXULDocument::StartDocumentLoad and
502 // nsXULDocument::ResumeWalk
503 // - Ben Goodger
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,
511 // parse from disk
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
515 // parsed from disk.
517 if (isCached) {
518 // ...in which case, we'll create a dummy stream that'll just
519 // load the thing.
520 result = new nsCachedChromeChannel(aURI);
521 if (! result)
522 return NS_ERROR_OUT_OF_MEMORY;
524 else {
525 #endif // MOZ_XUL
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));
542 if (NS_FAILED(rv)) {
543 #ifdef DEBUG
544 nsCAutoString spec;
545 aURI->GetSpec(spec);
546 printf("Couldn't convert chrome URL: %s\n", spec.get());
547 #endif
548 return rv;
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);
565 nsCAutoString path;
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);
581 #ifdef MOZ_XUL
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
590 // nsResURL class?
591 nsCOMPtr<nsIFastLoadService> fastLoadServ(do_GetFastLoadService());
592 if (fastLoadServ) {
593 nsCOMPtr<nsIObjectOutputStream> objectOutput;
594 fastLoadServ->GetOutputStream(getter_AddRefs(objectOutput));
595 if (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));
605 if (fileURL)
606 fileURL->GetFile(getter_AddRefs(file));
608 if (file) {
609 rv = fastLoadServ->AddDependency(file);
610 if (NS_FAILED(rv))
611 cache->AbortFastLoads();
616 #endif
618 *aResult = result;
619 NS_ADDREF(*aResult);
620 return NS_OK;
623 ////////////////////////////////////////////////////////////////////////////////