gecko/embedding/tests/wxEmbed/GeckoProtocolHandler.cpp

567 lines
16 KiB
C++

/* ***** BEGIN LICENSE BLOCK *****
* Version: Mozilla-sample-code 1.0
*
* Copyright (c) 2002 Netscape Communications Corporation and
* other contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this Mozilla sample software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Contributor(s):
* Adam Lock <adamlock@netscape.com>
*
* ***** END LICENSE BLOCK ***** */
#include "GeckoProtocolHandler.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIGenericFactory.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIProgressEventSink.h"
#include "nsILoadGroup.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIStringStream.h"
#include "nsIStreamListener.h"
#include "nsIInputStreamPump.h"
#include "nsEmbedString.h"
// Everytime register handler is called, it picks the next available CID in the
// list.
// TODO - is there a cross-platform way to generate UUIDs and obviate this?
static const nsCID kProtocolCIDs[] =
{
{ 0xfc8b2366, 0x0d07, 0x45ef, { 0x9f, 0xab, 0x22, 0x31, 0x9d, 0xbc, 0xfa, 0x77 } },
{ 0x6b5db250, 0xcf4b, 0x4ab1, { 0xb3, 0xaa, 0x1a, 0x9a, 0xd6, 0xdf, 0x7f, 0x95 } },
{ 0x677c6eaf, 0x3c3d, 0x4e0d, { 0xad, 0x30, 0x5a, 0xb8, 0x69, 0x1d, 0x1f, 0xfc } },
{ 0xbe383b01, 0x58d3, 0x4e65, { 0x9d, 0x50, 0x05, 0xb4, 0xc3, 0x92, 0x43, 0x2e } },
{ 0x81290231, 0xedf0, 0x4876, { 0x94, 0xa2, 0xdb, 0x96, 0xca, 0xa3, 0xc1, 0xfc } },
{ 0xf9c466b0, 0x0da8, 0x48a7, { 0xbb, 0xe4, 0x2f, 0x63, 0xb0, 0x71, 0x41, 0x6f } },
{ 0x9cbaef5e, 0xdf94, 0x4cb0, { 0xb4, 0xc3, 0x89, 0x66, 0x89, 0xd0, 0x2d, 0x56 } },
{ 0xce79440d, 0xdafc, 0x4908, { 0xb8, 0x94, 0xb2, 0x74, 0xa3, 0x51, 0x2f, 0x45 } }
};
static const int kProtocolCIDsSize = sizeof(kProtocolCIDs) / sizeof(kProtocolCIDs[0]);
static PRUint32 gUsedCIDs = 0;
struct GeckoChannelCallbacks
{
nsCString mScheme;
GeckoChannelCallback *mCallback;
// SUCKS, component registry should properly copy this variable or take ownership of
// it so it doesn't have to be made a static or global like this.
// I also wonder if having component info memory dotted all over the place doesn't
// impact component registry performance in some way.
nsModuleComponentInfo mComponentInfo;
};
static GeckoChannelCallbacks gCallbacks[kProtocolCIDsSize];
class GeckoProtocolHandlerImpl :
public nsIProtocolHandler
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
};
class GeckoProtocolChannel :
public nsIChannel,
public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
NS_DECL_NSICHANNEL
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
GeckoProtocolChannel();
nsresult Init(nsIURI *aURI);
protected:
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mOriginalURI;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsIProgressEventSink> mProgressSink;
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsISupports> mListenerContext;
nsCOMPtr<nsIInputStream> mContentStream;
nsCString mContentType;
nsCString mContentCharset;
PRUint32 mLoadFlags;
nsresult mStatus;
PRUint32 mContentLength;
void * mData;
nsCOMPtr<nsIInputStreamPump> mPump;
virtual ~GeckoProtocolChannel();
};
nsresult GeckoProtocolHandler::RegisterHandler(const char *aScheme, const char *aDescription, GeckoChannelCallback *aCallback)
{
if (!aScheme || !aCallback)
{
return NS_ERROR_INVALID_ARG;
}
if (gUsedCIDs >= kProtocolCIDsSize)
{
// We've run out of CIDs. Perhaps this code should be generating them
// on the fly somehow instead?
return NS_ERROR_FAILURE;
}
for (PRUint32 i = 0; i < gUsedCIDs; i++)
{
if (gCallbacks[i].mScheme.EqualsIgnoreCase(aScheme))
return NS_ERROR_FAILURE;
}
nsCAutoString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
contractID.Append(aScheme);
nsCID cid = kProtocolCIDs[gUsedCIDs];
gCallbacks[gUsedCIDs].mScheme = aScheme;
gCallbacks[gUsedCIDs].mCallback = aCallback;
gUsedCIDs++;
nsModuleComponentInfo &ci = gCallbacks[gUsedCIDs].mComponentInfo;
memset(&ci, 0, sizeof(ci));
ci.mDescription = strdup(aDescription);
ci.mCID = cid;
ci.mContractID = strdup(contractID.get());
ci.mConstructor = GeckoProtocolHandlerImpl::Create;
// Create a factory object which will create the protocol handler on demand
nsCOMPtr<nsIGenericFactory> factory;
NS_NewGenericFactory(getter_AddRefs(factory), &ci);
nsCOMPtr<nsIComponentRegistrar> registrar;
NS_GetComponentRegistrar(getter_AddRefs(registrar));
if (registrar)
registrar->RegisterFactory(cid, aDescription, contractID.get(), factory);
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
GeckoProtocolChannel::GeckoProtocolChannel() :
mContentLength(0),
mData(nsnull),
mStatus(NS_OK),
mLoadFlags(LOAD_NORMAL)
{
}
GeckoProtocolChannel::~GeckoProtocolChannel()
{
// if (mData)
// nsMemory::Free(mData);
}
NS_METHOD GeckoProtocolHandlerImpl::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
GeckoProtocolHandlerImpl *impl = new GeckoProtocolHandlerImpl();
if (!impl)
{
return NS_ERROR_OUT_OF_MEMORY;
}
*aResult = nsnull;
nsresult rv = impl->QueryInterface(aIID, aResult);
if (NS_FAILED(rv))
{
impl->Release();
}
return rv;
}
NS_IMPL_ISUPPORTS1(GeckoProtocolHandlerImpl, nsIProtocolHandler)
/* readonly attribute ACString scheme; */
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetScheme(nsACString & aScheme)
{
// Since we have no clue what scheme we're an implementation of,
// just return the first one that was registered.
aScheme = gCallbacks[0].mScheme;
return NS_OK;
}
/* readonly attribute long defaultPort; */
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetDefaultPort(PRInt32 *aDefaultPort)
{
*aDefaultPort = -1;
return NS_OK;
}
/* readonly attribute unsigned long protocolFlags; */
NS_IMETHODIMP GeckoProtocolHandlerImpl::GetProtocolFlags(PRUint32 *aProtocolFlags)
{
// XXXbz Not setting any of the protocol security flags for now, because I
// have no idea what this is used for. Whoever uses it should set the
// flags.
*aProtocolFlags = URI_NORELATIVE | URI_NOAUTH;
return NS_OK;
}
/* nsIURI newURI (in AUTF8String aSpec, in string aOriginCharset, in nsIURI aBaseURI); */
NS_IMETHODIMP GeckoProtocolHandlerImpl::NewURI(const nsACString & aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval)
{
nsresult rv;
nsIURI* url;
rv = CallCreateInstance(NS_SIMPLEURI_CONTRACTID, &url);
if (NS_FAILED(rv))
return rv;
rv = url->SetSpec(aSpec);
if (NS_FAILED(rv))
{
NS_RELEASE(url);
return rv;
}
*_retval = url;
return rv;
}
/* nsIChannel newChannel (in nsIURI aURI); */
NS_IMETHODIMP GeckoProtocolHandlerImpl::NewChannel(nsIURI *aURI, nsIChannel **_retval)
{
NS_ENSURE_ARG_POINTER(aURI);
GeckoProtocolChannel *channel = new GeckoProtocolChannel;
if (!channel)
{
return NS_ERROR_OUT_OF_MEMORY;
}
channel->Init(aURI);
channel->QueryInterface(NS_GET_IID(nsIChannel), (void **) _retval);
return NS_OK;
}
/* boolean allowPort (in long port, in string scheme); */
NS_IMETHODIMP GeckoProtocolHandlerImpl::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS4(GeckoProtocolChannel, nsIRequest, nsIChannel, nsIRequestObserver, nsIStreamListener)
nsresult GeckoProtocolChannel::Init(nsIURI *aURI)
{
mURI = aURI;
mOriginalURI = aURI;
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRequest methods
NS_IMETHODIMP
GeckoProtocolChannel::GetName(nsACString &result)
{
return mURI->GetSpec(result);
}
NS_IMETHODIMP
GeckoProtocolChannel::IsPending(PRBool *result)
{
*result = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetStatus(nsresult *status)
{
*status = mStatus;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::Cancel(nsresult status)
{
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
mStatus = status;
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
GeckoProtocolChannel::Suspend()
{
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
GeckoProtocolChannel::Resume()
{
return NS_ERROR_UNEXPECTED;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel methods:
NS_IMETHODIMP
GeckoProtocolChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI;
NS_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetOriginalURI(nsIURI* aURI)
{
NS_ENSURE_ARG_POINTER(aURI);
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetURI(nsIURI* *aURI)
{
*aURI = mURI;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::Open(nsIInputStream **_retval)
{
NS_NOTREACHED("GeckoProtocolChannel::Open");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
GeckoProtocolChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
nsresult rv = NS_OK;
nsCAutoString scheme;
mURI->GetScheme(scheme);
for (PRUint32 i = 0; i < gUsedCIDs; i++)
{
if (stricmp(scheme.get(), gCallbacks[i].mScheme.get()) == 0)
{
rv = gCallbacks[i].mCallback->GetData(
mURI, static_cast<nsIChannel *>(this), mContentType, &mData, &mContentLength);
if (NS_FAILED(rv)) return rv;
rv = NS_NewByteInputStream(getter_AddRefs(mContentStream),
(char *) mData, mContentLength,
NS_ASSIGNMENT_ADOPT);
if (NS_FAILED(rv)) {
nsMemory::Free(mData);
mData = nsnull;
return rv;
}
mListenerContext = aContext;
mListener = aListener;
// XXX should this use 64-bit content lengths?
nsresult rv = NS_NewInputStreamPump(
getter_AddRefs(mPump), mContentStream, nsInt64(-1),
nsInt64(mContentLength), 0, 0, PR_TRUE);
if (NS_FAILED(rv)) return rv;
if (mLoadGroup)
{
mLoadGroup->AddRequest(this, nsnull);
}
rv = mPump->AsyncRead(this, nsnull);
if (NS_FAILED(rv)) return rv;
return rv;
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetLoadFlags(PRUint32 *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetLoadFlags(PRUint32 aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetContentType(nsACString &aContentType)
{
aContentType = mContentType;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetContentType(const nsACString &aContentType)
{
mContentType = aContentType;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetContentCharset(nsACString &aContentCharset)
{
aContentCharset = mContentCharset;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
{
mContentCharset = aContentCharset;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetContentLength(PRInt32 *aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetContentLength(PRInt32 aContentLength)
{
// silently ignore this...
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
mProgressSink = do_GetInterface(mCallbacks);
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsIStreamListener methods
NS_IMETHODIMP
GeckoProtocolChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
{
return mListener->OnStartRequest(this, mListenerContext);
}
NS_IMETHODIMP
GeckoProtocolChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
{
if (NS_SUCCEEDED(mStatus))
mStatus = status;
mListener->OnStopRequest(this, mListenerContext, mStatus);
mListener = 0;
mListenerContext = 0;
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
mPump = 0;
mContentStream = 0;
// Drop notification callbacks to prevent cycles.
mCallbacks = 0;
mProgressSink = 0;
return NS_OK;
}
NS_IMETHODIMP
GeckoProtocolChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
nsIInputStream *stream,
PRUint32 offset, PRUint32 count)
{
nsresult rv;
rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
// XXX can this use real 64-bit ints?
if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
mProgressSink->OnProgress(this, nsnull, nsUint64(offset + count),
nsUint64(mContentLength));
return rv; // let the pump cancel on failure
}