forked from mirrors/gecko-dev
		
	 71422dcaa9
			
		
	
	
		71422dcaa9
		
	
	
	
	
		
			
			s/NS_PRECONDITION/MOZ_ASSERT/ and reindent MozReview-Commit-ID: KuUsnVe2h8L --HG-- extra : source : c14655ab3df2c9b1465dd8102b9d25683359a37b
		
			
				
	
	
		
			538 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 | |
|  *
 | |
|  * This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsStreamConverterService.h"
 | |
| #include "nsIComponentRegistrar.h"
 | |
| #include "nsAutoPtr.h"
 | |
| #include "nsString.h"
 | |
| #include "nsAtom.h"
 | |
| #include "nsDeque.h"
 | |
| #include "nsIInputStream.h"
 | |
| #include "nsIStreamConverter.h"
 | |
| #include "nsICategoryManager.h"
 | |
| #include "nsXPCOM.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsCOMArray.h"
 | |
| #include "nsTArray.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| #include "nsISimpleEnumerator.h"
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////
 | |
| // Breadth-First-Search (BFS) algorithm state classes and types.
 | |
| 
 | |
| // Used to establish discovered verticies.
 | |
| enum BFScolors {white, gray, black};
 | |
| 
 | |
| // BFS hashtable data class.
 | |
| struct BFSTableData {
 | |
|     nsCString key;
 | |
|     BFScolors color;
 | |
|     int32_t distance;
 | |
|     nsAutoPtr<nsCString> predecessor;
 | |
| 
 | |
|     explicit BFSTableData(const nsACString& aKey)
 | |
|       : key(aKey), color(white), distance(-1)
 | |
|     {
 | |
|     }
 | |
| };
 | |
| 
 | |
| ////////////////////////////////////////////////////////////
 | |
| // nsISupports methods
 | |
| NS_IMPL_ISUPPORTS(nsStreamConverterService, nsIStreamConverterService)
 | |
| 
 | |
| 
 | |
| ////////////////////////////////////////////////////////////
 | |
| // nsIStreamConverterService methods
 | |
| 
 | |
| ////////////////////////////////////////////////////////////
 | |
| // nsStreamConverterService methods
 | |
| 
 | |
| // Builds the graph represented as an adjacency list (and built up in
 | |
| // memory using an nsObjectHashtable and nsCOMArray combination).
 | |
| //
 | |
| // :BuildGraph() consults the category manager for all stream converter
 | |
| // CONTRACTIDS then fills the adjacency list with edges.
 | |
| // An edge in this case is comprised of a FROM and TO MIME type combination.
 | |
| //
 | |
| // CONTRACTID format:
 | |
| // @mozilla.org/streamconv;1?from=text/html&to=text/plain
 | |
| // XXX curently we only handle a single from and to combo, we should repeat the
 | |
| // XXX registration process for any series of from-to combos.
 | |
| // XXX can use nsTokenizer for this.
 | |
| //
 | |
| 
 | |
| nsresult
 | |
| nsStreamConverterService::BuildGraph() {
 | |
| 
 | |
|     nsresult rv;
 | |
| 
 | |
|     nsCOMPtr<nsICategoryManager> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     nsCOMPtr<nsISimpleEnumerator> entries;
 | |
|     rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY, getter_AddRefs(entries));
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     // go through each entry to build the graph
 | |
|     nsCOMPtr<nsISupports> supports;
 | |
|     nsCOMPtr<nsISupportsCString> entry;
 | |
|     rv = entries->GetNext(getter_AddRefs(supports));
 | |
|     while (NS_SUCCEEDED(rv)) {
 | |
|         entry = do_QueryInterface(supports);
 | |
| 
 | |
|         // get the entry string
 | |
|         nsAutoCString entryString;
 | |
|         rv = entry->GetData(entryString);
 | |
|         if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|         // cobble the entry string w/ the converter key to produce a full contractID.
 | |
|         nsAutoCString contractID(NS_ISTREAMCONVERTER_KEY);
 | |
|         contractID.Append(entryString);
 | |
| 
 | |
|         // now we've got the CONTRACTID, let's parse it up.
 | |
|         rv = AddAdjacency(contractID.get());
 | |
|         if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|         rv = entries->GetNext(getter_AddRefs(supports));
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| // XXX currently you can not add the same adjacency (i.e. you can't have multiple
 | |
| // XXX stream converters registering to handle the same from-to combination. It's
 | |
| // XXX not programatically prohibited, it's just that results are un-predictable
 | |
| // XXX right now.
 | |
| nsresult
 | |
| nsStreamConverterService::AddAdjacency(const char *aContractID) {
 | |
|     nsresult rv;
 | |
|     // first parse out the FROM and TO MIME-types.
 | |
| 
 | |
|     nsAutoCString fromStr, toStr;
 | |
|     rv = ParseFromTo(aContractID, fromStr, toStr);
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     // Each MIME-type is a vertex in the graph, so first lets make sure
 | |
|     // each MIME-type is represented as a key in our hashtable.
 | |
| 
 | |
|     nsTArray<RefPtr<nsAtom>>* fromEdges = mAdjacencyList.Get(fromStr);
 | |
|     if (!fromEdges) {
 | |
|         // There is no fromStr vertex, create one.
 | |
|         fromEdges = new nsTArray<RefPtr<nsAtom>>();
 | |
|         mAdjacencyList.Put(fromStr, fromEdges);
 | |
|     }
 | |
| 
 | |
|     if (!mAdjacencyList.Get(toStr)) {
 | |
|         // There is no toStr vertex, create one.
 | |
|         mAdjacencyList.Put(toStr, new nsTArray<RefPtr<nsAtom>>());
 | |
|     }
 | |
| 
 | |
|     // Now we know the FROM and TO types are represented as keys in the hashtable.
 | |
|     // Let's "connect" the verticies, making an edge.
 | |
| 
 | |
|     RefPtr<nsAtom> vertex = NS_Atomize(toStr);
 | |
|     if (!vertex) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|     NS_ASSERTION(fromEdges, "something wrong in adjacency list construction");
 | |
|     if (!fromEdges)
 | |
|         return NS_ERROR_FAILURE;
 | |
| 
 | |
|     return fromEdges->AppendElement(vertex) ? NS_OK : NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsStreamConverterService::ParseFromTo(const char *aContractID, nsCString &aFromRes, nsCString &aToRes) {
 | |
| 
 | |
|     nsAutoCString ContractIDStr(aContractID);
 | |
| 
 | |
|     int32_t fromLoc = ContractIDStr.Find("from=");
 | |
|     int32_t toLoc   = ContractIDStr.Find("to=");
 | |
|     if (-1 == fromLoc || -1 == toLoc ) return NS_ERROR_FAILURE;
 | |
| 
 | |
|     fromLoc = fromLoc + 5;
 | |
|     toLoc = toLoc + 3;
 | |
| 
 | |
|     nsAutoCString fromStr, toStr;
 | |
| 
 | |
|     ContractIDStr.Mid(fromStr, fromLoc, toLoc - 4 - fromLoc);
 | |
|     ContractIDStr.Mid(toStr, toLoc, ContractIDStr.Length() - toLoc);
 | |
| 
 | |
|     aFromRes.Assign(fromStr);
 | |
|     aToRes.Assign(toStr);
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| typedef nsClassHashtable<nsCStringHashKey, BFSTableData> BFSHashTable;
 | |
| 
 | |
| 
 | |
| // nsObjectHashtable enumerator functions.
 | |
| 
 | |
| class CStreamConvDeallocator : public nsDequeFunctor {
 | |
| public:
 | |
|     void operator()(void* anObject) override {
 | |
|         nsCString *string = (nsCString*)anObject;
 | |
|         delete string;
 | |
|     }
 | |
| };
 | |
| 
 | |
| // walks the graph using a breadth-first-search algorithm which generates a discovered
 | |
| // verticies tree. This tree is then walked up (from destination vertex, to origin vertex)
 | |
| // and each link in the chain is added to an nsStringArray. A direct lookup for the given
 | |
| // CONTRACTID should be made prior to calling this method in an attempt to find a direct
 | |
| // converter rather than walking the graph.
 | |
| nsresult
 | |
| nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) {
 | |
|     nsresult rv;
 | |
|     if (!aEdgeList) return NS_ERROR_NULL_POINTER;
 | |
|     *aEdgeList = nullptr;
 | |
| 
 | |
|     // walk the graph in search of the appropriate converter.
 | |
| 
 | |
|     uint32_t vertexCount = mAdjacencyList.Count();
 | |
|     if (0 >= vertexCount) return NS_ERROR_FAILURE;
 | |
| 
 | |
|     // Create a corresponding color table for each vertex in the graph.
 | |
|     BFSHashTable lBFSTable;
 | |
|     for (auto iter = mAdjacencyList.Iter(); !iter.Done(); iter.Next()) {
 | |
|         const nsACString &key = iter.Key();
 | |
|         MOZ_ASSERT(iter.UserData(), "no data in the table iteration");
 | |
|         lBFSTable.Put(key, new BFSTableData(key));
 | |
|     }
 | |
| 
 | |
|     NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem");
 | |
| 
 | |
|     // This is our source vertex; our starting point.
 | |
|     nsAutoCString fromC, toC;
 | |
|     rv = ParseFromTo(aContractID, fromC, toC);
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     BFSTableData *data = lBFSTable.Get(fromC);
 | |
|     if (!data) {
 | |
|         return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     data->color = gray;
 | |
|     data->distance = 0;
 | |
|     auto *dtorFunc = new CStreamConvDeallocator();
 | |
| 
 | |
|     nsDeque grayQ(dtorFunc);
 | |
| 
 | |
|     // Now generate the shortest path tree.
 | |
|     grayQ.Push(new nsCString(fromC));
 | |
|     while (0 < grayQ.GetSize()) {
 | |
|         nsCString *currentHead = (nsCString*)grayQ.PeekFront();
 | |
|         nsTArray<RefPtr<nsAtom>>* data2 = mAdjacencyList.Get(*currentHead);
 | |
|         if (!data2) return NS_ERROR_FAILURE;
 | |
| 
 | |
|         // Get the state of the current head to calculate the distance of each
 | |
|         // reachable vertex in the loop.
 | |
|         BFSTableData *headVertexState = lBFSTable.Get(*currentHead);
 | |
|         if (!headVertexState) return NS_ERROR_FAILURE;
 | |
| 
 | |
|         int32_t edgeCount = data2->Length();
 | |
| 
 | |
|         for (int32_t i = 0; i < edgeCount; i++) {
 | |
|             nsAtom* curVertexAtom = data2->ElementAt(i);
 | |
|             auto *curVertex = new nsCString();
 | |
|             curVertexAtom->ToUTF8String(*curVertex);
 | |
| 
 | |
|             BFSTableData *curVertexState = lBFSTable.Get(*curVertex);
 | |
|             if (!curVertexState) {
 | |
|                 delete curVertex;
 | |
|                 return NS_ERROR_FAILURE;
 | |
|             }
 | |
| 
 | |
|             if (white == curVertexState->color) {
 | |
|                 curVertexState->color = gray;
 | |
|                 curVertexState->distance = headVertexState->distance + 1;
 | |
|                 curVertexState->predecessor = new nsCString(*currentHead);
 | |
|                 grayQ.Push(curVertex);
 | |
|             } else {
 | |
|                 delete curVertex; // if this vertex has already been discovered, we don't want
 | |
|                                   // to leak it. (non-discovered vertex's get cleaned up when
 | |
|                                   // they're popped).
 | |
|             }
 | |
|         }
 | |
|         headVertexState->color = black;
 | |
|         nsCString *cur = (nsCString*)grayQ.PopFront();
 | |
|         delete cur;
 | |
|         cur = nullptr;
 | |
|     }
 | |
|     // The shortest path (if any) has been generated and is represented by the chain of
 | |
|     // BFSTableData->predecessor keys. Start at the bottom and work our way up.
 | |
| 
 | |
|     // first parse out the FROM and TO MIME-types being registered.
 | |
| 
 | |
|     nsAutoCString fromStr, toMIMEType;
 | |
|     rv = ParseFromTo(aContractID, fromStr, toMIMEType);
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     // get the root CONTRACTID
 | |
|     nsAutoCString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY);
 | |
|     auto *shortestPath = new nsTArray<nsCString>();
 | |
| 
 | |
|     data = lBFSTable.Get(toMIMEType);
 | |
|     if (!data) {
 | |
|         // If this vertex isn't in the BFSTable, then no-one has registered for it,
 | |
|         // therefore we can't do the conversion.
 | |
|         delete shortestPath;
 | |
|         return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     while (data) {
 | |
|         if (fromStr.Equals(data->key)) {
 | |
|             // found it. We're done here.
 | |
|             *aEdgeList = shortestPath;
 | |
|             return NS_OK;
 | |
|         }
 | |
| 
 | |
|         // reconstruct the CONTRACTID.
 | |
|         // Get the predecessor.
 | |
|         if (!data->predecessor) break; // no predecessor
 | |
|         BFSTableData *predecessorData = lBFSTable.Get(*data->predecessor);
 | |
| 
 | |
|         if (!predecessorData) break; // no predecessor, chain doesn't exist.
 | |
| 
 | |
|         // build out the CONTRACTID.
 | |
|         nsAutoCString newContractID(ContractIDPrefix);
 | |
|         newContractID.AppendLiteral("?from=");
 | |
| 
 | |
|         newContractID.Append(predecessorData->key);
 | |
| 
 | |
|         newContractID.AppendLiteral("&to=");
 | |
|         newContractID.Append(data->key);
 | |
| 
 | |
|         // Add this CONTRACTID to the chain.
 | |
|         rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE;  // XXX this method incorrectly returns a bool
 | |
|         NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed");
 | |
| 
 | |
|         // move up the tree.
 | |
|         data = predecessorData;
 | |
|     }
 | |
|     delete shortestPath;
 | |
|     return NS_ERROR_FAILURE; // couldn't find a stream converter or chain.
 | |
| }
 | |
| 
 | |
| 
 | |
| /////////////////////////////////////////////////////
 | |
| // nsIStreamConverterService methods
 | |
| NS_IMETHODIMP
 | |
| nsStreamConverterService::CanConvert(const char* aFromType,
 | |
|                                      const char* aToType,
 | |
|                                      bool* _retval) {
 | |
|     nsCOMPtr<nsIComponentRegistrar> reg;
 | |
|     nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
 | |
|     if (NS_FAILED(rv))
 | |
|         return rv;
 | |
| 
 | |
|     nsAutoCString contractID;
 | |
|     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
 | |
|     contractID.Append(aFromType);
 | |
|     contractID.AppendLiteral("&to=");
 | |
|     contractID.Append(aToType);
 | |
| 
 | |
|     // See if we have a direct match
 | |
|     rv = reg->IsContractIDRegistered(contractID.get(), _retval);
 | |
|     if (NS_FAILED(rv))
 | |
|         return rv;
 | |
|     if (*_retval)
 | |
|         return NS_OK;
 | |
| 
 | |
|     // Otherwise try the graph.
 | |
|     rv = BuildGraph();
 | |
|     if (NS_FAILED(rv))
 | |
|         return rv;
 | |
| 
 | |
|     nsTArray<nsCString> *converterChain = nullptr;
 | |
|     rv = FindConverter(contractID.get(), &converterChain);
 | |
|     *_retval = NS_SUCCEEDED(rv);
 | |
| 
 | |
|     delete converterChain;
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsStreamConverterService::Convert(nsIInputStream *aFromStream,
 | |
|                                   const char *aFromType,
 | |
|                                   const char *aToType,
 | |
|                                   nsISupports *aContext,
 | |
|                                   nsIInputStream **_retval) {
 | |
|     if (!aFromStream || !aFromType || !aToType || !_retval) return NS_ERROR_NULL_POINTER;
 | |
|     nsresult rv;
 | |
| 
 | |
|     // first determine whether we can even handle this conversion
 | |
|     // build a CONTRACTID
 | |
|     nsAutoCString contractID;
 | |
|     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
 | |
|     contractID.Append(aFromType);
 | |
|     contractID.AppendLiteral("&to=");
 | |
|     contractID.Append(aToType);
 | |
|     const char *cContractID = contractID.get();
 | |
| 
 | |
|     nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv));
 | |
|     if (NS_FAILED(rv)) {
 | |
|         // couldn't go direct, let's try walking the graph of converters.
 | |
|         rv = BuildGraph();
 | |
|         if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|         nsTArray<nsCString> *converterChain = nullptr;
 | |
| 
 | |
|         rv = FindConverter(cContractID, &converterChain);
 | |
|         if (NS_FAILED(rv)) {
 | |
|             // can't make this conversion.
 | |
|             // XXX should have a more descriptive error code.
 | |
|             return NS_ERROR_FAILURE;
 | |
|         }
 | |
| 
 | |
|         int32_t edgeCount = int32_t(converterChain->Length());
 | |
|         NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
 | |
| 
 | |
| 
 | |
|         // convert the stream using each edge of the graph as a step.
 | |
|         // this is our stream conversion traversal.
 | |
|         nsCOMPtr<nsIInputStream> dataToConvert = aFromStream;
 | |
|         nsCOMPtr<nsIInputStream> convertedData;
 | |
| 
 | |
|         for (int32_t i = edgeCount-1; i >= 0; i--) {
 | |
|             const char *lContractID = converterChain->ElementAt(i).get();
 | |
| 
 | |
|             converter = do_CreateInstance(lContractID, &rv);
 | |
| 
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
| 
 | |
|             nsAutoCString fromStr, toStr;
 | |
|             rv = ParseFromTo(lContractID, fromStr, toStr);
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
| 
 | |
|             rv = converter->Convert(dataToConvert, fromStr.get(), toStr.get(), aContext, getter_AddRefs(convertedData));
 | |
|             dataToConvert = convertedData;
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         delete converterChain;
 | |
|         convertedData.forget(_retval);
 | |
|     } else {
 | |
|         // we're going direct.
 | |
|         rv = converter->Convert(aFromStream, aFromType, aToType, aContext, _retval);
 | |
|     }
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsStreamConverterService::AsyncConvertData(const char *aFromType,
 | |
|                                            const char *aToType,
 | |
|                                            nsIStreamListener *aListener,
 | |
|                                            nsISupports *aContext,
 | |
|                                            nsIStreamListener **_retval) {
 | |
|     if (!aFromType || !aToType || !aListener || !_retval) return NS_ERROR_NULL_POINTER;
 | |
| 
 | |
|     nsresult rv;
 | |
| 
 | |
|     // first determine whether we can even handle this conversion
 | |
|     // build a CONTRACTID
 | |
|     nsAutoCString contractID;
 | |
|     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
 | |
|     contractID.Append(aFromType);
 | |
|     contractID.AppendLiteral("&to=");
 | |
|     contractID.Append(aToType);
 | |
|     const char *cContractID = contractID.get();
 | |
| 
 | |
|     nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv));
 | |
|     if (NS_FAILED(rv)) {
 | |
|         // couldn't go direct, let's try walking the graph of converters.
 | |
|         rv = BuildGraph();
 | |
|         if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|         nsTArray<nsCString> *converterChain = nullptr;
 | |
| 
 | |
|         rv = FindConverter(cContractID, &converterChain);
 | |
|         if (NS_FAILED(rv)) {
 | |
|             // can't make this conversion.
 | |
|             // XXX should have a more descriptive error code.
 | |
|             return NS_ERROR_FAILURE;
 | |
|         }
 | |
| 
 | |
|         // aListener is the listener that wants the final, converted, data.
 | |
|         // we initialize finalListener w/ aListener so it gets put at the
 | |
|         // tail end of the chain, which in the loop below, means the *first*
 | |
|         // converter created.
 | |
|         nsCOMPtr<nsIStreamListener> finalListener = aListener;
 | |
| 
 | |
|         // convert the stream using each edge of the graph as a step.
 | |
|         // this is our stream conversion traversal.
 | |
|         int32_t edgeCount = int32_t(converterChain->Length());
 | |
|         NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
 | |
|         for (int i = 0; i < edgeCount; i++) {
 | |
|             const char *lContractID = converterChain->ElementAt(i).get();
 | |
| 
 | |
|             // create the converter for this from/to pair
 | |
|             nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID));
 | |
|             NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't registered");
 | |
| 
 | |
|             nsAutoCString fromStr, toStr;
 | |
|             rv = ParseFromTo(lContractID, fromStr, toStr);
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
| 
 | |
|             // connect the converter w/ the listener that should get the converted data.
 | |
|             rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext);
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
| 
 | |
|             nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv));
 | |
|             if (NS_FAILED(rv)) {
 | |
|                 delete converterChain;
 | |
|                 return rv;
 | |
|             }
 | |
| 
 | |
|             // the last iteration of this loop will result in finalListener
 | |
|             // pointing to the converter that "starts" the conversion chain.
 | |
|             // this converter's "from" type is the original "from" type. Prior
 | |
|             // to the last iteration, finalListener will continuously be wedged
 | |
|             // into the next listener in the chain, then be updated.
 | |
|             finalListener = chainListener;
 | |
|         }
 | |
|         delete converterChain;
 | |
|         // return the first listener in the chain.
 | |
|         finalListener.forget(_retval);
 | |
|     } else {
 | |
|         // we're going direct.
 | |
|         rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
 | |
|         listener.forget(_retval);
 | |
|     }
 | |
| 
 | |
|     return rv;
 | |
| 
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| NS_NewStreamConv(nsStreamConverterService** aStreamConv)
 | |
| {
 | |
|     MOZ_ASSERT(aStreamConv != nullptr, "null ptr");
 | |
|     if (!aStreamConv) return NS_ERROR_NULL_POINTER;
 | |
| 
 | |
|     *aStreamConv = new nsStreamConverterService();
 | |
|     NS_ADDREF(*aStreamConv);
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 |