michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsIStreamConverter.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "mozilla/Module.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIStringStream.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsMemory.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIRequest.h" michael@0: #include "nsNetCID.h" michael@0: michael@0: #define ASYNC_TEST // undefine this if you want to test sycnronous conversion. michael@0: michael@0: ///////////////////////////////// michael@0: // Event pump setup michael@0: ///////////////////////////////// michael@0: #ifdef XP_WIN michael@0: #include michael@0: #endif michael@0: michael@0: static int gKeepRunning = 0; michael@0: ///////////////////////////////// michael@0: // Event pump END michael@0: ///////////////////////////////// michael@0: michael@0: michael@0: ///////////////////////////////// michael@0: // Test converters include michael@0: ///////////////////////////////// michael@0: #include "Converters.h" michael@0: michael@0: // CID setup michael@0: static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // EndListener - This listener is the final one in the chain. It michael@0: // receives the fully converted data, although it doesn't do anything with michael@0: // the data. michael@0: //////////////////////////////////////////////////////////////////////// michael@0: class EndListener MOZ_FINAL : public nsIStreamListener { michael@0: public: michael@0: // nsISupports declaration michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: EndListener() {} michael@0: michael@0: // nsIStreamListener method michael@0: NS_IMETHOD OnDataAvailable(nsIRequest* request, nsISupports *ctxt, nsIInputStream *inStr, michael@0: uint64_t sourceOffset, uint32_t count) michael@0: { michael@0: nsresult rv; michael@0: uint32_t read; michael@0: uint64_t len64; michael@0: rv = inStr->Available(&len64); michael@0: if (NS_FAILED(rv)) return rv; michael@0: uint32_t len = (uint32_t)std::min(len64, (uint64_t)(UINT32_MAX - 1)); michael@0: michael@0: char *buffer = (char*)nsMemory::Alloc(len + 1); michael@0: if (!buffer) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = inStr->Read(buffer, len, &read); michael@0: buffer[len] = '\0'; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: printf("CONTEXT %p: Received %u bytes and the following data: \n %s\n\n", michael@0: static_cast(ctxt), read, buffer); michael@0: } michael@0: nsMemory::Free(buffer); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsIRequestObserver methods michael@0: NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt) { return NS_OK; } michael@0: michael@0: NS_IMETHOD OnStopRequest(nsIRequest* request, nsISupports *ctxt, michael@0: nsresult aStatus) { return NS_OK; } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(EndListener, michael@0: nsIStreamListener, michael@0: nsIRequestObserver) michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // EndListener END michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult SendData(const char * aData, nsIStreamListener* aListener, nsIRequest* request) { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr dataStream michael@0: (do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = dataStream->SetData(aData, strlen(aData)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint64_t avail = 0; michael@0: dataStream->Available(&avail); michael@0: michael@0: uint64_t offset = 0; michael@0: while (avail > 0) { michael@0: uint32_t count = saturated(avail); michael@0: rv = aListener->OnDataAvailable(request, nullptr, dataStream, michael@0: offset, count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: offset += count; michael@0: avail -= count; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: #define SEND_DATA(x) SendData(x, converterListener, request) michael@0: michael@0: static const mozilla::Module::CIDEntry kTestCIDs[] = { michael@0: { &kTestConverterCID, false, nullptr, CreateTestConverter }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kTestContracts[] = { michael@0: { NS_ISTREAMCONVERTER_KEY "?from=a/foo&to=b/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=b/foo&to=c/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=b/foo&to=d/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=c/foo&to=d/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=d/foo&to=e/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=d/foo&to=f/foo", &kTestConverterCID }, michael@0: { NS_ISTREAMCONVERTER_KEY "?from=t/foo&to=k/foo", &kTestConverterCID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::CategoryEntry kTestCategories[] = { michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=a/foo&to=b/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=b/foo&to=c/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=b/foo&to=d/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=c/foo&to=d/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=d/foo&to=e/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=d/foo&to=f/foo", "x" }, michael@0: { NS_ISTREAMCONVERTER_KEY, "?from=t/foo&to=k/foo", "x" }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kTestModule = { michael@0: mozilla::Module::kVersion, michael@0: kTestCIDs, michael@0: kTestContracts, michael@0: kTestCategories michael@0: }; michael@0: michael@0: int michael@0: main(int argc, char* argv[]) michael@0: { michael@0: nsresult rv; michael@0: { michael@0: XRE_AddStaticComponent(&kTestModule); michael@0: michael@0: nsCOMPtr servMan; michael@0: NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr); michael@0: michael@0: nsCOMPtr thread = do_GetCurrentThread(); michael@0: michael@0: nsCOMPtr catman = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return -1; michael@0: nsCString previous; michael@0: michael@0: nsCOMPtr StreamConvService = michael@0: do_GetService(kStreamConverterServiceCID, &rv); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: // Define the *from* content type and *to* content-type for conversion. michael@0: static const char fromStr[] = "a/foo"; michael@0: static const char toStr[] = "c/foo"; michael@0: michael@0: #ifdef ASYNC_TEST michael@0: // ASYNCHRONOUS conversion michael@0: michael@0: // Build up a channel that represents the content we're michael@0: // starting the transaction with. michael@0: // michael@0: // sample multipart mixed content-type string: michael@0: // "multipart/x-mixed-replacE;boundary=thisrandomstring" michael@0: #if 0 michael@0: nsCOMPtr channel; michael@0: nsCOMPtr dummyURI; michael@0: rv = NS_NewURI(getter_AddRefs(dummyURI), "http://meaningless"); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: rv = NS_NewInputStreamChannel(getter_AddRefs(channel), michael@0: dummyURI, michael@0: nullptr, // inStr michael@0: "text/plain", // content-type michael@0: -1); // XXX fix contentLength michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: nsCOMPtr request(do_QueryInterface(channel)); michael@0: #endif michael@0: michael@0: nsCOMPtr request; michael@0: michael@0: // setup a listener to receive the converted data. This guy is the end michael@0: // listener in the chain, he wants the fully converted (toType) data. michael@0: // An example of this listener in mozilla would be the DocLoader. michael@0: nsIStreamListener *dataReceiver = new EndListener(); michael@0: NS_ADDREF(dataReceiver); michael@0: michael@0: // setup a listener to push the data into. This listener sits inbetween the michael@0: // unconverted data of fromType, and the final listener in the chain (in this case michael@0: // the dataReceiver. michael@0: nsIStreamListener *converterListener = nullptr; michael@0: rv = StreamConvService->AsyncConvertData(fromStr, toStr, michael@0: dataReceiver, nullptr, &converterListener); michael@0: if (NS_FAILED(rv)) return -1; michael@0: NS_RELEASE(dataReceiver); michael@0: michael@0: // at this point we have a stream listener to push data to, and the one michael@0: // that will receive the converted data. Let's mimic On*() calls and get the conversion michael@0: // going. Typically these On*() calls would be made inside their respective wrappers On*() michael@0: // methods. michael@0: rv = converterListener->OnStartRequest(request, nullptr); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: rv = SEND_DATA("aaa"); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: rv = SEND_DATA("aaa"); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: // Finish the request. michael@0: rv = converterListener->OnStopRequest(request, nullptr, rv); michael@0: if (NS_FAILED(rv)) return -1; michael@0: michael@0: NS_RELEASE(converterListener); michael@0: #else michael@0: // SYNCHRONOUS conversion michael@0: nsCOMPtr convertedData; michael@0: rv = StreamConvService->Convert(inputData, fromStr, toStr, michael@0: nullptr, getter_AddRefs(convertedData)); michael@0: if (NS_FAILED(rv)) return -1; michael@0: #endif michael@0: michael@0: // Enter the message pump to allow the URL load to proceed. michael@0: while ( gKeepRunning ) { michael@0: if (!NS_ProcessNextEvent(thread)) michael@0: break; michael@0: } michael@0: } // this scopes the nsCOMPtrs michael@0: // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM michael@0: NS_ShutdownXPCOM(nullptr); michael@0: return 0; michael@0: }