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.
27 * Adam Lock <adamlock@netscape.com>
29 * ***** END LICENSE BLOCK ***** */
31 #include "GeckoProtocolHandler.h"
34 #include "nsNetUtil.h"
35 #include "nsIGenericFactory.h"
36 #include "nsIComponentManager.h"
37 #include "nsIComponentRegistrar.h"
38 #include "nsIProgressEventSink.h"
39 #include "nsILoadGroup.h"
40 #include "nsIInterfaceRequestor.h"
41 #include "nsIInterfaceRequestorUtils.h"
42 #include "nsIStringStream.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
);
157 nsCOMPtr
<nsIComponentRegistrar
> registrar
;
158 NS_GetComponentRegistrar(getter_AddRefs(registrar
));
160 registrar
->RegisterFactory(cid
, aDescription
, contractID
.get(), factory
);
166 ///////////////////////////////////////////////////////////////////////////////
167 ///////////////////////////////////////////////////////////////////////////////
170 GeckoProtocolChannel::GeckoProtocolChannel() :
174 mLoadFlags(LOAD_NORMAL
)
178 GeckoProtocolChannel::~GeckoProtocolChannel()
181 // nsMemory::Free(mData);
184 NS_METHOD
GeckoProtocolHandlerImpl::Create(nsISupports
*aOuter
, REFNSIID aIID
, void **aResult
)
186 GeckoProtocolHandlerImpl
*impl
= new GeckoProtocolHandlerImpl();
189 return NS_ERROR_OUT_OF_MEMORY
;
192 nsresult rv
= impl
->QueryInterface(aIID
, aResult
);
200 NS_IMPL_ISUPPORTS1(GeckoProtocolHandlerImpl
, nsIProtocolHandler
)
202 /* readonly attribute ACString scheme; */
203 NS_IMETHODIMP
GeckoProtocolHandlerImpl::GetScheme(nsACString
& aScheme
)
205 // Since we have no clue what scheme we're an implementation of,
206 // just return the first one that was registered.
207 aScheme
= gCallbacks
[0].mScheme
;
211 /* readonly attribute long defaultPort; */
212 NS_IMETHODIMP
GeckoProtocolHandlerImpl::GetDefaultPort(PRInt32
*aDefaultPort
)
218 /* readonly attribute unsigned long protocolFlags; */
219 NS_IMETHODIMP
GeckoProtocolHandlerImpl::GetProtocolFlags(PRUint32
*aProtocolFlags
)
221 // XXXbz Not setting any of the protocol security flags for now, because I
222 // have no idea what this is used for. Whoever uses it should set the
224 *aProtocolFlags
= URI_NORELATIVE
| URI_NOAUTH
;
228 /* nsIURI newURI (in AUTF8String aSpec, in string aOriginCharset, in nsIURI aBaseURI); */
229 NS_IMETHODIMP
GeckoProtocolHandlerImpl::NewURI(const nsACString
& aSpec
, const char *aOriginCharset
, nsIURI
*aBaseURI
, nsIURI
**_retval
)
233 rv
= CallCreateInstance(NS_SIMPLEURI_CONTRACTID
, &url
);
236 rv
= url
->SetSpec(aSpec
);
246 /* nsIChannel newChannel (in nsIURI aURI); */
247 NS_IMETHODIMP
GeckoProtocolHandlerImpl::NewChannel(nsIURI
*aURI
, nsIChannel
**_retval
)
249 NS_ENSURE_ARG_POINTER(aURI
);
250 GeckoProtocolChannel
*channel
= new GeckoProtocolChannel
;
253 return NS_ERROR_OUT_OF_MEMORY
;
256 channel
->QueryInterface(NS_GET_IID(nsIChannel
), (void **) _retval
);
260 /* boolean allowPort (in long port, in string scheme); */
261 NS_IMETHODIMP
GeckoProtocolHandlerImpl::AllowPort(PRInt32 port
, const char *scheme
, PRBool
*_retval
)
263 return NS_ERROR_NOT_IMPLEMENTED
;
267 ///////////////////////////////////////////////////////////////////////////////
268 ///////////////////////////////////////////////////////////////////////////////
270 NS_IMPL_ISUPPORTS4(GeckoProtocolChannel
, nsIRequest
, nsIChannel
, nsIRequestObserver
, nsIStreamListener
)
272 nsresult
GeckoProtocolChannel::Init(nsIURI
*aURI
)
280 ///////////////////////////////////////////////////////////////////////////////
281 // nsIRequest methods
285 GeckoProtocolChannel::GetName(nsACString
&result
)
287 return mURI
->GetSpec(result
);
291 GeckoProtocolChannel::IsPending(PRBool
*result
)
298 GeckoProtocolChannel::GetStatus(nsresult
*status
)
305 GeckoProtocolChannel::Cancel(nsresult status
)
307 NS_ASSERTION(NS_FAILED(status
), "shouldn't cancel with a success code");
310 return NS_ERROR_UNEXPECTED
;
314 GeckoProtocolChannel::Suspend()
316 return NS_ERROR_UNEXPECTED
;
320 GeckoProtocolChannel::Resume()
322 return NS_ERROR_UNEXPECTED
;
326 ////////////////////////////////////////////////////////////////////////////////
327 // nsIChannel methods:
330 GeckoProtocolChannel::GetOriginalURI(nsIURI
* *aURI
)
332 *aURI
= mOriginalURI
;
338 GeckoProtocolChannel::SetOriginalURI(nsIURI
* aURI
)
340 NS_ENSURE_ARG_POINTER(aURI
);
346 GeckoProtocolChannel::GetURI(nsIURI
* *aURI
)
354 GeckoProtocolChannel::Open(nsIInputStream
**_retval
)
356 NS_NOTREACHED("GeckoProtocolChannel::Open");
357 return NS_ERROR_NOT_IMPLEMENTED
;
361 GeckoProtocolChannel::AsyncOpen(nsIStreamListener
*aListener
, nsISupports
*aContext
)
365 nsCAutoString scheme
;
366 mURI
->GetScheme(scheme
);
367 for (PRUint32 i
= 0; i
< gUsedCIDs
; i
++)
369 if (stricmp(scheme
.get(), gCallbacks
[i
].mScheme
.get()) == 0)
371 rv
= gCallbacks
[i
].mCallback
->GetData(
372 mURI
, static_cast<nsIChannel
*>(this), mContentType
, &mData
, &mContentLength
);
373 if (NS_FAILED(rv
)) return rv
;
375 rv
= NS_NewByteInputStream(getter_AddRefs(mContentStream
),
376 (char *) mData
, mContentLength
,
377 NS_ASSIGNMENT_ADOPT
);
379 nsMemory::Free(mData
);
384 mListenerContext
= aContext
;
385 mListener
= aListener
;
387 // XXX should this use 64-bit content lengths?
388 nsresult rv
= NS_NewInputStreamPump(
389 getter_AddRefs(mPump
), mContentStream
, nsInt64(-1),
390 nsInt64(mContentLength
), 0, 0, PR_TRUE
);
391 if (NS_FAILED(rv
)) return rv
;
395 mLoadGroup
->AddRequest(this, nsnull
);
398 rv
= mPump
->AsyncRead(this, nsnull
);
399 if (NS_FAILED(rv
)) return rv
;
405 return NS_ERROR_FAILURE
;
409 GeckoProtocolChannel::GetLoadFlags(PRUint32
*aLoadFlags
)
411 *aLoadFlags
= mLoadFlags
;
416 GeckoProtocolChannel::SetLoadFlags(PRUint32 aLoadFlags
)
418 mLoadFlags
= aLoadFlags
;
423 GeckoProtocolChannel::GetContentType(nsACString
&aContentType
)
425 aContentType
= mContentType
;
430 GeckoProtocolChannel::SetContentType(const nsACString
&aContentType
)
432 mContentType
= aContentType
;
437 GeckoProtocolChannel::GetContentCharset(nsACString
&aContentCharset
)
439 aContentCharset
= mContentCharset
;
444 GeckoProtocolChannel::SetContentCharset(const nsACString
&aContentCharset
)
446 mContentCharset
= aContentCharset
;
451 GeckoProtocolChannel::GetContentLength(PRInt32
*aContentLength
)
453 *aContentLength
= mContentLength
;
458 GeckoProtocolChannel::SetContentLength(PRInt32 aContentLength
)
460 // silently ignore this...
465 GeckoProtocolChannel::GetLoadGroup(nsILoadGroup
* *aLoadGroup
)
467 *aLoadGroup
= mLoadGroup
;
468 NS_IF_ADDREF(*aLoadGroup
);
473 GeckoProtocolChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
475 mLoadGroup
= aLoadGroup
;
480 GeckoProtocolChannel::GetOwner(nsISupports
* *aOwner
)
482 *aOwner
= mOwner
.get();
483 NS_IF_ADDREF(*aOwner
);
488 GeckoProtocolChannel::SetOwner(nsISupports
* aOwner
)
495 GeckoProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor
* *aNotificationCallbacks
)
497 *aNotificationCallbacks
= mCallbacks
.get();
498 NS_IF_ADDREF(*aNotificationCallbacks
);
503 GeckoProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aNotificationCallbacks
)
505 mCallbacks
= aNotificationCallbacks
;
506 mProgressSink
= do_GetInterface(mCallbacks
);
511 GeckoProtocolChannel::GetSecurityInfo(nsISupports
**aSecurityInfo
)
513 *aSecurityInfo
= nsnull
;
518 ///////////////////////////////////////////////////////////////////////////////
519 // nsIStreamListener methods
523 GeckoProtocolChannel::OnStartRequest(nsIRequest
*req
, nsISupports
*ctx
)
525 return mListener
->OnStartRequest(this, mListenerContext
);
529 GeckoProtocolChannel::OnStopRequest(nsIRequest
*req
, nsISupports
*ctx
, nsresult status
)
531 if (NS_SUCCEEDED(mStatus
))
534 mListener
->OnStopRequest(this, mListenerContext
, mStatus
);
536 mListenerContext
= 0;
539 mLoadGroup
->RemoveRequest(this, nsnull
, mStatus
);
544 // Drop notification callbacks to prevent cycles.
552 GeckoProtocolChannel::OnDataAvailable(nsIRequest
*req
, nsISupports
*ctx
,
553 nsIInputStream
*stream
,
554 PRUint32 offset
, PRUint32 count
)
558 rv
= mListener
->OnDataAvailable(this, mListenerContext
, stream
, offset
, count
);
560 // XXX can this use real 64-bit ints?
561 if (mProgressSink
&& NS_SUCCEEDED(rv
) && !(mLoadFlags
& LOAD_BACKGROUND
))
562 mProgressSink
->OnProgress(this, nsnull
, nsUint64(offset
+ count
),
563 nsUint64(mContentLength
));
565 return rv
; // let the pump cancel on failure