1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: Mozilla-sample-code 1.0
4
* Copyright (c) 2002 Netscape Communications Corporation and
7
* Permission is hereby granted, free of charge, to any person obtaining a
8
* copy of this Mozilla sample software and associated documentation files
9
* (the "Software"), to deal in the Software without restriction, including
10
* without limitation the rights to use, copy, modify, merge, publish,
11
* distribute, sublicense, and/or sell copies of the Software, and to permit
12
* persons to whom the Software is furnished to do so, subject to the
13
* following conditions:
15
* The above copyright notice and this permission notice shall be included
16
* in all copies or substantial portions of the Software.
18
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24
* DEALINGS IN THE SOFTWARE.
28
* Adam Lock <adamlock@netscape.com>
30
* ***** END LICENSE BLOCK ***** */
32
#include "GeckoProtocolHandler.h"
35
#include "nsNetUtil.h"
36
#include "nsIGenericFactory.h"
37
#include "nsIComponentManager.h"
38
#include "nsIProgressEventSink.h"
39
#include "nsILoadGroup.h"
40
#include "nsIInterfaceRequestor.h"
41
#include "nsIInterfaceRequestorUtils.h"
42
#include "nsIByteArrayInputStream.h"
43
#include "nsIStreamListener.h"
44
#include "nsIInputStreamPump.h"
45
#include "nsEmbedString.h"
47
// Everytime register handler is called, it picks the next available CID in the
49
// TODO - is there a cross-platform way to generate UUIDs and obviate this?
50
static const nsCID kProtocolCIDs[] =
52
{ 0xfc8b2366, 0x0d07, 0x45ef, { 0x9f, 0xab, 0x22, 0x31, 0x9d, 0xbc, 0xfa, 0x77 } },
53
{ 0x6b5db250, 0xcf4b, 0x4ab1, { 0xb3, 0xaa, 0x1a, 0x9a, 0xd6, 0xdf, 0x7f, 0x95 } },
54
{ 0x677c6eaf, 0x3c3d, 0x4e0d, { 0xad, 0x30, 0x5a, 0xb8, 0x69, 0x1d, 0x1f, 0xfc } },
55
{ 0xbe383b01, 0x58d3, 0x4e65, { 0x9d, 0x50, 0x05, 0xb4, 0xc3, 0x92, 0x43, 0x2e } },
56
{ 0x81290231, 0xedf0, 0x4876, { 0x94, 0xa2, 0xdb, 0x96, 0xca, 0xa3, 0xc1, 0xfc } },
57
{ 0xf9c466b0, 0x0da8, 0x48a7, { 0xbb, 0xe4, 0x2f, 0x63, 0xb0, 0x71, 0x41, 0x6f } },
58
{ 0x9cbaef5e, 0xdf94, 0x4cb0, { 0xb4, 0xc3, 0x89, 0x66, 0x89, 0xd0, 0x2d, 0x56 } },
59
{ 0xce79440d, 0xdafc, 0x4908, { 0xb8, 0x94, 0xb2, 0x74, 0xa3, 0x51, 0x2f, 0x45 } }
61
static const int kProtocolCIDsSize = sizeof(kProtocolCIDs) / sizeof(kProtocolCIDs[0]);
62
static PRUint32 gUsedCIDs = 0;
63
struct GeckoChannelCallbacks
66
GeckoChannelCallback *mCallback;
67
// SUCKS, component registry should properly copy this variable or take ownership of
68
// it so it doesn't have to be made a static or global like this.
69
// I also wonder if having component info memory dotted all over the place doesn't
70
// impact component registry performance in some way.
71
nsModuleComponentInfo mComponentInfo;
73
static GeckoChannelCallbacks gCallbacks[kProtocolCIDsSize];
75
class GeckoProtocolHandlerImpl :
76
public nsIProtocolHandler
80
NS_DECL_NSIPROTOCOLHANDLER
81
static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
85
class GeckoProtocolChannel :
87
public nsIStreamListener
93
NS_DECL_NSIREQUESTOBSERVER
94
NS_DECL_NSISTREAMLISTENER
96
GeckoProtocolChannel();
97
nsresult Init(nsIURI *aURI);
100
nsCOMPtr<nsIURI> mURI;
101
nsCOMPtr<nsIURI> mOriginalURI;
102
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
103
nsCOMPtr<nsIProgressEventSink> mProgressSink;
104
nsCOMPtr<nsISupports> mOwner;
105
nsCOMPtr<nsILoadGroup> mLoadGroup;
106
nsCOMPtr<nsIStreamListener> mListener;
107
nsCOMPtr<nsISupports> mListenerContext;
108
nsCOMPtr<nsIInputStream> mContentStream;
109
nsCString mContentType;
110
nsCString mContentCharset;
113
PRUint32 mContentLength;
115
nsCOMPtr<nsIInputStreamPump> mPump;
117
virtual ~GeckoProtocolChannel();
120
nsresult GeckoProtocolHandler::RegisterHandler(const char *aScheme, const char *aDescription, GeckoChannelCallback *aCallback)
122
if (!aScheme || !aCallback)
124
return NS_ERROR_INVALID_ARG;
127
if (gUsedCIDs >= kProtocolCIDsSize)
129
// We've run out of CIDs. Perhaps this code should be generating them
130
// on the fly somehow instead?
131
return NS_ERROR_FAILURE;
133
for (PRUint32 i = 0; i < gUsedCIDs; i++)
135
if (gCallbacks[i].mScheme.EqualsIgnoreCase(aScheme))
136
return NS_ERROR_FAILURE;
139
nsCAutoString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
140
contractID.Append(aScheme);
141
nsCID cid = kProtocolCIDs[gUsedCIDs];
142
gCallbacks[gUsedCIDs].mScheme = aScheme;
143
gCallbacks[gUsedCIDs].mCallback = aCallback;
146
nsModuleComponentInfo &ci = gCallbacks[gUsedCIDs].mComponentInfo;
147
memset(&ci, 0, sizeof(ci));
148
ci.mDescription = strdup(aDescription);
150
ci.mContractID = strdup(contractID.get());
151
ci.mConstructor = GeckoProtocolHandlerImpl::Create;
153
// Create a factory object which will create the protocol handler on demand
154
nsCOMPtr<nsIGenericFactory> factory;
155
NS_NewGenericFactory(getter_AddRefs(factory), &ci);
156
nsComponentManager::RegisterFactory(
157
cid, aDescription, contractID.get(), factory, PR_FALSE);
163
///////////////////////////////////////////////////////////////////////////////
164
///////////////////////////////////////////////////////////////////////////////
167
GeckoProtocolChannel::GeckoProtocolChannel() :
171
mLoadFlags(LOAD_NORMAL)
175
GeckoProtocolChannel::~GeckoProtocolChannel()
178
// nsMemory::Free(mData);
181
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
183
NS_METHOD GeckoProtocolHandlerImpl::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
185
GeckoProtocolHandlerImpl *impl = new GeckoProtocolHandlerImpl();
188
return NS_ERROR_OUT_OF_MEMORY;
191
nsresult rv = impl->QueryInterface(aIID, aResult);
199
NS_IMPL_ISUPPORTS1(GeckoProtocolHandlerImpl, nsIProtocolHandler)
201
/* readonly attribute ACString scheme; */
202
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetScheme(nsACString & aScheme)
204
// Since we have no clue what scheme we're an implementation of,
205
// just return the first one that was registered.
206
aScheme = gCallbacks[0].mScheme;
210
/* readonly attribute long defaultPort; */
211
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetDefaultPort(PRInt32 *aDefaultPort)
217
/* readonly attribute unsigned long protocolFlags; */
218
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetProtocolFlags(PRUint32 *aProtocolFlags)
220
*aProtocolFlags = URI_NORELATIVE | URI_NOAUTH;
224
/* nsIURI newURI (in AUTF8String aSpec, in string aOriginCharset, in nsIURI aBaseURI); */
225
NS_IMETHODIMP GeckoProtocolHandlerImpl::NewURI(const nsACString & aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval)
228
nsIURI* url = nsnull;
229
rv = nsComponentManager::CreateInstance(
230
kSimpleURICID, nsnull, NS_GET_IID(nsIURI), (void**) &url);
233
rv = url->SetSpec(aSpec);
243
/* nsIChannel newChannel (in nsIURI aURI); */
244
NS_IMETHODIMP GeckoProtocolHandlerImpl::NewChannel(nsIURI *aURI, nsIChannel **_retval)
246
GeckoProtocolChannel *channel = new GeckoProtocolChannel;
249
return NS_ERROR_OUT_OF_MEMORY;
252
channel->QueryInterface(NS_GET_IID(nsIChannel), (void **) _retval);
256
/* boolean allowPort (in long port, in string scheme); */
257
NS_IMETHODIMP GeckoProtocolHandlerImpl::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
259
return NS_ERROR_NOT_IMPLEMENTED;
263
///////////////////////////////////////////////////////////////////////////////
264
///////////////////////////////////////////////////////////////////////////////
266
NS_IMPL_ISUPPORTS4(GeckoProtocolChannel, nsIRequest, nsIChannel, nsIRequestObserver, nsIStreamListener)
268
nsresult GeckoProtocolChannel::Init(nsIURI *aURI)
275
///////////////////////////////////////////////////////////////////////////////
276
// nsIRequest methods
280
GeckoProtocolChannel::GetName(nsACString &result)
282
return mURI->GetSpec(result);
286
GeckoProtocolChannel::IsPending(PRBool *result)
293
GeckoProtocolChannel::GetStatus(nsresult *status)
300
GeckoProtocolChannel::Cancel(nsresult status)
302
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
305
return NS_ERROR_UNEXPECTED;
309
GeckoProtocolChannel::Suspend()
311
return NS_ERROR_UNEXPECTED;
315
GeckoProtocolChannel::Resume()
317
return NS_ERROR_UNEXPECTED;
321
////////////////////////////////////////////////////////////////////////////////
322
// nsIChannel methods:
325
GeckoProtocolChannel::GetOriginalURI(nsIURI* *aURI)
327
*aURI = mOriginalURI ? mOriginalURI : mURI;
333
GeckoProtocolChannel::SetOriginalURI(nsIURI* aURI)
340
GeckoProtocolChannel::GetURI(nsIURI* *aURI)
348
GeckoProtocolChannel::Open(nsIInputStream **_retval)
350
NS_NOTREACHED("GeckoProtocolChannel::Open");
351
return NS_ERROR_NOT_IMPLEMENTED;
355
GeckoProtocolChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
359
nsCAutoString scheme;
360
mURI->GetScheme(scheme);
361
for (PRUint32 i = 0; i < gUsedCIDs; i++)
363
if (stricmp(scheme.get(), gCallbacks[i].mScheme.get()) == 0)
365
rv = gCallbacks[i].mCallback->GetData(
366
mURI, NS_STATIC_CAST(nsIChannel *,this), mContentType, &mData, &mContentLength);
367
if (NS_FAILED(rv)) return rv;
369
nsCOMPtr<nsIByteArrayInputStream> stream;
370
rv = NS_NewByteArrayInputStream(getter_AddRefs(stream), (char *) mData, mContentLength);
371
if (NS_FAILED(rv)) return rv;
372
mContentStream = do_QueryInterface(stream);
374
mListenerContext = aContext;
375
mListener = aListener;
377
nsresult rv = NS_NewInputStreamPump(
378
getter_AddRefs(mPump), mContentStream, -1, mContentLength, 0, 0, PR_TRUE);
379
if (NS_FAILED(rv)) return rv;
383
mLoadGroup->AddRequest(this, nsnull);
386
rv = mPump->AsyncRead(this, nsnull);
387
if (NS_FAILED(rv)) return rv;
393
return NS_ERROR_FAILURE;
397
GeckoProtocolChannel::GetLoadFlags(PRUint32 *aLoadFlags)
399
*aLoadFlags = mLoadFlags;
404
GeckoProtocolChannel::SetLoadFlags(PRUint32 aLoadFlags)
406
mLoadFlags = aLoadFlags;
411
GeckoProtocolChannel::GetContentType(nsACString &aContentType)
413
aContentType = mContentType;
418
GeckoProtocolChannel::SetContentType(const nsACString &aContentType)
420
mContentType = aContentType;
425
GeckoProtocolChannel::GetContentCharset(nsACString &aContentCharset)
427
aContentCharset = mContentCharset;
432
GeckoProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
434
mContentCharset = aContentCharset;
439
GeckoProtocolChannel::GetContentLength(PRInt32 *aContentLength)
441
*aContentLength = mContentLength;
446
GeckoProtocolChannel::SetContentLength(PRInt32 aContentLength)
448
// silently ignore this...
453
GeckoProtocolChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
455
*aLoadGroup = mLoadGroup;
456
NS_IF_ADDREF(*aLoadGroup);
461
GeckoProtocolChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
463
mLoadGroup = aLoadGroup;
468
GeckoProtocolChannel::GetOwner(nsISupports* *aOwner)
470
*aOwner = mOwner.get();
471
NS_IF_ADDREF(*aOwner);
476
GeckoProtocolChannel::SetOwner(nsISupports* aOwner)
483
GeckoProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
485
*aNotificationCallbacks = mCallbacks.get();
486
NS_IF_ADDREF(*aNotificationCallbacks);
491
GeckoProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
493
mCallbacks = aNotificationCallbacks;
494
mProgressSink = do_GetInterface(mCallbacks);
499
GeckoProtocolChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
501
*aSecurityInfo = nsnull;
506
///////////////////////////////////////////////////////////////////////////////
507
// nsIStreamListener methods
511
GeckoProtocolChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
513
return mListener->OnStartRequest(this, mListenerContext);
517
GeckoProtocolChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
519
if (NS_SUCCEEDED(mStatus))
522
mListener->OnStopRequest(this, mListenerContext, mStatus);
524
mListenerContext = 0;
527
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
535
GeckoProtocolChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
536
nsIInputStream *stream,
537
PRUint32 offset, PRUint32 count)
541
rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
543
if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
544
mProgressSink->OnProgress(this, nsnull, offset + count, mContentLength);
546
return rv; // let the pump cancel on failure