Back to index

lightning-sunbird  0.9+nobinonly
GeckoProtocolHandler.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: Mozilla-sample-code 1.0
00003  *
00004  * Copyright (c) 2002 Netscape Communications Corporation and
00005  * other contributors
00006  *
00007  * Permission is hereby granted, free of charge, to any person obtaining a
00008  * copy of this Mozilla sample software and associated documentation files
00009  * (the "Software"), to deal in the Software without restriction, including
00010  * without limitation the rights to use, copy, modify, merge, publish,
00011  * distribute, sublicense, and/or sell copies of the Software, and to permit
00012  * persons to whom the Software is furnished to do so, subject to the
00013  * following conditions:
00014  *
00015  * The above copyright notice and this permission notice shall be included
00016  * in all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00019  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00021  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00023  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00024  * DEALINGS IN THE SOFTWARE.
00025  *
00026  * Contributor(s):
00027  *   Adam Lock <adamlock@netscape.com>
00028  *
00029  * ***** END LICENSE BLOCK ***** */
00030 
00031 #include "GeckoProtocolHandler.h"
00032 
00033 #include "nsNetCID.h"
00034 #include "nsNetUtil.h"
00035 #include "nsIGenericFactory.h"
00036 #include "nsIComponentManager.h"
00037 #include "nsIComponentRegistrar.h"
00038 #include "nsIProgressEventSink.h"
00039 #include "nsILoadGroup.h"
00040 #include "nsIInterfaceRequestor.h"
00041 #include "nsIInterfaceRequestorUtils.h"
00042 #include "nsIByteArrayInputStream.h"
00043 #include "nsIStreamListener.h"
00044 #include "nsIInputStreamPump.h"
00045 #include "nsEmbedString.h"
00046 
00047 // Everytime register handler is called, it picks the next available CID in the
00048 // list.
00049 // TODO - is there a cross-platform way to generate UUIDs and obviate this?
00050 static const nsCID kProtocolCIDs[] = 
00051 {
00052     { 0xfc8b2366, 0x0d07, 0x45ef, { 0x9f, 0xab, 0x22, 0x31, 0x9d, 0xbc, 0xfa, 0x77 } },
00053     { 0x6b5db250, 0xcf4b, 0x4ab1, { 0xb3, 0xaa, 0x1a, 0x9a, 0xd6, 0xdf, 0x7f, 0x95 } },
00054     { 0x677c6eaf, 0x3c3d, 0x4e0d, { 0xad, 0x30, 0x5a, 0xb8, 0x69, 0x1d, 0x1f, 0xfc } },
00055     { 0xbe383b01, 0x58d3, 0x4e65, { 0x9d, 0x50, 0x05, 0xb4, 0xc3, 0x92, 0x43, 0x2e } },
00056     { 0x81290231, 0xedf0, 0x4876, { 0x94, 0xa2, 0xdb, 0x96, 0xca, 0xa3, 0xc1, 0xfc } },
00057     { 0xf9c466b0, 0x0da8, 0x48a7, { 0xbb, 0xe4, 0x2f, 0x63, 0xb0, 0x71, 0x41, 0x6f } },
00058     { 0x9cbaef5e, 0xdf94, 0x4cb0, { 0xb4, 0xc3, 0x89, 0x66, 0x89, 0xd0, 0x2d, 0x56 } },
00059     { 0xce79440d, 0xdafc, 0x4908, { 0xb8, 0x94, 0xb2, 0x74, 0xa3, 0x51, 0x2f, 0x45 } }
00060 };
00061 static const int kProtocolCIDsSize = sizeof(kProtocolCIDs) / sizeof(kProtocolCIDs[0]);
00062 static PRUint32 gUsedCIDs = 0;
00063 struct GeckoChannelCallbacks
00064 {
00065     nsCString mScheme;
00066     GeckoChannelCallback *mCallback;
00067     // SUCKS, component registry should properly copy this variable or take ownership of
00068     // it so it doesn't have to be made a static or global like this.
00069     // I also wonder if having component info memory dotted all over the place doesn't
00070     // impact component registry performance in some way.
00071     nsModuleComponentInfo mComponentInfo;
00072 };
00073 static GeckoChannelCallbacks gCallbacks[kProtocolCIDsSize];
00074 
00075 class GeckoProtocolHandlerImpl :
00076     public nsIProtocolHandler
00077 {
00078 public:
00079     NS_DECL_ISUPPORTS
00080     NS_DECL_NSIPROTOCOLHANDLER
00081     static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
00082 };
00083 
00084 
00085 class GeckoProtocolChannel :
00086     public nsIChannel,
00087     public nsIStreamListener
00088 {
00089 public:
00090     NS_DECL_ISUPPORTS
00091     NS_DECL_NSIREQUEST
00092     NS_DECL_NSICHANNEL
00093     NS_DECL_NSIREQUESTOBSERVER
00094     NS_DECL_NSISTREAMLISTENER
00095 
00096     GeckoProtocolChannel();
00097     nsresult Init(nsIURI *aURI);
00098 
00099 protected:
00100     nsCOMPtr<nsIURI>                    mURI;
00101     nsCOMPtr<nsIURI>                    mOriginalURI;
00102     nsCOMPtr<nsIInterfaceRequestor>     mCallbacks;
00103     nsCOMPtr<nsIProgressEventSink>      mProgressSink;
00104     nsCOMPtr<nsISupports>               mOwner;
00105     nsCOMPtr<nsILoadGroup>              mLoadGroup;
00106     nsCOMPtr<nsIStreamListener>         mListener;
00107     nsCOMPtr<nsISupports>               mListenerContext;
00108     nsCOMPtr<nsIInputStream>            mContentStream;
00109     nsCString                           mContentType;
00110     nsCString                           mContentCharset;
00111     PRUint32                            mLoadFlags;
00112     nsresult                            mStatus;
00113     PRUint32                            mContentLength;
00114     void                              * mData;
00115     nsCOMPtr<nsIInputStreamPump>        mPump;
00116 
00117     virtual ~GeckoProtocolChannel();
00118 };
00119 
00120 nsresult GeckoProtocolHandler::RegisterHandler(const char *aScheme, const char *aDescription, GeckoChannelCallback *aCallback)
00121 {
00122     if (!aScheme || !aCallback)
00123     {
00124         return NS_ERROR_INVALID_ARG;
00125     }
00126 
00127     if (gUsedCIDs >= kProtocolCIDsSize)
00128     {
00129         // We've run out of CIDs. Perhaps this code should be generating them
00130         // on the fly somehow instead?
00131         return NS_ERROR_FAILURE;
00132     }
00133     for (PRUint32 i = 0; i < gUsedCIDs; i++)
00134     {
00135         if (gCallbacks[i].mScheme.EqualsIgnoreCase(aScheme))
00136             return NS_ERROR_FAILURE;
00137     }
00138 
00139     nsCAutoString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
00140     contractID.Append(aScheme);
00141     nsCID cid = kProtocolCIDs[gUsedCIDs];
00142     gCallbacks[gUsedCIDs].mScheme = aScheme;
00143     gCallbacks[gUsedCIDs].mCallback = aCallback;
00144     gUsedCIDs++;
00145 
00146     nsModuleComponentInfo &ci = gCallbacks[gUsedCIDs].mComponentInfo;
00147     memset(&ci, 0, sizeof(ci));
00148     ci.mDescription = strdup(aDescription);
00149     ci.mCID = cid;
00150     ci.mContractID = strdup(contractID.get());
00151     ci.mConstructor = GeckoProtocolHandlerImpl::Create;
00152 
00153     // Create a factory object which will create the protocol handler on demand
00154     nsCOMPtr<nsIGenericFactory> factory;
00155     NS_NewGenericFactory(getter_AddRefs(factory), &ci);
00156 
00157     nsCOMPtr<nsIComponentRegistrar> registrar;
00158     NS_GetComponentRegistrar(getter_AddRefs(registrar));
00159     if (registrar)
00160         registrar->RegisterFactory(cid, aDescription, contractID.get(), factory);
00161 
00162     return NS_OK;
00163 }
00164 
00165 
00168 
00169 
00170 GeckoProtocolChannel::GeckoProtocolChannel() :
00171     mContentLength(0),
00172     mData(nsnull),
00173     mStatus(NS_OK),
00174     mLoadFlags(LOAD_NORMAL)
00175 {
00176 }
00177 
00178 GeckoProtocolChannel::~GeckoProtocolChannel()
00179 {
00180 //    if (mData)
00181 //        nsMemory::Free(mData);
00182 }
00183 
00184 static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
00185 
00186 NS_METHOD GeckoProtocolHandlerImpl::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00187 {
00188     GeckoProtocolHandlerImpl *impl = new GeckoProtocolHandlerImpl();
00189     if (!impl)
00190     {
00191         return NS_ERROR_OUT_OF_MEMORY;
00192     }
00193     *aResult = nsnull;
00194     nsresult rv = impl->QueryInterface(aIID, aResult);
00195     if (NS_FAILED(rv))
00196     {
00197         impl->Release();
00198     }
00199     return rv;
00200 }
00201 
00202 NS_IMPL_ISUPPORTS1(GeckoProtocolHandlerImpl, nsIProtocolHandler)
00203 
00204 /* readonly attribute ACString scheme; */
00205 NS_IMETHODIMP GeckoProtocolHandlerImpl::GetScheme(nsACString & aScheme)
00206 {
00207     // Since we have no clue what scheme we're an implementation of,
00208     // just return the first one that was registered.
00209     aScheme = gCallbacks[0].mScheme;
00210     return NS_OK;
00211 }
00212 
00213 /* readonly attribute long defaultPort; */
00214 NS_IMETHODIMP GeckoProtocolHandlerImpl::GetDefaultPort(PRInt32 *aDefaultPort)
00215 {
00216     *aDefaultPort = -1;
00217     return NS_OK;
00218 }
00219 
00220 /* readonly attribute unsigned long protocolFlags; */
00221 NS_IMETHODIMP GeckoProtocolHandlerImpl::GetProtocolFlags(PRUint32 *aProtocolFlags)
00222 {
00223     *aProtocolFlags = URI_NORELATIVE | URI_NOAUTH;
00224     return NS_OK;
00225 }
00226 
00227 /* nsIURI newURI (in AUTF8String aSpec, in string aOriginCharset, in nsIURI aBaseURI); */
00228 NS_IMETHODIMP GeckoProtocolHandlerImpl::NewURI(const nsACString & aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval)
00229 {
00230     nsresult rv;
00231     nsIURI* url;
00232     rv = CallCreateInstance(kSimpleURICID, &url);
00233     if (NS_FAILED(rv))
00234         return rv;
00235     rv = url->SetSpec(aSpec);
00236     if (NS_FAILED(rv))
00237     {
00238         NS_RELEASE(url);
00239         return rv;
00240     }
00241     *_retval = url;
00242     return rv;
00243 }
00244 
00245 /* nsIChannel newChannel (in nsIURI aURI); */
00246 NS_IMETHODIMP GeckoProtocolHandlerImpl::NewChannel(nsIURI *aURI, nsIChannel **_retval)
00247 {
00248     NS_ENSURE_ARG_POINTER(aURI);
00249     GeckoProtocolChannel *channel = new GeckoProtocolChannel;
00250     if (!channel)
00251     {
00252         return NS_ERROR_OUT_OF_MEMORY;
00253     }
00254     channel->Init(aURI);
00255     channel->QueryInterface(NS_GET_IID(nsIChannel), (void **) _retval);
00256     return NS_OK;
00257 }
00258 
00259 /* boolean allowPort (in long port, in string scheme); */
00260 NS_IMETHODIMP GeckoProtocolHandlerImpl::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
00261 {
00262     return NS_ERROR_NOT_IMPLEMENTED;
00263 }
00264 
00265 
00268 
00269 NS_IMPL_ISUPPORTS4(GeckoProtocolChannel, nsIRequest, nsIChannel, nsIRequestObserver, nsIStreamListener)
00270 
00271 nsresult GeckoProtocolChannel::Init(nsIURI *aURI)
00272 {
00273     mURI = aURI;
00274     return NS_OK;
00275 }
00276 
00277 
00279 // nsIRequest methods
00280 
00281 
00282 NS_IMETHODIMP
00283 GeckoProtocolChannel::GetName(nsACString &result)
00284 {
00285     return mURI->GetSpec(result);
00286 }
00287 
00288 NS_IMETHODIMP
00289 GeckoProtocolChannel::IsPending(PRBool *result)
00290 {
00291     *result = PR_FALSE;
00292     return NS_OK;
00293 }
00294 
00295 NS_IMETHODIMP
00296 GeckoProtocolChannel::GetStatus(nsresult *status)
00297 {
00298     *status = mStatus;
00299     return NS_OK;
00300 }
00301 
00302 NS_IMETHODIMP
00303 GeckoProtocolChannel::Cancel(nsresult status)
00304 {
00305     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00306 
00307     mStatus = status;
00308     return NS_ERROR_UNEXPECTED;
00309 }
00310 
00311 NS_IMETHODIMP
00312 GeckoProtocolChannel::Suspend()
00313 {
00314     return NS_ERROR_UNEXPECTED;
00315 }
00316 
00317 NS_IMETHODIMP
00318 GeckoProtocolChannel::Resume()
00319 {
00320     return NS_ERROR_UNEXPECTED;
00321 }
00322 
00323 
00325 // nsIChannel methods:
00326 
00327 NS_IMETHODIMP
00328 GeckoProtocolChannel::GetOriginalURI(nsIURI* *aURI)
00329 {
00330     *aURI = mOriginalURI ? mOriginalURI : mURI;
00331     NS_ADDREF(*aURI);
00332     return NS_OK;
00333 }
00334 
00335 NS_IMETHODIMP
00336 GeckoProtocolChannel::SetOriginalURI(nsIURI* aURI)
00337 {
00338     mOriginalURI = aURI;
00339     return NS_OK;
00340 }
00341 
00342 NS_IMETHODIMP
00343 GeckoProtocolChannel::GetURI(nsIURI* *aURI)
00344 {
00345     *aURI = mURI;
00346     NS_IF_ADDREF(*aURI);
00347     return NS_OK;
00348 }
00349 
00350 NS_IMETHODIMP
00351 GeckoProtocolChannel::Open(nsIInputStream **_retval)
00352 {
00353     NS_NOTREACHED("GeckoProtocolChannel::Open");
00354     return NS_ERROR_NOT_IMPLEMENTED;
00355 }
00356 
00357 NS_IMETHODIMP
00358 GeckoProtocolChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
00359 {
00360     nsresult rv = NS_OK;
00361 
00362     nsCAutoString scheme;
00363     mURI->GetScheme(scheme);
00364     for (PRUint32 i = 0; i < gUsedCIDs; i++)
00365     {
00366         if (stricmp(scheme.get(), gCallbacks[i].mScheme.get()) == 0)
00367         {
00368             rv = gCallbacks[i].mCallback->GetData(
00369                 mURI, NS_STATIC_CAST(nsIChannel *,this), mContentType, &mData, &mContentLength);
00370             if (NS_FAILED(rv)) return rv;
00371             
00372             nsCOMPtr<nsIByteArrayInputStream> stream;
00373             rv = NS_NewByteArrayInputStream(getter_AddRefs(stream), (char *) mData, mContentLength);
00374             if (NS_FAILED(rv)) return rv;
00375             mContentStream = do_QueryInterface(stream);
00376 
00377             mListenerContext = aContext;
00378             mListener = aListener;
00379 
00380             // XXX should this use 64-bit content lengths?
00381             nsresult rv = NS_NewInputStreamPump(
00382                 getter_AddRefs(mPump), mContentStream, nsInt64(-1),
00383                 nsInt64(mContentLength), 0, 0, PR_TRUE);
00384             if (NS_FAILED(rv)) return rv;
00385 
00386             if (mLoadGroup)
00387             {
00388                 mLoadGroup->AddRequest(this, nsnull);
00389             }
00390 
00391             rv = mPump->AsyncRead(this, nsnull);
00392             if (NS_FAILED(rv)) return rv;
00393 
00394             return rv;
00395         }
00396     }
00397 
00398     return NS_ERROR_FAILURE;
00399 }
00400 
00401 NS_IMETHODIMP
00402 GeckoProtocolChannel::GetLoadFlags(PRUint32 *aLoadFlags)
00403 {
00404     *aLoadFlags = mLoadFlags;
00405     return NS_OK;
00406 }
00407 
00408 NS_IMETHODIMP
00409 GeckoProtocolChannel::SetLoadFlags(PRUint32 aLoadFlags)
00410 {
00411     mLoadFlags = aLoadFlags;
00412     return NS_OK;
00413 }
00414 
00415 NS_IMETHODIMP
00416 GeckoProtocolChannel::GetContentType(nsACString &aContentType)
00417 {
00418     aContentType = mContentType;
00419     return NS_OK;
00420 }
00421 
00422 NS_IMETHODIMP
00423 GeckoProtocolChannel::SetContentType(const nsACString &aContentType)
00424 {
00425     mContentType = aContentType;
00426     return NS_OK;
00427 }
00428 
00429 NS_IMETHODIMP
00430 GeckoProtocolChannel::GetContentCharset(nsACString &aContentCharset)
00431 {
00432     aContentCharset = mContentCharset;
00433     return NS_OK;
00434 }
00435 
00436 NS_IMETHODIMP
00437 GeckoProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
00438 {
00439     mContentCharset = aContentCharset;
00440     return NS_OK;
00441 }
00442 
00443 NS_IMETHODIMP
00444 GeckoProtocolChannel::GetContentLength(PRInt32 *aContentLength)
00445 {
00446     *aContentLength = mContentLength;
00447     return NS_OK;
00448 }
00449 
00450 NS_IMETHODIMP
00451 GeckoProtocolChannel::SetContentLength(PRInt32 aContentLength)
00452 {
00453     // silently ignore this...
00454     return NS_OK;
00455 }
00456 
00457 NS_IMETHODIMP
00458 GeckoProtocolChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00459 {
00460     *aLoadGroup = mLoadGroup;
00461     NS_IF_ADDREF(*aLoadGroup);
00462     return NS_OK;
00463 }
00464 
00465 NS_IMETHODIMP
00466 GeckoProtocolChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00467 {
00468     mLoadGroup = aLoadGroup;
00469     return NS_OK;
00470 }
00471 
00472 NS_IMETHODIMP
00473 GeckoProtocolChannel::GetOwner(nsISupports* *aOwner)
00474 {
00475     *aOwner = mOwner.get();
00476     NS_IF_ADDREF(*aOwner);
00477     return NS_OK;
00478 }
00479 
00480 NS_IMETHODIMP
00481 GeckoProtocolChannel::SetOwner(nsISupports* aOwner)
00482 {
00483     mOwner = aOwner;
00484     return NS_OK;
00485 }
00486 
00487 NS_IMETHODIMP
00488 GeckoProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00489 {
00490     *aNotificationCallbacks = mCallbacks.get();
00491     NS_IF_ADDREF(*aNotificationCallbacks);
00492     return NS_OK;
00493 }
00494 
00495 NS_IMETHODIMP
00496 GeckoProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00497 {
00498     mCallbacks = aNotificationCallbacks;
00499     mProgressSink = do_GetInterface(mCallbacks);
00500     return NS_OK;
00501 }
00502 
00503 NS_IMETHODIMP 
00504 GeckoProtocolChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
00505 {
00506     *aSecurityInfo = nsnull;
00507     return NS_OK;
00508 }
00509 
00510 
00512 // nsIStreamListener methods
00513 
00514 
00515 NS_IMETHODIMP
00516 GeckoProtocolChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
00517 {
00518     return mListener->OnStartRequest(this, mListenerContext);
00519 }
00520 
00521 NS_IMETHODIMP
00522 GeckoProtocolChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
00523 {
00524     if (NS_SUCCEEDED(mStatus))
00525         mStatus = status;
00526 
00527     mListener->OnStopRequest(this, mListenerContext, mStatus);
00528     mListener = 0;
00529     mListenerContext = 0;
00530 
00531     if (mLoadGroup)
00532         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00533 
00534     mPump = 0;
00535     mContentStream = 0;
00536 
00537     // Drop notification callbacks to prevent cycles.
00538     mCallbacks = 0;
00539     mProgressSink = 0;
00540 
00541     return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP
00545 GeckoProtocolChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
00546                                       nsIInputStream *stream,
00547                                       PRUint32 offset, PRUint32 count)
00548 {
00549     nsresult rv;
00550 
00551     rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
00552 
00553     // XXX can this use real 64-bit ints?
00554     if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
00555         mProgressSink->OnProgress(this, nsnull, nsUint64(offset + count),
00556                                   nsUint64(mContentLength));
00557 
00558     return rv; // let the pump cancel on failure
00559 }