Remove UTF8 BOM marker from last commit.
[wine-gecko.git] / chrome / src / nsChromeProtocolHandler.cpp
blobf17958cbd04c606c17d659a909e50da9a11c0075
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 , mLoadFlags(nsIRequest::LOAD_NORMAL)
164 , mStatus(NS_OK)
166 #ifdef PR_LOGGING
167 if (! gLog)
168 gLog = PR_NewLogModule("nsCachedChromeChannel");
169 #endif
171 LOG(("nsCachedChromeChannel[%p]: created", this));
175 nsCachedChromeChannel::~nsCachedChromeChannel()
177 LOG(("nsCachedChromeChannel[%p]: destroyed", this));
181 NS_IMETHODIMP
182 nsCachedChromeChannel::GetOriginalURI(nsIURI* *aOriginalURI)
184 *aOriginalURI = mOriginalURI ? mOriginalURI : mURI;
185 NS_ADDREF(*aOriginalURI);
186 return NS_OK;
189 NS_IMETHODIMP
190 nsCachedChromeChannel::SetOriginalURI(nsIURI* aOriginalURI)
192 mOriginalURI = aOriginalURI;
193 return NS_OK;
196 NS_IMETHODIMP
197 nsCachedChromeChannel::GetURI(nsIURI* *aURI)
199 *aURI = mURI;
200 NS_ADDREF(*aURI);
201 return NS_OK;
204 NS_IMETHODIMP
205 nsCachedChromeChannel::Open(nsIInputStream **_retval)
207 // NS_NOTREACHED("don't do that");
208 *_retval = nsnull;
209 return NS_ERROR_FAILURE;
212 NS_IMETHODIMP
213 nsCachedChromeChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
215 NS_ENSURE_ARG_POINTER(listener);
217 nsresult rv;
219 // Fire OnStartRequest and OnStopRequest, which will cause the XUL
220 // document to get embedded.
221 LOG(("nsCachedChromeChannel[%p]: posting load event for %p",
222 this, listener));
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);
231 if (NS_FAILED(rv))
232 return rv;
234 mContext = ctxt;
235 mListener = listener;
237 if (mLoadGroup) {
238 LOG(("nsCachedChromeChannel[%p]: adding self to load group %p",
239 this, mLoadGroup.get()));
241 (void) mLoadGroup->AddRequest(this, nsnull);
243 return NS_OK;
246 NS_IMETHODIMP
247 nsCachedChromeChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
249 return NS_ERROR_NOT_IMPLEMENTED;
252 NS_IMETHODIMP
253 nsCachedChromeChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
255 *aLoadFlags = mLoadFlags;
256 return NS_OK;
259 NS_IMETHODIMP
260 nsCachedChromeChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
262 mLoadFlags = aLoadFlags;
263 return NS_OK;
266 NS_IMETHODIMP
267 nsCachedChromeChannel::GetOwner(nsISupports * *aOwner)
269 *aOwner = mOwner;
270 NS_IF_ADDREF(*aOwner);
271 return NS_OK;
274 NS_IMETHODIMP
275 nsCachedChromeChannel::SetOwner(nsISupports * aOwner)
277 mOwner = aOwner;
278 return NS_OK;
281 NS_IMETHODIMP
282 nsCachedChromeChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
284 *aLoadGroup = mLoadGroup;
285 NS_IF_ADDREF(*aLoadGroup);
286 return NS_OK;
289 NS_IMETHODIMP
290 nsCachedChromeChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
292 mLoadGroup = aLoadGroup;
293 return NS_OK;
296 NS_IMETHODIMP
297 nsCachedChromeChannel::GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks)
299 *aNotificationCallbacks = nsnull;
300 return NS_ERROR_FAILURE;
303 NS_IMETHODIMP
304 nsCachedChromeChannel::SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks)
306 return NS_OK; // ignored
309 NS_IMETHODIMP
310 nsCachedChromeChannel::GetContentType(nsACString &aContentType)
312 aContentType.AssignLiteral("mozilla.application/cached-xul");
313 return NS_OK;
316 NS_IMETHODIMP
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;
324 NS_IMETHODIMP
325 nsCachedChromeChannel::GetContentCharset(nsACString &aContentCharset)
327 aContentCharset.Truncate();
328 return NS_OK;
331 NS_IMETHODIMP
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;
339 NS_IMETHODIMP
340 nsCachedChromeChannel::GetContentLength(PRInt32 *aContentLength)
342 NS_NOTREACHED("don't do that");
343 *aContentLength = 0;
344 return NS_ERROR_FAILURE;
347 NS_IMETHODIMP
348 nsCachedChromeChannel::SetContentLength(PRInt32 aContentLength)
350 NS_NOTREACHED("nsCachedChromeChannel::SetContentLength");
351 return NS_ERROR_NOT_IMPLEMENTED;
354 void
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))
363 return;
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);
375 if (mLoadGroup) {
376 LOG(("nsCachedChromeChannel[%p]: removing self from load group %p",
377 this, mLoadGroup.get()));
378 mLoadGroup->RemoveRequest(this, nsnull, mStatus);
381 mListener = nsnull;
382 mContext = nsnull;
385 ////////////////////////////////////////////////////////////////////////////////
387 NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler,
388 nsIProtocolHandler,
389 nsISupportsWeakReference)
391 ////////////////////////////////////////////////////////////////////////////////
392 // nsIProtocolHandler methods:
394 NS_IMETHODIMP
395 nsChromeProtocolHandler::GetScheme(nsACString &result)
397 result.AssignLiteral("chrome");
398 return NS_OK;
401 NS_IMETHODIMP
402 nsChromeProtocolHandler::GetDefaultPort(PRInt32 *result)
404 *result = -1; // no port for chrome: URLs
405 return NS_OK;
408 NS_IMETHODIMP
409 nsChromeProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
411 // don't override anything.
412 *_retval = PR_FALSE;
413 return NS_OK;
416 NS_IMETHODIMP
417 nsChromeProtocolHandler::GetProtocolFlags(PRUint32 *result)
419 *result = URI_STD | URI_IS_UI_RESOURCE;
420 return NS_OK;
423 NS_IMETHODIMP
424 nsChromeProtocolHandler::NewURI(const nsACString &aSpec,
425 const char *aCharset,
426 nsIURI *aBaseURI,
427 nsIURI **result)
429 nsresult rv;
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);
438 if (NS_FAILED(rv))
439 return rv;
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);
449 if (NS_FAILED(rv))
450 return rv;
452 surl->SetMutable(PR_FALSE);
454 NS_ADDREF(*result = url);
455 return NS_OK;
458 NS_IMETHODIMP
459 nsChromeProtocolHandler::NewChannel(nsIURI* aURI,
460 nsIChannel* *aResult)
462 nsresult rv;
464 NS_ENSURE_ARG_POINTER(aURI);
465 NS_PRECONDITION(aResult, "Null out param");
467 #ifdef DEBUG
468 // Check that the uri we got is already canonified
469 nsresult debug_rv;
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)) {
476 PRBool same;
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!");
483 #endif
485 nsCOMPtr<nsIChannel> result;
487 #ifdef MOZ_XUL
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;
494 if (cache)
495 isCached = cache->IsCached(aURI);
496 else
497 NS_WARNING("Unable to obtain the XUL prototype cache!");
499 // Same comment as nsXULDocument::StartDocumentLoad and
500 // nsXULDocument::ResumeWalk
501 // - Ben Goodger
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,
509 // parse from disk
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
513 // parsed from disk.
515 if (isCached) {
516 // ...in which case, we'll create a dummy stream that'll just
517 // load the thing.
518 result = new nsCachedChromeChannel(aURI);
519 if (! result)
520 return NS_ERROR_OUT_OF_MEMORY;
522 else {
523 #endif // MOZ_XUL
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));
540 if (NS_FAILED(rv)) {
541 #ifdef DEBUG
542 nsCAutoString spec;
543 aURI->GetSpec(spec);
544 printf("Couldn't convert chrome URL: %s\n", spec.get());
545 #endif
546 return rv;
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));
558 if (fileChan) {
559 #ifdef DEBUG
560 nsCOMPtr<nsIFile> file;
561 fileChan->GetFile(getter_AddRefs(file));
563 PRBool exists = PR_FALSE;
564 file->Exists(&exists);
565 if (!exists) {
566 nsCAutoString path;
567 file->GetNativePath(path);
568 printf("Chrome file doesn't exist: %s\n", path.get());
570 #endif
572 else {
573 nsCOMPtr<nsIJARChannel> jarChan
574 (do_QueryInterface(result));
575 if (!jarChan) {
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");
581 result = nsnull;
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);
595 nsCAutoString path;
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);
611 #ifdef MOZ_XUL
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
620 // nsResURL class?
621 nsCOMPtr<nsIFastLoadService> fastLoadServ(do_GetFastLoadService());
622 if (fastLoadServ) {
623 nsCOMPtr<nsIObjectOutputStream> objectOutput;
624 fastLoadServ->GetOutputStream(getter_AddRefs(objectOutput));
625 if (objectOutput) {
626 nsCOMPtr<nsIFile> file;
628 if (fileChan) {
629 fileChan->GetFile(getter_AddRefs(file));
630 } else {
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));
643 if (fileURL)
644 fileURL->GetFile(getter_AddRefs(file));
647 if (file) {
648 rv = fastLoadServ->AddDependency(file);
649 if (NS_FAILED(rv))
650 cache->AbortFastLoads();
655 #endif
657 *aResult = result;
658 NS_ADDREF(*aResult);
659 return NS_OK;
662 ////////////////////////////////////////////////////////////////////////////////