Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / netwerk / streamconv / src / nsStreamConverterService.cpp
blob3b588d4efa362cd7f0bc033b47d509d1bf02523c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK *****
40 * This Original Code has been modified by IBM Corporation.
41 * Modifications made by IBM described herein are
42 * Copyright (c) International Business Machines
43 * Corporation, 2000
45 * Modifications to Mozilla code or documentation
46 * identified per MPL Section 3.3
48 * Date Modified by Description of modification
49 * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
50 * use in OS2
53 #include "nsStreamConverterService.h"
54 #include "nsIServiceManager.h"
55 #include "nsIComponentManager.h"
56 #include "nsIComponentRegistrar.h"
57 #include "nsString.h"
58 #include "nsReadableUtils.h"
59 #include "nsIAtom.h"
60 #include "nsDeque.h"
61 #include "nsIInputStream.h"
62 #include "nsIOutputStream.h"
63 #include "nsIStreamConverter.h"
64 #include "nsICategoryManager.h"
65 #include "nsXPCOM.h"
66 #include "nsISupportsPrimitives.h"
67 #include "nsXPIDLString.h"
69 ////////////////////////////////////////////////////////////
70 // nsISupports methods
71 NS_IMPL_ISUPPORTS1(nsStreamConverterService, nsIStreamConverterService)
74 ////////////////////////////////////////////////////////////
75 // nsIStreamConverterService methods
77 ////////////////////////////////////////////////////////////
78 // nsStreamConverterService methods
79 nsStreamConverterService::nsStreamConverterService() : mAdjacencyList(nsnull) {
82 nsStreamConverterService::~nsStreamConverterService() {
83 NS_ASSERTION(mAdjacencyList, "init wasn't called, or the retval was ignored");
84 delete mAdjacencyList;
87 // Delete all the entries in the adjacency list
88 static PRBool DeleteAdjacencyEntry(nsHashKey *aKey, void *aData, void* closure) {
89 SCTableData *entry = (SCTableData*)aData;
90 NS_ASSERTION(entry->key && entry->data.edges, "malformed adjacency list entry");
91 delete entry->key;
92 delete entry->data.edges;
93 delete entry;
94 return PR_TRUE;
97 nsresult
98 nsStreamConverterService::Init() {
99 mAdjacencyList = new nsObjectHashtable(nsnull, nsnull,
100 DeleteAdjacencyEntry, nsnull);
101 if (!mAdjacencyList) return NS_ERROR_OUT_OF_MEMORY;
102 return NS_OK;
105 // Builds the graph represented as an adjacency list (and built up in
106 // memory using an nsObjectHashtable and nsISupportsArray combination).
108 // :BuildGraph() consults the category manager for all stream converter
109 // CONTRACTIDS then fills the adjacency list with edges.
110 // An edge in this case is comprised of a FROM and TO MIME type combination.
112 // CONTRACTID format:
113 // @mozilla.org/streamconv;1?from=text/html&to=text/plain
114 // XXX curently we only handle a single from and to combo, we should repeat the
115 // XXX registration process for any series of from-to combos.
116 // XXX can use nsTokenizer for this.
119 nsresult
120 nsStreamConverterService::BuildGraph() {
122 nsresult rv;
124 nsCOMPtr<nsICategoryManager> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
125 if (NS_FAILED(rv)) return rv;
127 nsCOMPtr<nsISimpleEnumerator> entries;
128 rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY, getter_AddRefs(entries));
129 if (NS_FAILED(rv)) return rv;
131 // go through each entry to build the graph
132 nsCOMPtr<nsISupportsCString> entry;
133 rv = entries->GetNext(getter_AddRefs(entry));
134 while (NS_SUCCEEDED(rv)) {
136 // get the entry string
137 nsCAutoString entryString;
138 rv = entry->GetData(entryString);
139 if (NS_FAILED(rv)) return rv;
141 // cobble the entry string w/ the converter key to produce a full contractID.
142 nsCAutoString contractID(NS_ISTREAMCONVERTER_KEY);
143 contractID.Append(entryString);
145 // now we've got the CONTRACTID, let's parse it up.
146 rv = AddAdjacency(contractID.get());
147 if (NS_FAILED(rv)) return rv;
149 rv = entries->GetNext(getter_AddRefs(entry));
152 return NS_OK;
156 // XXX currently you can not add the same adjacency (i.e. you can't have multiple
157 // XXX stream converters registering to handle the same from-to combination. It's
158 // XXX not programatically prohibited, it's just that results are un-predictable
159 // XXX right now.
160 nsresult
161 nsStreamConverterService::AddAdjacency(const char *aContractID) {
162 nsresult rv;
163 // first parse out the FROM and TO MIME-types.
165 nsCAutoString fromStr, toStr;
166 rv = ParseFromTo(aContractID, fromStr, toStr);
167 if (NS_FAILED(rv)) return rv;
169 // Each MIME-type is a vertex in the graph, so first lets make sure
170 // each MIME-type is represented as a key in our hashtable.
172 nsCStringKey fromKey(fromStr);
173 SCTableData *fromEdges = (SCTableData*)mAdjacencyList->Get(&fromKey);
174 if (!fromEdges) {
175 // There is no fromStr vertex, create one.
177 nsCStringKey *newFromKey = new nsCStringKey(ToNewCString(fromStr), fromStr.Length(), nsCStringKey::OWN);
178 if (!newFromKey) return NS_ERROR_OUT_OF_MEMORY;
180 SCTableData *data = new SCTableData(newFromKey);
181 if (!data) {
182 delete newFromKey;
183 return NS_ERROR_OUT_OF_MEMORY;
186 nsCOMArray<nsIAtom>* edgeArray = new nsCOMArray<nsIAtom>;
187 if (!edgeArray) {
188 delete newFromKey;
189 data->key = nsnull;
190 delete data;
191 return NS_ERROR_OUT_OF_MEMORY;
193 data->data.edges = edgeArray;
195 mAdjacencyList->Put(newFromKey, data);
196 fromEdges = data;
199 nsCStringKey toKey(toStr);
200 if (!mAdjacencyList->Get(&toKey)) {
201 // There is no toStr vertex, create one.
202 nsCStringKey *newToKey = new nsCStringKey(ToNewCString(toStr), toStr.Length(), nsCStringKey::OWN);
203 if (!newToKey) return NS_ERROR_OUT_OF_MEMORY;
205 SCTableData *data = new SCTableData(newToKey);
206 if (!data) {
207 delete newToKey;
208 return NS_ERROR_OUT_OF_MEMORY;
211 nsCOMArray<nsIAtom>* edgeArray = new nsCOMArray<nsIAtom>;
212 if (!edgeArray) {
213 delete newToKey;
214 data->key = nsnull;
215 delete data;
216 return NS_ERROR_OUT_OF_MEMORY;
218 data->data.edges = edgeArray;
219 mAdjacencyList->Put(newToKey, data);
222 // Now we know the FROM and TO types are represented as keys in the hashtable.
223 // Let's "connect" the verticies, making an edge.
225 nsCOMPtr<nsIAtom> vertex = do_GetAtom(toStr);
226 if (!vertex) return NS_ERROR_OUT_OF_MEMORY;
228 NS_ASSERTION(fromEdges, "something wrong in adjacency list construction");
229 if (!fromEdges)
230 return NS_ERROR_FAILURE;
232 nsCOMArray<nsIAtom> *adjacencyList = fromEdges->data.edges;
233 return adjacencyList->AppendObject(vertex) ? NS_OK : NS_ERROR_FAILURE;
236 nsresult
237 nsStreamConverterService::ParseFromTo(const char *aContractID, nsCString &aFromRes, nsCString &aToRes) {
239 nsCAutoString ContractIDStr(aContractID);
241 PRInt32 fromLoc = ContractIDStr.Find("from=");
242 PRInt32 toLoc = ContractIDStr.Find("to=");
243 if (-1 == fromLoc || -1 == toLoc ) return NS_ERROR_FAILURE;
245 fromLoc = fromLoc + 5;
246 toLoc = toLoc + 3;
248 nsCAutoString fromStr, toStr;
250 ContractIDStr.Mid(fromStr, fromLoc, toLoc - 4 - fromLoc);
251 ContractIDStr.Mid(toStr, toLoc, ContractIDStr.Length() - toLoc);
253 aFromRes.Assign(fromStr);
254 aToRes.Assign(toStr);
256 return NS_OK;
259 // nsObjectHashtable enumerator functions.
261 // Initializes the BFS state table.
262 static PRBool InitBFSTable(nsHashKey *aKey, void *aData, void* closure) {
263 NS_ASSERTION((SCTableData*)aData, "no data in the table enumeration");
265 nsHashtable *BFSTable = (nsHashtable*)closure;
266 if (!BFSTable) return PR_FALSE;
268 BFSState *state = new BFSState;
269 if (!state) return PR_FALSE;
271 state->color = white;
272 state->distance = -1;
273 state->predecessor = nsnull;
275 SCTableData *data = new SCTableData(static_cast<nsCStringKey*>(aKey));
276 if (!data) {
277 delete state;
278 return PR_FALSE;
280 data->data.state = state;
282 BFSTable->Put(aKey, data);
283 return PR_TRUE;
286 // cleans up the BFS state table
287 static PRBool DeleteBFSEntry(nsHashKey *aKey, void *aData, void *closure) {
288 SCTableData *data = (SCTableData*)aData;
289 BFSState *state = data->data.state;
290 delete state;
291 data->key = nsnull;
292 delete data;
293 return PR_TRUE;
296 class CStreamConvDeallocator : public nsDequeFunctor {
297 public:
298 virtual void* operator()(void* anObject) {
299 nsCStringKey *key = (nsCStringKey*)anObject;
300 delete key;
301 return 0;
305 // walks the graph using a breadth-first-search algorithm which generates a discovered
306 // verticies tree. This tree is then walked up (from destination vertex, to origin vertex)
307 // and each link in the chain is added to an nsStringArray. A direct lookup for the given
308 // CONTRACTID should be made prior to calling this method in an attempt to find a direct
309 // converter rather than walking the graph.
310 nsresult
311 nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) {
312 nsresult rv;
313 if (!aEdgeList) return NS_ERROR_NULL_POINTER;
314 *aEdgeList = nsnull;
316 // walk the graph in search of the appropriate converter.
318 PRInt32 vertexCount = mAdjacencyList->Count();
319 if (0 >= vertexCount) return NS_ERROR_FAILURE;
321 // Create a corresponding color table for each vertex in the graph.
322 nsObjectHashtable lBFSTable(nsnull, nsnull, DeleteBFSEntry, nsnull);
323 mAdjacencyList->Enumerate(InitBFSTable, &lBFSTable);
325 NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem");
327 // This is our source vertex; our starting point.
328 nsCAutoString fromC, toC;
329 rv = ParseFromTo(aContractID, fromC, toC);
330 if (NS_FAILED(rv)) return rv;
332 nsCStringKey *source = new nsCStringKey(fromC.get());
333 if (!source) return NS_ERROR_OUT_OF_MEMORY;
335 SCTableData *data = (SCTableData*)lBFSTable.Get(source);
336 if (!data) {
337 delete source;
338 return NS_ERROR_FAILURE;
341 BFSState *state = data->data.state;
343 state->color = gray;
344 state->distance = 0;
345 CStreamConvDeallocator *dtorFunc = new CStreamConvDeallocator();
346 if (!dtorFunc) {
347 delete source;
348 return NS_ERROR_OUT_OF_MEMORY;
351 nsDeque grayQ(dtorFunc);
353 // Now generate the shortest path tree.
354 grayQ.Push(source);
355 while (0 < grayQ.GetSize()) {
356 nsCStringKey *currentHead = (nsCStringKey*)grayQ.PeekFront();
357 SCTableData *data2 = (SCTableData*)mAdjacencyList->Get(currentHead);
358 if (!data2) return NS_ERROR_FAILURE;
360 nsCOMArray<nsIAtom> *edges = data2->data.edges;
361 NS_ASSERTION(edges, "something went wrong with BFS strmconv algorithm");
362 if (!edges) return NS_ERROR_FAILURE;
364 // Get the state of the current head to calculate the distance of each
365 // reachable vertex in the loop.
366 data2 = (SCTableData*)lBFSTable.Get(currentHead);
367 if (!data2) return NS_ERROR_FAILURE;
369 BFSState *headVertexState = data2->data.state;
370 NS_ASSERTION(headVertexState, "problem with the BFS strmconv algorithm");
371 if (!headVertexState) return NS_ERROR_FAILURE;
373 PRInt32 edgeCount = edges->Count();
375 for (PRInt32 i = 0; i < edgeCount; i++) {
376 nsIAtom* curVertexAtom = edges->ObjectAt(i);
377 nsAutoString curVertexStr;
378 curVertexAtom->ToString(curVertexStr);
379 nsCStringKey *curVertex = new nsCStringKey(ToNewCString(curVertexStr),
380 curVertexStr.Length(), nsCStringKey::OWN);
381 if (!curVertex) return NS_ERROR_OUT_OF_MEMORY;
383 SCTableData *data3 = (SCTableData*)lBFSTable.Get(curVertex);
384 if (!data3) {
385 delete curVertex;
386 return NS_ERROR_FAILURE;
388 BFSState *curVertexState = data3->data.state;
389 NS_ASSERTION(curVertexState, "something went wrong with the BFS strmconv algorithm");
390 if (!curVertexState) return NS_ERROR_FAILURE;
392 if (white == curVertexState->color) {
393 curVertexState->color = gray;
394 curVertexState->distance = headVertexState->distance + 1;
395 curVertexState->predecessor = (nsCStringKey*)currentHead->Clone();
396 if (!curVertexState->predecessor) {
397 delete curVertex;
398 return NS_ERROR_OUT_OF_MEMORY;
400 grayQ.Push(curVertex);
401 } else {
402 delete curVertex; // if this vertex has already been discovered, we don't want
403 // to leak it. (non-discovered vertex's get cleaned up when
404 // they're popped).
407 headVertexState->color = black;
408 nsCStringKey *cur = (nsCStringKey*)grayQ.PopFront();
409 delete cur;
410 cur = nsnull;
412 // The shortest path (if any) has been generated and is represetned by the chain of
413 // BFSState->predecessor keys. Start at the bottom and work our way up.
415 // first parse out the FROM and TO MIME-types being registered.
417 nsCAutoString fromStr, toStr;
418 rv = ParseFromTo(aContractID, fromStr, toStr);
419 if (NS_FAILED(rv)) return rv;
421 // get the root CONTRACTID
422 nsCAutoString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY);
423 nsTArray<nsCString> *shortestPath = new nsTArray<nsCString>();
424 if (!shortestPath) return NS_ERROR_OUT_OF_MEMORY;
426 nsCStringKey toMIMEType(toStr);
427 data = (SCTableData*)lBFSTable.Get(&toMIMEType);
428 if (!data) {
429 // If this vertex isn't in the BFSTable, then no-one has registered for it,
430 // therefore we can't do the conversion.
431 delete shortestPath;
432 return NS_ERROR_FAILURE;
435 while (data) {
436 BFSState *curState = data->data.state;
438 nsCStringKey *key = data->key;
440 if (fromStr.Equals(key->GetString())) {
441 // found it. We're done here.
442 *aEdgeList = shortestPath;
443 return NS_OK;
446 // reconstruct the CONTRACTID.
447 // Get the predecessor.
448 if (!curState->predecessor) break; // no predecessor
449 SCTableData *predecessorData = (SCTableData*)lBFSTable.Get(curState->predecessor);
451 if (!predecessorData) break; // no predecessor, chain doesn't exist.
453 // build out the CONTRACTID.
454 nsCAutoString newContractID(ContractIDPrefix);
455 newContractID.AppendLiteral("?from=");
457 nsCStringKey *predecessorKey = predecessorData->key;
458 newContractID.Append(predecessorKey->GetString());
460 newContractID.AppendLiteral("&to=");
461 newContractID.Append(key->GetString());
463 // Add this CONTRACTID to the chain.
464 rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool
465 NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed");
467 // move up the tree.
468 data = predecessorData;
470 delete shortestPath;
471 return NS_ERROR_FAILURE; // couldn't find a stream converter or chain.
475 /////////////////////////////////////////////////////
476 // nsIStreamConverterService methods
477 NS_IMETHODIMP
478 nsStreamConverterService::CanConvert(const char* aFromType,
479 const char* aToType,
480 PRBool* _retval) {
481 nsCOMPtr<nsIComponentRegistrar> reg;
482 nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
483 if (NS_FAILED(rv))
484 return rv;
486 nsCAutoString contractID;
487 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
488 contractID.Append(aFromType);
489 contractID.AppendLiteral("&to=");
490 contractID.Append(aToType);
492 // See if we have a direct match
493 rv = reg->IsContractIDRegistered(contractID.get(), _retval);
494 if (NS_FAILED(rv))
495 return rv;
496 if (*_retval)
497 return NS_OK;
499 // Otherwise try the graph.
500 rv = BuildGraph();
501 if (NS_FAILED(rv))
502 return rv;
504 nsTArray<nsCString> *converterChain = nsnull;
505 rv = FindConverter(contractID.get(), &converterChain);
506 *_retval = NS_SUCCEEDED(rv);
508 delete converterChain;
509 return NS_OK;
512 NS_IMETHODIMP
513 nsStreamConverterService::Convert(nsIInputStream *aFromStream,
514 const char *aFromType,
515 const char *aToType,
516 nsISupports *aContext,
517 nsIInputStream **_retval) {
518 if (!aFromStream || !aFromType || !aToType || !_retval) return NS_ERROR_NULL_POINTER;
519 nsresult rv;
521 // first determine whether we can even handle this conversion
522 // build a CONTRACTID
523 nsCAutoString contractID;
524 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
525 contractID.Append(aFromType);
526 contractID.AppendLiteral("&to=");
527 contractID.Append(aToType);
528 const char *cContractID = contractID.get();
530 nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv));
531 if (NS_FAILED(rv)) {
532 // couldn't go direct, let's try walking the graph of converters.
533 rv = BuildGraph();
534 if (NS_FAILED(rv)) return rv;
536 nsTArray<nsCString> *converterChain = nsnull;
538 rv = FindConverter(cContractID, &converterChain);
539 if (NS_FAILED(rv)) {
540 // can't make this conversion.
541 // XXX should have a more descriptive error code.
542 return NS_ERROR_FAILURE;
545 PRInt32 edgeCount = PRInt32(converterChain->Length());
546 NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
549 // convert the stream using each edge of the graph as a step.
550 // this is our stream conversion traversal.
551 nsCOMPtr<nsIInputStream> dataToConvert = aFromStream;
552 nsCOMPtr<nsIInputStream> convertedData;
554 for (PRInt32 i = edgeCount-1; i >= 0; i--) {
555 const char *lContractID = converterChain->ElementAt(i).get();
557 converter = do_CreateInstance(lContractID, &rv);
559 if (NS_FAILED(rv)) {
560 delete converterChain;
561 return rv;
564 nsCAutoString fromStr, toStr;
565 rv = ParseFromTo(lContractID, fromStr, toStr);
566 if (NS_FAILED(rv)) {
567 delete converterChain;
568 return rv;
571 rv = converter->Convert(dataToConvert, fromStr.get(), toStr.get(), aContext, getter_AddRefs(convertedData));
572 dataToConvert = convertedData;
573 if (NS_FAILED(rv)) {
574 delete converterChain;
575 return rv;
579 delete converterChain;
580 *_retval = convertedData;
581 NS_ADDREF(*_retval);
583 } else {
584 // we're going direct.
585 rv = converter->Convert(aFromStream, aFromType, aToType, aContext, _retval);
588 return rv;
592 NS_IMETHODIMP
593 nsStreamConverterService::AsyncConvertData(const char *aFromType,
594 const char *aToType,
595 nsIStreamListener *aListener,
596 nsISupports *aContext,
597 nsIStreamListener **_retval) {
598 if (!aFromType || !aToType || !aListener || !_retval) return NS_ERROR_NULL_POINTER;
600 nsresult rv;
602 // first determine whether we can even handle this conversion
603 // build a CONTRACTID
604 nsCAutoString contractID;
605 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
606 contractID.Append(aFromType);
607 contractID.AppendLiteral("&to=");
608 contractID.Append(aToType);
609 const char *cContractID = contractID.get();
611 nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv));
612 if (NS_FAILED(rv)) {
613 // couldn't go direct, let's try walking the graph of converters.
614 rv = BuildGraph();
615 if (NS_FAILED(rv)) return rv;
617 nsTArray<nsCString> *converterChain = nsnull;
619 rv = FindConverter(cContractID, &converterChain);
620 if (NS_FAILED(rv)) {
621 // can't make this conversion.
622 // XXX should have a more descriptive error code.
623 return NS_ERROR_FAILURE;
626 // aListener is the listener that wants the final, converted, data.
627 // we initialize finalListener w/ aListener so it gets put at the
628 // tail end of the chain, which in the loop below, means the *first*
629 // converter created.
630 nsCOMPtr<nsIStreamListener> finalListener = aListener;
632 // convert the stream using each edge of the graph as a step.
633 // this is our stream conversion traversal.
634 PRInt32 edgeCount = PRInt32(converterChain->Length());
635 NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
636 for (int i = 0; i < edgeCount; i++) {
637 const char *lContractID = converterChain->ElementAt(i).get();
639 // create the converter for this from/to pair
640 nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID));
641 NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't registered");
643 nsCAutoString fromStr, toStr;
644 rv = ParseFromTo(lContractID, fromStr, toStr);
645 if (NS_FAILED(rv)) {
646 delete converterChain;
647 return rv;
650 // connect the converter w/ the listener that should get the converted data.
651 rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext);
652 if (NS_FAILED(rv)) {
653 delete converterChain;
654 return rv;
657 nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv));
658 if (NS_FAILED(rv)) {
659 delete converterChain;
660 return rv;
663 // the last iteration of this loop will result in finalListener
664 // pointing to the converter that "starts" the conversion chain.
665 // this converter's "from" type is the original "from" type. Prior
666 // to the last iteration, finalListener will continuously be wedged
667 // into the next listener in the chain, then be updated.
668 finalListener = chainListener;
670 delete converterChain;
671 // return the first listener in the chain.
672 *_retval = finalListener;
673 NS_ADDREF(*_retval);
675 } else {
676 // we're going direct.
677 *_retval = listener;
678 NS_ADDREF(*_retval);
680 rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
683 return rv;
687 nsresult
688 NS_NewStreamConv(nsStreamConverterService** aStreamConv)
690 NS_PRECONDITION(aStreamConv != nsnull, "null ptr");
691 if (!aStreamConv) return NS_ERROR_NULL_POINTER;
693 *aStreamConv = new nsStreamConverterService();
694 if (!*aStreamConv) return NS_ERROR_OUT_OF_MEMORY;
696 NS_ADDREF(*aStreamConv);
697 nsresult rv = (*aStreamConv)->Init();
698 if (NS_FAILED(rv))
699 NS_RELEASE(*aStreamConv);
701 return rv;