1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/src/nsStreamConverterService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,604 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + * 1.10 + * 1.11 + * This Original Code has been modified by IBM Corporation. 1.12 + * Modifications made by IBM described herein are 1.13 + * Copyright (c) International Business Machines 1.14 + * Corporation, 2000 1.15 + * 1.16 + * Modifications to Mozilla code or documentation 1.17 + * identified per MPL Section 3.3 1.18 + * 1.19 + * Date Modified by Description of modification 1.20 + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink 1.21 + * use in OS2 1.22 + */ 1.23 + 1.24 +#include "nsStreamConverterService.h" 1.25 +#include "nsIComponentRegistrar.h" 1.26 +#include "nsAutoPtr.h" 1.27 +#include "nsString.h" 1.28 +#include "nsIAtom.h" 1.29 +#include "nsDeque.h" 1.30 +#include "nsIInputStream.h" 1.31 +#include "nsIStreamConverter.h" 1.32 +#include "nsICategoryManager.h" 1.33 +#include "nsXPCOM.h" 1.34 +#include "nsISupportsPrimitives.h" 1.35 +#include "nsCOMArray.h" 1.36 +#include "nsTArray.h" 1.37 +#include "nsServiceManagerUtils.h" 1.38 +#include "nsHashtable.h" 1.39 +#include "nsISimpleEnumerator.h" 1.40 + 1.41 +/////////////////////////////////////////////////////////////////// 1.42 +// Breadth-First-Search (BFS) algorithm state classes and types. 1.43 + 1.44 +// Adjacency list data class. 1.45 +typedef nsCOMArray<nsIAtom> SCTableData; 1.46 + 1.47 +// Delete all the entries in the adjacency list 1.48 +static bool DeleteAdjacencyEntry(nsHashKey *aKey, void *aData, void* closure) { 1.49 + SCTableData *entry = (SCTableData*)aData; 1.50 + delete entry; 1.51 + return true; 1.52 +} 1.53 + 1.54 +// Used to establish discovered verticies. 1.55 +enum BFScolors {white, gray, black}; 1.56 + 1.57 +// BFS hashtable data class. 1.58 +struct BFSTableData { 1.59 + nsCStringKey *key; 1.60 + BFScolors color; 1.61 + int32_t distance; 1.62 + nsAutoPtr<nsCStringKey> predecessor; 1.63 + 1.64 + explicit BFSTableData(nsCStringKey* aKey) 1.65 + : key(aKey), color(white), distance(-1) 1.66 + { 1.67 + } 1.68 +}; 1.69 + 1.70 +//////////////////////////////////////////////////////////// 1.71 +// nsISupports methods 1.72 +NS_IMPL_ISUPPORTS(nsStreamConverterService, nsIStreamConverterService) 1.73 + 1.74 + 1.75 +//////////////////////////////////////////////////////////// 1.76 +// nsIStreamConverterService methods 1.77 + 1.78 +//////////////////////////////////////////////////////////// 1.79 +// nsStreamConverterService methods 1.80 +nsStreamConverterService::nsStreamConverterService() 1.81 + : mAdjacencyList(nullptr, nullptr, DeleteAdjacencyEntry, nullptr) 1.82 +{ 1.83 +} 1.84 + 1.85 +nsStreamConverterService::~nsStreamConverterService() { 1.86 +} 1.87 + 1.88 +// Builds the graph represented as an adjacency list (and built up in 1.89 +// memory using an nsObjectHashtable and nsISupportsArray combination). 1.90 +// 1.91 +// :BuildGraph() consults the category manager for all stream converter 1.92 +// CONTRACTIDS then fills the adjacency list with edges. 1.93 +// An edge in this case is comprised of a FROM and TO MIME type combination. 1.94 +// 1.95 +// CONTRACTID format: 1.96 +// @mozilla.org/streamconv;1?from=text/html&to=text/plain 1.97 +// XXX curently we only handle a single from and to combo, we should repeat the 1.98 +// XXX registration process for any series of from-to combos. 1.99 +// XXX can use nsTokenizer for this. 1.100 +// 1.101 + 1.102 +nsresult 1.103 +nsStreamConverterService::BuildGraph() { 1.104 + 1.105 + nsresult rv; 1.106 + 1.107 + nsCOMPtr<nsICategoryManager> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); 1.108 + if (NS_FAILED(rv)) return rv; 1.109 + 1.110 + nsCOMPtr<nsISimpleEnumerator> entries; 1.111 + rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY, getter_AddRefs(entries)); 1.112 + if (NS_FAILED(rv)) return rv; 1.113 + 1.114 + // go through each entry to build the graph 1.115 + nsCOMPtr<nsISupports> supports; 1.116 + nsCOMPtr<nsISupportsCString> entry; 1.117 + rv = entries->GetNext(getter_AddRefs(supports)); 1.118 + while (NS_SUCCEEDED(rv)) { 1.119 + entry = do_QueryInterface(supports); 1.120 + 1.121 + // get the entry string 1.122 + nsAutoCString entryString; 1.123 + rv = entry->GetData(entryString); 1.124 + if (NS_FAILED(rv)) return rv; 1.125 + 1.126 + // cobble the entry string w/ the converter key to produce a full contractID. 1.127 + nsAutoCString contractID(NS_ISTREAMCONVERTER_KEY); 1.128 + contractID.Append(entryString); 1.129 + 1.130 + // now we've got the CONTRACTID, let's parse it up. 1.131 + rv = AddAdjacency(contractID.get()); 1.132 + if (NS_FAILED(rv)) return rv; 1.133 + 1.134 + rv = entries->GetNext(getter_AddRefs(supports)); 1.135 + } 1.136 + 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 + 1.141 +// XXX currently you can not add the same adjacency (i.e. you can't have multiple 1.142 +// XXX stream converters registering to handle the same from-to combination. It's 1.143 +// XXX not programatically prohibited, it's just that results are un-predictable 1.144 +// XXX right now. 1.145 +nsresult 1.146 +nsStreamConverterService::AddAdjacency(const char *aContractID) { 1.147 + nsresult rv; 1.148 + // first parse out the FROM and TO MIME-types. 1.149 + 1.150 + nsAutoCString fromStr, toStr; 1.151 + rv = ParseFromTo(aContractID, fromStr, toStr); 1.152 + if (NS_FAILED(rv)) return rv; 1.153 + 1.154 + // Each MIME-type is a vertex in the graph, so first lets make sure 1.155 + // each MIME-type is represented as a key in our hashtable. 1.156 + 1.157 + nsCStringKey fromKey(fromStr); 1.158 + SCTableData *fromEdges = (SCTableData*)mAdjacencyList.Get(&fromKey); 1.159 + if (!fromEdges) { 1.160 + // There is no fromStr vertex, create one. 1.161 + fromEdges = new SCTableData(); 1.162 + mAdjacencyList.Put(&fromKey, fromEdges); 1.163 + } 1.164 + 1.165 + nsCStringKey toKey(toStr); 1.166 + if (!mAdjacencyList.Get(&toKey)) { 1.167 + // There is no toStr vertex, create one. 1.168 + mAdjacencyList.Put(&toKey, new SCTableData()); 1.169 + } 1.170 + 1.171 + // Now we know the FROM and TO types are represented as keys in the hashtable. 1.172 + // Let's "connect" the verticies, making an edge. 1.173 + 1.174 + nsCOMPtr<nsIAtom> vertex = do_GetAtom(toStr); 1.175 + if (!vertex) return NS_ERROR_OUT_OF_MEMORY; 1.176 + 1.177 + NS_ASSERTION(fromEdges, "something wrong in adjacency list construction"); 1.178 + if (!fromEdges) 1.179 + return NS_ERROR_FAILURE; 1.180 + 1.181 + return fromEdges->AppendObject(vertex) ? NS_OK : NS_ERROR_FAILURE; 1.182 +} 1.183 + 1.184 +nsresult 1.185 +nsStreamConverterService::ParseFromTo(const char *aContractID, nsCString &aFromRes, nsCString &aToRes) { 1.186 + 1.187 + nsAutoCString ContractIDStr(aContractID); 1.188 + 1.189 + int32_t fromLoc = ContractIDStr.Find("from="); 1.190 + int32_t toLoc = ContractIDStr.Find("to="); 1.191 + if (-1 == fromLoc || -1 == toLoc ) return NS_ERROR_FAILURE; 1.192 + 1.193 + fromLoc = fromLoc + 5; 1.194 + toLoc = toLoc + 3; 1.195 + 1.196 + nsAutoCString fromStr, toStr; 1.197 + 1.198 + ContractIDStr.Mid(fromStr, fromLoc, toLoc - 4 - fromLoc); 1.199 + ContractIDStr.Mid(toStr, toLoc, ContractIDStr.Length() - toLoc); 1.200 + 1.201 + aFromRes.Assign(fromStr); 1.202 + aToRes.Assign(toStr); 1.203 + 1.204 + return NS_OK; 1.205 +} 1.206 + 1.207 +// nsObjectHashtable enumerator functions. 1.208 + 1.209 +// Initializes the BFS state table. 1.210 +static bool InitBFSTable(nsHashKey *aKey, void *aData, void* closure) { 1.211 + NS_ASSERTION((SCTableData*)aData, "no data in the table enumeration"); 1.212 + 1.213 + nsHashtable *BFSTable = (nsHashtable*)closure; 1.214 + if (!BFSTable) return false; 1.215 + 1.216 + BFSTable->Put(aKey, new BFSTableData(static_cast<nsCStringKey*>(aKey))); 1.217 + return true; 1.218 +} 1.219 + 1.220 +// cleans up the BFS state table 1.221 +static bool DeleteBFSEntry(nsHashKey *aKey, void *aData, void *closure) { 1.222 + BFSTableData *data = (BFSTableData*)aData; 1.223 + data->key = nullptr; 1.224 + delete data; 1.225 + return true; 1.226 +} 1.227 + 1.228 +class CStreamConvDeallocator : public nsDequeFunctor { 1.229 +public: 1.230 + virtual void* operator()(void* anObject) { 1.231 + nsCStringKey *key = (nsCStringKey*)anObject; 1.232 + delete key; 1.233 + return 0; 1.234 + } 1.235 +}; 1.236 + 1.237 +// walks the graph using a breadth-first-search algorithm which generates a discovered 1.238 +// verticies tree. This tree is then walked up (from destination vertex, to origin vertex) 1.239 +// and each link in the chain is added to an nsStringArray. A direct lookup for the given 1.240 +// CONTRACTID should be made prior to calling this method in an attempt to find a direct 1.241 +// converter rather than walking the graph. 1.242 +nsresult 1.243 +nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) { 1.244 + nsresult rv; 1.245 + if (!aEdgeList) return NS_ERROR_NULL_POINTER; 1.246 + *aEdgeList = nullptr; 1.247 + 1.248 + // walk the graph in search of the appropriate converter. 1.249 + 1.250 + int32_t vertexCount = mAdjacencyList.Count(); 1.251 + if (0 >= vertexCount) return NS_ERROR_FAILURE; 1.252 + 1.253 + // Create a corresponding color table for each vertex in the graph. 1.254 + nsObjectHashtable lBFSTable(nullptr, nullptr, DeleteBFSEntry, nullptr); 1.255 + mAdjacencyList.Enumerate(InitBFSTable, &lBFSTable); 1.256 + 1.257 + NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem"); 1.258 + 1.259 + // This is our source vertex; our starting point. 1.260 + nsAutoCString fromC, toC; 1.261 + rv = ParseFromTo(aContractID, fromC, toC); 1.262 + if (NS_FAILED(rv)) return rv; 1.263 + 1.264 + nsCStringKey *source = new nsCStringKey(fromC.get()); 1.265 + 1.266 + BFSTableData *data = (BFSTableData*)lBFSTable.Get(source); 1.267 + if (!data) { 1.268 + delete source; 1.269 + return NS_ERROR_FAILURE; 1.270 + } 1.271 + 1.272 + data->color = gray; 1.273 + data->distance = 0; 1.274 + CStreamConvDeallocator *dtorFunc = new CStreamConvDeallocator(); 1.275 + 1.276 + nsDeque grayQ(dtorFunc); 1.277 + 1.278 + // Now generate the shortest path tree. 1.279 + grayQ.Push(source); 1.280 + while (0 < grayQ.GetSize()) { 1.281 + nsCStringKey *currentHead = (nsCStringKey*)grayQ.PeekFront(); 1.282 + SCTableData *data2 = (SCTableData*)mAdjacencyList.Get(currentHead); 1.283 + if (!data2) return NS_ERROR_FAILURE; 1.284 + 1.285 + // Get the state of the current head to calculate the distance of each 1.286 + // reachable vertex in the loop. 1.287 + BFSTableData *headVertexState = (BFSTableData*)lBFSTable.Get(currentHead); 1.288 + if (!headVertexState) return NS_ERROR_FAILURE; 1.289 + 1.290 + int32_t edgeCount = data2->Count(); 1.291 + 1.292 + for (int32_t i = 0; i < edgeCount; i++) { 1.293 + nsIAtom* curVertexAtom = data2->ObjectAt(i); 1.294 + nsAutoString curVertexStr; 1.295 + curVertexAtom->ToString(curVertexStr); 1.296 + nsCStringKey *curVertex = new nsCStringKey(ToNewCString(curVertexStr), 1.297 + curVertexStr.Length(), nsCStringKey::OWN); 1.298 + 1.299 + BFSTableData *curVertexState = (BFSTableData*)lBFSTable.Get(curVertex); 1.300 + if (!curVertexState) { 1.301 + delete curVertex; 1.302 + return NS_ERROR_FAILURE; 1.303 + } 1.304 + 1.305 + if (white == curVertexState->color) { 1.306 + curVertexState->color = gray; 1.307 + curVertexState->distance = headVertexState->distance + 1; 1.308 + curVertexState->predecessor = (nsCStringKey*)currentHead->Clone(); 1.309 + if (!curVertexState->predecessor) { 1.310 + delete curVertex; 1.311 + return NS_ERROR_OUT_OF_MEMORY; 1.312 + } 1.313 + grayQ.Push(curVertex); 1.314 + } else { 1.315 + delete curVertex; // if this vertex has already been discovered, we don't want 1.316 + // to leak it. (non-discovered vertex's get cleaned up when 1.317 + // they're popped). 1.318 + } 1.319 + } 1.320 + headVertexState->color = black; 1.321 + nsCStringKey *cur = (nsCStringKey*)grayQ.PopFront(); 1.322 + delete cur; 1.323 + cur = nullptr; 1.324 + } 1.325 + // The shortest path (if any) has been generated and is represented by the chain of 1.326 + // BFSTableData->predecessor keys. Start at the bottom and work our way up. 1.327 + 1.328 + // first parse out the FROM and TO MIME-types being registered. 1.329 + 1.330 + nsAutoCString fromStr, toStr; 1.331 + rv = ParseFromTo(aContractID, fromStr, toStr); 1.332 + if (NS_FAILED(rv)) return rv; 1.333 + 1.334 + // get the root CONTRACTID 1.335 + nsAutoCString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY); 1.336 + nsTArray<nsCString> *shortestPath = new nsTArray<nsCString>(); 1.337 + 1.338 + nsCStringKey toMIMEType(toStr); 1.339 + data = (BFSTableData*)lBFSTable.Get(&toMIMEType); 1.340 + if (!data) { 1.341 + // If this vertex isn't in the BFSTable, then no-one has registered for it, 1.342 + // therefore we can't do the conversion. 1.343 + delete shortestPath; 1.344 + return NS_ERROR_FAILURE; 1.345 + } 1.346 + 1.347 + while (data) { 1.348 + nsCStringKey *key = data->key; 1.349 + 1.350 + if (fromStr.Equals(key->GetString())) { 1.351 + // found it. We're done here. 1.352 + *aEdgeList = shortestPath; 1.353 + return NS_OK; 1.354 + } 1.355 + 1.356 + // reconstruct the CONTRACTID. 1.357 + // Get the predecessor. 1.358 + if (!data->predecessor) break; // no predecessor 1.359 + BFSTableData *predecessorData = (BFSTableData*)lBFSTable.Get(data->predecessor); 1.360 + 1.361 + if (!predecessorData) break; // no predecessor, chain doesn't exist. 1.362 + 1.363 + // build out the CONTRACTID. 1.364 + nsAutoCString newContractID(ContractIDPrefix); 1.365 + newContractID.AppendLiteral("?from="); 1.366 + 1.367 + nsCStringKey *predecessorKey = predecessorData->key; 1.368 + newContractID.Append(predecessorKey->GetString()); 1.369 + 1.370 + newContractID.AppendLiteral("&to="); 1.371 + newContractID.Append(key->GetString()); 1.372 + 1.373 + // Add this CONTRACTID to the chain. 1.374 + rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool 1.375 + NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed"); 1.376 + 1.377 + // move up the tree. 1.378 + data = predecessorData; 1.379 + } 1.380 + delete shortestPath; 1.381 + return NS_ERROR_FAILURE; // couldn't find a stream converter or chain. 1.382 +} 1.383 + 1.384 + 1.385 +///////////////////////////////////////////////////// 1.386 +// nsIStreamConverterService methods 1.387 +NS_IMETHODIMP 1.388 +nsStreamConverterService::CanConvert(const char* aFromType, 1.389 + const char* aToType, 1.390 + bool* _retval) { 1.391 + nsCOMPtr<nsIComponentRegistrar> reg; 1.392 + nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg)); 1.393 + if (NS_FAILED(rv)) 1.394 + return rv; 1.395 + 1.396 + nsAutoCString contractID; 1.397 + contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from="); 1.398 + contractID.Append(aFromType); 1.399 + contractID.AppendLiteral("&to="); 1.400 + contractID.Append(aToType); 1.401 + 1.402 + // See if we have a direct match 1.403 + rv = reg->IsContractIDRegistered(contractID.get(), _retval); 1.404 + if (NS_FAILED(rv)) 1.405 + return rv; 1.406 + if (*_retval) 1.407 + return NS_OK; 1.408 + 1.409 + // Otherwise try the graph. 1.410 + rv = BuildGraph(); 1.411 + if (NS_FAILED(rv)) 1.412 + return rv; 1.413 + 1.414 + nsTArray<nsCString> *converterChain = nullptr; 1.415 + rv = FindConverter(contractID.get(), &converterChain); 1.416 + *_retval = NS_SUCCEEDED(rv); 1.417 + 1.418 + delete converterChain; 1.419 + return NS_OK; 1.420 +} 1.421 + 1.422 +NS_IMETHODIMP 1.423 +nsStreamConverterService::Convert(nsIInputStream *aFromStream, 1.424 + const char *aFromType, 1.425 + const char *aToType, 1.426 + nsISupports *aContext, 1.427 + nsIInputStream **_retval) { 1.428 + if (!aFromStream || !aFromType || !aToType || !_retval) return NS_ERROR_NULL_POINTER; 1.429 + nsresult rv; 1.430 + 1.431 + // first determine whether we can even handle this conversion 1.432 + // build a CONTRACTID 1.433 + nsAutoCString contractID; 1.434 + contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from="); 1.435 + contractID.Append(aFromType); 1.436 + contractID.AppendLiteral("&to="); 1.437 + contractID.Append(aToType); 1.438 + const char *cContractID = contractID.get(); 1.439 + 1.440 + nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv)); 1.441 + if (NS_FAILED(rv)) { 1.442 + // couldn't go direct, let's try walking the graph of converters. 1.443 + rv = BuildGraph(); 1.444 + if (NS_FAILED(rv)) return rv; 1.445 + 1.446 + nsTArray<nsCString> *converterChain = nullptr; 1.447 + 1.448 + rv = FindConverter(cContractID, &converterChain); 1.449 + if (NS_FAILED(rv)) { 1.450 + // can't make this conversion. 1.451 + // XXX should have a more descriptive error code. 1.452 + return NS_ERROR_FAILURE; 1.453 + } 1.454 + 1.455 + int32_t edgeCount = int32_t(converterChain->Length()); 1.456 + NS_ASSERTION(edgeCount > 0, "findConverter should have failed"); 1.457 + 1.458 + 1.459 + // convert the stream using each edge of the graph as a step. 1.460 + // this is our stream conversion traversal. 1.461 + nsCOMPtr<nsIInputStream> dataToConvert = aFromStream; 1.462 + nsCOMPtr<nsIInputStream> convertedData; 1.463 + 1.464 + for (int32_t i = edgeCount-1; i >= 0; i--) { 1.465 + const char *lContractID = converterChain->ElementAt(i).get(); 1.466 + 1.467 + converter = do_CreateInstance(lContractID, &rv); 1.468 + 1.469 + if (NS_FAILED(rv)) { 1.470 + delete converterChain; 1.471 + return rv; 1.472 + } 1.473 + 1.474 + nsAutoCString fromStr, toStr; 1.475 + rv = ParseFromTo(lContractID, fromStr, toStr); 1.476 + if (NS_FAILED(rv)) { 1.477 + delete converterChain; 1.478 + return rv; 1.479 + } 1.480 + 1.481 + rv = converter->Convert(dataToConvert, fromStr.get(), toStr.get(), aContext, getter_AddRefs(convertedData)); 1.482 + dataToConvert = convertedData; 1.483 + if (NS_FAILED(rv)) { 1.484 + delete converterChain; 1.485 + return rv; 1.486 + } 1.487 + } 1.488 + 1.489 + delete converterChain; 1.490 + *_retval = convertedData; 1.491 + NS_ADDREF(*_retval); 1.492 + 1.493 + } else { 1.494 + // we're going direct. 1.495 + rv = converter->Convert(aFromStream, aFromType, aToType, aContext, _retval); 1.496 + } 1.497 + 1.498 + return rv; 1.499 +} 1.500 + 1.501 + 1.502 +NS_IMETHODIMP 1.503 +nsStreamConverterService::AsyncConvertData(const char *aFromType, 1.504 + const char *aToType, 1.505 + nsIStreamListener *aListener, 1.506 + nsISupports *aContext, 1.507 + nsIStreamListener **_retval) { 1.508 + if (!aFromType || !aToType || !aListener || !_retval) return NS_ERROR_NULL_POINTER; 1.509 + 1.510 + nsresult rv; 1.511 + 1.512 + // first determine whether we can even handle this conversion 1.513 + // build a CONTRACTID 1.514 + nsAutoCString contractID; 1.515 + contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from="); 1.516 + contractID.Append(aFromType); 1.517 + contractID.AppendLiteral("&to="); 1.518 + contractID.Append(aToType); 1.519 + const char *cContractID = contractID.get(); 1.520 + 1.521 + nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv)); 1.522 + if (NS_FAILED(rv)) { 1.523 + // couldn't go direct, let's try walking the graph of converters. 1.524 + rv = BuildGraph(); 1.525 + if (NS_FAILED(rv)) return rv; 1.526 + 1.527 + nsTArray<nsCString> *converterChain = nullptr; 1.528 + 1.529 + rv = FindConverter(cContractID, &converterChain); 1.530 + if (NS_FAILED(rv)) { 1.531 + // can't make this conversion. 1.532 + // XXX should have a more descriptive error code. 1.533 + return NS_ERROR_FAILURE; 1.534 + } 1.535 + 1.536 + // aListener is the listener that wants the final, converted, data. 1.537 + // we initialize finalListener w/ aListener so it gets put at the 1.538 + // tail end of the chain, which in the loop below, means the *first* 1.539 + // converter created. 1.540 + nsCOMPtr<nsIStreamListener> finalListener = aListener; 1.541 + 1.542 + // convert the stream using each edge of the graph as a step. 1.543 + // this is our stream conversion traversal. 1.544 + int32_t edgeCount = int32_t(converterChain->Length()); 1.545 + NS_ASSERTION(edgeCount > 0, "findConverter should have failed"); 1.546 + for (int i = 0; i < edgeCount; i++) { 1.547 + const char *lContractID = converterChain->ElementAt(i).get(); 1.548 + 1.549 + // create the converter for this from/to pair 1.550 + nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID)); 1.551 + NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't registered"); 1.552 + 1.553 + nsAutoCString fromStr, toStr; 1.554 + rv = ParseFromTo(lContractID, fromStr, toStr); 1.555 + if (NS_FAILED(rv)) { 1.556 + delete converterChain; 1.557 + return rv; 1.558 + } 1.559 + 1.560 + // connect the converter w/ the listener that should get the converted data. 1.561 + rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext); 1.562 + if (NS_FAILED(rv)) { 1.563 + delete converterChain; 1.564 + return rv; 1.565 + } 1.566 + 1.567 + nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv)); 1.568 + if (NS_FAILED(rv)) { 1.569 + delete converterChain; 1.570 + return rv; 1.571 + } 1.572 + 1.573 + // the last iteration of this loop will result in finalListener 1.574 + // pointing to the converter that "starts" the conversion chain. 1.575 + // this converter's "from" type is the original "from" type. Prior 1.576 + // to the last iteration, finalListener will continuously be wedged 1.577 + // into the next listener in the chain, then be updated. 1.578 + finalListener = chainListener; 1.579 + } 1.580 + delete converterChain; 1.581 + // return the first listener in the chain. 1.582 + *_retval = finalListener; 1.583 + NS_ADDREF(*_retval); 1.584 + 1.585 + } else { 1.586 + // we're going direct. 1.587 + *_retval = listener; 1.588 + NS_ADDREF(*_retval); 1.589 + 1.590 + rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext); 1.591 + } 1.592 + 1.593 + return rv; 1.594 + 1.595 +} 1.596 + 1.597 +nsresult 1.598 +NS_NewStreamConv(nsStreamConverterService** aStreamConv) 1.599 +{ 1.600 + NS_PRECONDITION(aStreamConv != nullptr, "null ptr"); 1.601 + if (!aStreamConv) return NS_ERROR_NULL_POINTER; 1.602 + 1.603 + *aStreamConv = new nsStreamConverterService(); 1.604 + NS_ADDREF(*aStreamConv); 1.605 + 1.606 + return NS_OK; 1.607 +}