netwerk/streamconv/src/nsStreamConverterService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6 *
michael@0 7 *
michael@0 8 * This Original Code has been modified by IBM Corporation.
michael@0 9 * Modifications made by IBM described herein are
michael@0 10 * Copyright (c) International Business Machines
michael@0 11 * Corporation, 2000
michael@0 12 *
michael@0 13 * Modifications to Mozilla code or documentation
michael@0 14 * identified per MPL Section 3.3
michael@0 15 *
michael@0 16 * Date Modified by Description of modification
michael@0 17 * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
michael@0 18 * use in OS2
michael@0 19 */
michael@0 20
michael@0 21 #include "nsStreamConverterService.h"
michael@0 22 #include "nsIComponentRegistrar.h"
michael@0 23 #include "nsAutoPtr.h"
michael@0 24 #include "nsString.h"
michael@0 25 #include "nsIAtom.h"
michael@0 26 #include "nsDeque.h"
michael@0 27 #include "nsIInputStream.h"
michael@0 28 #include "nsIStreamConverter.h"
michael@0 29 #include "nsICategoryManager.h"
michael@0 30 #include "nsXPCOM.h"
michael@0 31 #include "nsISupportsPrimitives.h"
michael@0 32 #include "nsCOMArray.h"
michael@0 33 #include "nsTArray.h"
michael@0 34 #include "nsServiceManagerUtils.h"
michael@0 35 #include "nsHashtable.h"
michael@0 36 #include "nsISimpleEnumerator.h"
michael@0 37
michael@0 38 ///////////////////////////////////////////////////////////////////
michael@0 39 // Breadth-First-Search (BFS) algorithm state classes and types.
michael@0 40
michael@0 41 // Adjacency list data class.
michael@0 42 typedef nsCOMArray<nsIAtom> SCTableData;
michael@0 43
michael@0 44 // Delete all the entries in the adjacency list
michael@0 45 static bool DeleteAdjacencyEntry(nsHashKey *aKey, void *aData, void* closure) {
michael@0 46 SCTableData *entry = (SCTableData*)aData;
michael@0 47 delete entry;
michael@0 48 return true;
michael@0 49 }
michael@0 50
michael@0 51 // Used to establish discovered verticies.
michael@0 52 enum BFScolors {white, gray, black};
michael@0 53
michael@0 54 // BFS hashtable data class.
michael@0 55 struct BFSTableData {
michael@0 56 nsCStringKey *key;
michael@0 57 BFScolors color;
michael@0 58 int32_t distance;
michael@0 59 nsAutoPtr<nsCStringKey> predecessor;
michael@0 60
michael@0 61 explicit BFSTableData(nsCStringKey* aKey)
michael@0 62 : key(aKey), color(white), distance(-1)
michael@0 63 {
michael@0 64 }
michael@0 65 };
michael@0 66
michael@0 67 ////////////////////////////////////////////////////////////
michael@0 68 // nsISupports methods
michael@0 69 NS_IMPL_ISUPPORTS(nsStreamConverterService, nsIStreamConverterService)
michael@0 70
michael@0 71
michael@0 72 ////////////////////////////////////////////////////////////
michael@0 73 // nsIStreamConverterService methods
michael@0 74
michael@0 75 ////////////////////////////////////////////////////////////
michael@0 76 // nsStreamConverterService methods
michael@0 77 nsStreamConverterService::nsStreamConverterService()
michael@0 78 : mAdjacencyList(nullptr, nullptr, DeleteAdjacencyEntry, nullptr)
michael@0 79 {
michael@0 80 }
michael@0 81
michael@0 82 nsStreamConverterService::~nsStreamConverterService() {
michael@0 83 }
michael@0 84
michael@0 85 // Builds the graph represented as an adjacency list (and built up in
michael@0 86 // memory using an nsObjectHashtable and nsISupportsArray combination).
michael@0 87 //
michael@0 88 // :BuildGraph() consults the category manager for all stream converter
michael@0 89 // CONTRACTIDS then fills the adjacency list with edges.
michael@0 90 // An edge in this case is comprised of a FROM and TO MIME type combination.
michael@0 91 //
michael@0 92 // CONTRACTID format:
michael@0 93 // @mozilla.org/streamconv;1?from=text/html&to=text/plain
michael@0 94 // XXX curently we only handle a single from and to combo, we should repeat the
michael@0 95 // XXX registration process for any series of from-to combos.
michael@0 96 // XXX can use nsTokenizer for this.
michael@0 97 //
michael@0 98
michael@0 99 nsresult
michael@0 100 nsStreamConverterService::BuildGraph() {
michael@0 101
michael@0 102 nsresult rv;
michael@0 103
michael@0 104 nsCOMPtr<nsICategoryManager> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
michael@0 105 if (NS_FAILED(rv)) return rv;
michael@0 106
michael@0 107 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 108 rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY, getter_AddRefs(entries));
michael@0 109 if (NS_FAILED(rv)) return rv;
michael@0 110
michael@0 111 // go through each entry to build the graph
michael@0 112 nsCOMPtr<nsISupports> supports;
michael@0 113 nsCOMPtr<nsISupportsCString> entry;
michael@0 114 rv = entries->GetNext(getter_AddRefs(supports));
michael@0 115 while (NS_SUCCEEDED(rv)) {
michael@0 116 entry = do_QueryInterface(supports);
michael@0 117
michael@0 118 // get the entry string
michael@0 119 nsAutoCString entryString;
michael@0 120 rv = entry->GetData(entryString);
michael@0 121 if (NS_FAILED(rv)) return rv;
michael@0 122
michael@0 123 // cobble the entry string w/ the converter key to produce a full contractID.
michael@0 124 nsAutoCString contractID(NS_ISTREAMCONVERTER_KEY);
michael@0 125 contractID.Append(entryString);
michael@0 126
michael@0 127 // now we've got the CONTRACTID, let's parse it up.
michael@0 128 rv = AddAdjacency(contractID.get());
michael@0 129 if (NS_FAILED(rv)) return rv;
michael@0 130
michael@0 131 rv = entries->GetNext(getter_AddRefs(supports));
michael@0 132 }
michael@0 133
michael@0 134 return NS_OK;
michael@0 135 }
michael@0 136
michael@0 137
michael@0 138 // XXX currently you can not add the same adjacency (i.e. you can't have multiple
michael@0 139 // XXX stream converters registering to handle the same from-to combination. It's
michael@0 140 // XXX not programatically prohibited, it's just that results are un-predictable
michael@0 141 // XXX right now.
michael@0 142 nsresult
michael@0 143 nsStreamConverterService::AddAdjacency(const char *aContractID) {
michael@0 144 nsresult rv;
michael@0 145 // first parse out the FROM and TO MIME-types.
michael@0 146
michael@0 147 nsAutoCString fromStr, toStr;
michael@0 148 rv = ParseFromTo(aContractID, fromStr, toStr);
michael@0 149 if (NS_FAILED(rv)) return rv;
michael@0 150
michael@0 151 // Each MIME-type is a vertex in the graph, so first lets make sure
michael@0 152 // each MIME-type is represented as a key in our hashtable.
michael@0 153
michael@0 154 nsCStringKey fromKey(fromStr);
michael@0 155 SCTableData *fromEdges = (SCTableData*)mAdjacencyList.Get(&fromKey);
michael@0 156 if (!fromEdges) {
michael@0 157 // There is no fromStr vertex, create one.
michael@0 158 fromEdges = new SCTableData();
michael@0 159 mAdjacencyList.Put(&fromKey, fromEdges);
michael@0 160 }
michael@0 161
michael@0 162 nsCStringKey toKey(toStr);
michael@0 163 if (!mAdjacencyList.Get(&toKey)) {
michael@0 164 // There is no toStr vertex, create one.
michael@0 165 mAdjacencyList.Put(&toKey, new SCTableData());
michael@0 166 }
michael@0 167
michael@0 168 // Now we know the FROM and TO types are represented as keys in the hashtable.
michael@0 169 // Let's "connect" the verticies, making an edge.
michael@0 170
michael@0 171 nsCOMPtr<nsIAtom> vertex = do_GetAtom(toStr);
michael@0 172 if (!vertex) return NS_ERROR_OUT_OF_MEMORY;
michael@0 173
michael@0 174 NS_ASSERTION(fromEdges, "something wrong in adjacency list construction");
michael@0 175 if (!fromEdges)
michael@0 176 return NS_ERROR_FAILURE;
michael@0 177
michael@0 178 return fromEdges->AppendObject(vertex) ? NS_OK : NS_ERROR_FAILURE;
michael@0 179 }
michael@0 180
michael@0 181 nsresult
michael@0 182 nsStreamConverterService::ParseFromTo(const char *aContractID, nsCString &aFromRes, nsCString &aToRes) {
michael@0 183
michael@0 184 nsAutoCString ContractIDStr(aContractID);
michael@0 185
michael@0 186 int32_t fromLoc = ContractIDStr.Find("from=");
michael@0 187 int32_t toLoc = ContractIDStr.Find("to=");
michael@0 188 if (-1 == fromLoc || -1 == toLoc ) return NS_ERROR_FAILURE;
michael@0 189
michael@0 190 fromLoc = fromLoc + 5;
michael@0 191 toLoc = toLoc + 3;
michael@0 192
michael@0 193 nsAutoCString fromStr, toStr;
michael@0 194
michael@0 195 ContractIDStr.Mid(fromStr, fromLoc, toLoc - 4 - fromLoc);
michael@0 196 ContractIDStr.Mid(toStr, toLoc, ContractIDStr.Length() - toLoc);
michael@0 197
michael@0 198 aFromRes.Assign(fromStr);
michael@0 199 aToRes.Assign(toStr);
michael@0 200
michael@0 201 return NS_OK;
michael@0 202 }
michael@0 203
michael@0 204 // nsObjectHashtable enumerator functions.
michael@0 205
michael@0 206 // Initializes the BFS state table.
michael@0 207 static bool InitBFSTable(nsHashKey *aKey, void *aData, void* closure) {
michael@0 208 NS_ASSERTION((SCTableData*)aData, "no data in the table enumeration");
michael@0 209
michael@0 210 nsHashtable *BFSTable = (nsHashtable*)closure;
michael@0 211 if (!BFSTable) return false;
michael@0 212
michael@0 213 BFSTable->Put(aKey, new BFSTableData(static_cast<nsCStringKey*>(aKey)));
michael@0 214 return true;
michael@0 215 }
michael@0 216
michael@0 217 // cleans up the BFS state table
michael@0 218 static bool DeleteBFSEntry(nsHashKey *aKey, void *aData, void *closure) {
michael@0 219 BFSTableData *data = (BFSTableData*)aData;
michael@0 220 data->key = nullptr;
michael@0 221 delete data;
michael@0 222 return true;
michael@0 223 }
michael@0 224
michael@0 225 class CStreamConvDeallocator : public nsDequeFunctor {
michael@0 226 public:
michael@0 227 virtual void* operator()(void* anObject) {
michael@0 228 nsCStringKey *key = (nsCStringKey*)anObject;
michael@0 229 delete key;
michael@0 230 return 0;
michael@0 231 }
michael@0 232 };
michael@0 233
michael@0 234 // walks the graph using a breadth-first-search algorithm which generates a discovered
michael@0 235 // verticies tree. This tree is then walked up (from destination vertex, to origin vertex)
michael@0 236 // and each link in the chain is added to an nsStringArray. A direct lookup for the given
michael@0 237 // CONTRACTID should be made prior to calling this method in an attempt to find a direct
michael@0 238 // converter rather than walking the graph.
michael@0 239 nsresult
michael@0 240 nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) {
michael@0 241 nsresult rv;
michael@0 242 if (!aEdgeList) return NS_ERROR_NULL_POINTER;
michael@0 243 *aEdgeList = nullptr;
michael@0 244
michael@0 245 // walk the graph in search of the appropriate converter.
michael@0 246
michael@0 247 int32_t vertexCount = mAdjacencyList.Count();
michael@0 248 if (0 >= vertexCount) return NS_ERROR_FAILURE;
michael@0 249
michael@0 250 // Create a corresponding color table for each vertex in the graph.
michael@0 251 nsObjectHashtable lBFSTable(nullptr, nullptr, DeleteBFSEntry, nullptr);
michael@0 252 mAdjacencyList.Enumerate(InitBFSTable, &lBFSTable);
michael@0 253
michael@0 254 NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem");
michael@0 255
michael@0 256 // This is our source vertex; our starting point.
michael@0 257 nsAutoCString fromC, toC;
michael@0 258 rv = ParseFromTo(aContractID, fromC, toC);
michael@0 259 if (NS_FAILED(rv)) return rv;
michael@0 260
michael@0 261 nsCStringKey *source = new nsCStringKey(fromC.get());
michael@0 262
michael@0 263 BFSTableData *data = (BFSTableData*)lBFSTable.Get(source);
michael@0 264 if (!data) {
michael@0 265 delete source;
michael@0 266 return NS_ERROR_FAILURE;
michael@0 267 }
michael@0 268
michael@0 269 data->color = gray;
michael@0 270 data->distance = 0;
michael@0 271 CStreamConvDeallocator *dtorFunc = new CStreamConvDeallocator();
michael@0 272
michael@0 273 nsDeque grayQ(dtorFunc);
michael@0 274
michael@0 275 // Now generate the shortest path tree.
michael@0 276 grayQ.Push(source);
michael@0 277 while (0 < grayQ.GetSize()) {
michael@0 278 nsCStringKey *currentHead = (nsCStringKey*)grayQ.PeekFront();
michael@0 279 SCTableData *data2 = (SCTableData*)mAdjacencyList.Get(currentHead);
michael@0 280 if (!data2) return NS_ERROR_FAILURE;
michael@0 281
michael@0 282 // Get the state of the current head to calculate the distance of each
michael@0 283 // reachable vertex in the loop.
michael@0 284 BFSTableData *headVertexState = (BFSTableData*)lBFSTable.Get(currentHead);
michael@0 285 if (!headVertexState) return NS_ERROR_FAILURE;
michael@0 286
michael@0 287 int32_t edgeCount = data2->Count();
michael@0 288
michael@0 289 for (int32_t i = 0; i < edgeCount; i++) {
michael@0 290 nsIAtom* curVertexAtom = data2->ObjectAt(i);
michael@0 291 nsAutoString curVertexStr;
michael@0 292 curVertexAtom->ToString(curVertexStr);
michael@0 293 nsCStringKey *curVertex = new nsCStringKey(ToNewCString(curVertexStr),
michael@0 294 curVertexStr.Length(), nsCStringKey::OWN);
michael@0 295
michael@0 296 BFSTableData *curVertexState = (BFSTableData*)lBFSTable.Get(curVertex);
michael@0 297 if (!curVertexState) {
michael@0 298 delete curVertex;
michael@0 299 return NS_ERROR_FAILURE;
michael@0 300 }
michael@0 301
michael@0 302 if (white == curVertexState->color) {
michael@0 303 curVertexState->color = gray;
michael@0 304 curVertexState->distance = headVertexState->distance + 1;
michael@0 305 curVertexState->predecessor = (nsCStringKey*)currentHead->Clone();
michael@0 306 if (!curVertexState->predecessor) {
michael@0 307 delete curVertex;
michael@0 308 return NS_ERROR_OUT_OF_MEMORY;
michael@0 309 }
michael@0 310 grayQ.Push(curVertex);
michael@0 311 } else {
michael@0 312 delete curVertex; // if this vertex has already been discovered, we don't want
michael@0 313 // to leak it. (non-discovered vertex's get cleaned up when
michael@0 314 // they're popped).
michael@0 315 }
michael@0 316 }
michael@0 317 headVertexState->color = black;
michael@0 318 nsCStringKey *cur = (nsCStringKey*)grayQ.PopFront();
michael@0 319 delete cur;
michael@0 320 cur = nullptr;
michael@0 321 }
michael@0 322 // The shortest path (if any) has been generated and is represented by the chain of
michael@0 323 // BFSTableData->predecessor keys. Start at the bottom and work our way up.
michael@0 324
michael@0 325 // first parse out the FROM and TO MIME-types being registered.
michael@0 326
michael@0 327 nsAutoCString fromStr, toStr;
michael@0 328 rv = ParseFromTo(aContractID, fromStr, toStr);
michael@0 329 if (NS_FAILED(rv)) return rv;
michael@0 330
michael@0 331 // get the root CONTRACTID
michael@0 332 nsAutoCString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY);
michael@0 333 nsTArray<nsCString> *shortestPath = new nsTArray<nsCString>();
michael@0 334
michael@0 335 nsCStringKey toMIMEType(toStr);
michael@0 336 data = (BFSTableData*)lBFSTable.Get(&toMIMEType);
michael@0 337 if (!data) {
michael@0 338 // If this vertex isn't in the BFSTable, then no-one has registered for it,
michael@0 339 // therefore we can't do the conversion.
michael@0 340 delete shortestPath;
michael@0 341 return NS_ERROR_FAILURE;
michael@0 342 }
michael@0 343
michael@0 344 while (data) {
michael@0 345 nsCStringKey *key = data->key;
michael@0 346
michael@0 347 if (fromStr.Equals(key->GetString())) {
michael@0 348 // found it. We're done here.
michael@0 349 *aEdgeList = shortestPath;
michael@0 350 return NS_OK;
michael@0 351 }
michael@0 352
michael@0 353 // reconstruct the CONTRACTID.
michael@0 354 // Get the predecessor.
michael@0 355 if (!data->predecessor) break; // no predecessor
michael@0 356 BFSTableData *predecessorData = (BFSTableData*)lBFSTable.Get(data->predecessor);
michael@0 357
michael@0 358 if (!predecessorData) break; // no predecessor, chain doesn't exist.
michael@0 359
michael@0 360 // build out the CONTRACTID.
michael@0 361 nsAutoCString newContractID(ContractIDPrefix);
michael@0 362 newContractID.AppendLiteral("?from=");
michael@0 363
michael@0 364 nsCStringKey *predecessorKey = predecessorData->key;
michael@0 365 newContractID.Append(predecessorKey->GetString());
michael@0 366
michael@0 367 newContractID.AppendLiteral("&to=");
michael@0 368 newContractID.Append(key->GetString());
michael@0 369
michael@0 370 // Add this CONTRACTID to the chain.
michael@0 371 rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool
michael@0 372 NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed");
michael@0 373
michael@0 374 // move up the tree.
michael@0 375 data = predecessorData;
michael@0 376 }
michael@0 377 delete shortestPath;
michael@0 378 return NS_ERROR_FAILURE; // couldn't find a stream converter or chain.
michael@0 379 }
michael@0 380
michael@0 381
michael@0 382 /////////////////////////////////////////////////////
michael@0 383 // nsIStreamConverterService methods
michael@0 384 NS_IMETHODIMP
michael@0 385 nsStreamConverterService::CanConvert(const char* aFromType,
michael@0 386 const char* aToType,
michael@0 387 bool* _retval) {
michael@0 388 nsCOMPtr<nsIComponentRegistrar> reg;
michael@0 389 nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
michael@0 390 if (NS_FAILED(rv))
michael@0 391 return rv;
michael@0 392
michael@0 393 nsAutoCString contractID;
michael@0 394 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
michael@0 395 contractID.Append(aFromType);
michael@0 396 contractID.AppendLiteral("&to=");
michael@0 397 contractID.Append(aToType);
michael@0 398
michael@0 399 // See if we have a direct match
michael@0 400 rv = reg->IsContractIDRegistered(contractID.get(), _retval);
michael@0 401 if (NS_FAILED(rv))
michael@0 402 return rv;
michael@0 403 if (*_retval)
michael@0 404 return NS_OK;
michael@0 405
michael@0 406 // Otherwise try the graph.
michael@0 407 rv = BuildGraph();
michael@0 408 if (NS_FAILED(rv))
michael@0 409 return rv;
michael@0 410
michael@0 411 nsTArray<nsCString> *converterChain = nullptr;
michael@0 412 rv = FindConverter(contractID.get(), &converterChain);
michael@0 413 *_retval = NS_SUCCEEDED(rv);
michael@0 414
michael@0 415 delete converterChain;
michael@0 416 return NS_OK;
michael@0 417 }
michael@0 418
michael@0 419 NS_IMETHODIMP
michael@0 420 nsStreamConverterService::Convert(nsIInputStream *aFromStream,
michael@0 421 const char *aFromType,
michael@0 422 const char *aToType,
michael@0 423 nsISupports *aContext,
michael@0 424 nsIInputStream **_retval) {
michael@0 425 if (!aFromStream || !aFromType || !aToType || !_retval) return NS_ERROR_NULL_POINTER;
michael@0 426 nsresult rv;
michael@0 427
michael@0 428 // first determine whether we can even handle this conversion
michael@0 429 // build a CONTRACTID
michael@0 430 nsAutoCString contractID;
michael@0 431 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
michael@0 432 contractID.Append(aFromType);
michael@0 433 contractID.AppendLiteral("&to=");
michael@0 434 contractID.Append(aToType);
michael@0 435 const char *cContractID = contractID.get();
michael@0 436
michael@0 437 nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv));
michael@0 438 if (NS_FAILED(rv)) {
michael@0 439 // couldn't go direct, let's try walking the graph of converters.
michael@0 440 rv = BuildGraph();
michael@0 441 if (NS_FAILED(rv)) return rv;
michael@0 442
michael@0 443 nsTArray<nsCString> *converterChain = nullptr;
michael@0 444
michael@0 445 rv = FindConverter(cContractID, &converterChain);
michael@0 446 if (NS_FAILED(rv)) {
michael@0 447 // can't make this conversion.
michael@0 448 // XXX should have a more descriptive error code.
michael@0 449 return NS_ERROR_FAILURE;
michael@0 450 }
michael@0 451
michael@0 452 int32_t edgeCount = int32_t(converterChain->Length());
michael@0 453 NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
michael@0 454
michael@0 455
michael@0 456 // convert the stream using each edge of the graph as a step.
michael@0 457 // this is our stream conversion traversal.
michael@0 458 nsCOMPtr<nsIInputStream> dataToConvert = aFromStream;
michael@0 459 nsCOMPtr<nsIInputStream> convertedData;
michael@0 460
michael@0 461 for (int32_t i = edgeCount-1; i >= 0; i--) {
michael@0 462 const char *lContractID = converterChain->ElementAt(i).get();
michael@0 463
michael@0 464 converter = do_CreateInstance(lContractID, &rv);
michael@0 465
michael@0 466 if (NS_FAILED(rv)) {
michael@0 467 delete converterChain;
michael@0 468 return rv;
michael@0 469 }
michael@0 470
michael@0 471 nsAutoCString fromStr, toStr;
michael@0 472 rv = ParseFromTo(lContractID, fromStr, toStr);
michael@0 473 if (NS_FAILED(rv)) {
michael@0 474 delete converterChain;
michael@0 475 return rv;
michael@0 476 }
michael@0 477
michael@0 478 rv = converter->Convert(dataToConvert, fromStr.get(), toStr.get(), aContext, getter_AddRefs(convertedData));
michael@0 479 dataToConvert = convertedData;
michael@0 480 if (NS_FAILED(rv)) {
michael@0 481 delete converterChain;
michael@0 482 return rv;
michael@0 483 }
michael@0 484 }
michael@0 485
michael@0 486 delete converterChain;
michael@0 487 *_retval = convertedData;
michael@0 488 NS_ADDREF(*_retval);
michael@0 489
michael@0 490 } else {
michael@0 491 // we're going direct.
michael@0 492 rv = converter->Convert(aFromStream, aFromType, aToType, aContext, _retval);
michael@0 493 }
michael@0 494
michael@0 495 return rv;
michael@0 496 }
michael@0 497
michael@0 498
michael@0 499 NS_IMETHODIMP
michael@0 500 nsStreamConverterService::AsyncConvertData(const char *aFromType,
michael@0 501 const char *aToType,
michael@0 502 nsIStreamListener *aListener,
michael@0 503 nsISupports *aContext,
michael@0 504 nsIStreamListener **_retval) {
michael@0 505 if (!aFromType || !aToType || !aListener || !_retval) return NS_ERROR_NULL_POINTER;
michael@0 506
michael@0 507 nsresult rv;
michael@0 508
michael@0 509 // first determine whether we can even handle this conversion
michael@0 510 // build a CONTRACTID
michael@0 511 nsAutoCString contractID;
michael@0 512 contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
michael@0 513 contractID.Append(aFromType);
michael@0 514 contractID.AppendLiteral("&to=");
michael@0 515 contractID.Append(aToType);
michael@0 516 const char *cContractID = contractID.get();
michael@0 517
michael@0 518 nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv));
michael@0 519 if (NS_FAILED(rv)) {
michael@0 520 // couldn't go direct, let's try walking the graph of converters.
michael@0 521 rv = BuildGraph();
michael@0 522 if (NS_FAILED(rv)) return rv;
michael@0 523
michael@0 524 nsTArray<nsCString> *converterChain = nullptr;
michael@0 525
michael@0 526 rv = FindConverter(cContractID, &converterChain);
michael@0 527 if (NS_FAILED(rv)) {
michael@0 528 // can't make this conversion.
michael@0 529 // XXX should have a more descriptive error code.
michael@0 530 return NS_ERROR_FAILURE;
michael@0 531 }
michael@0 532
michael@0 533 // aListener is the listener that wants the final, converted, data.
michael@0 534 // we initialize finalListener w/ aListener so it gets put at the
michael@0 535 // tail end of the chain, which in the loop below, means the *first*
michael@0 536 // converter created.
michael@0 537 nsCOMPtr<nsIStreamListener> finalListener = aListener;
michael@0 538
michael@0 539 // convert the stream using each edge of the graph as a step.
michael@0 540 // this is our stream conversion traversal.
michael@0 541 int32_t edgeCount = int32_t(converterChain->Length());
michael@0 542 NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
michael@0 543 for (int i = 0; i < edgeCount; i++) {
michael@0 544 const char *lContractID = converterChain->ElementAt(i).get();
michael@0 545
michael@0 546 // create the converter for this from/to pair
michael@0 547 nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID));
michael@0 548 NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't registered");
michael@0 549
michael@0 550 nsAutoCString fromStr, toStr;
michael@0 551 rv = ParseFromTo(lContractID, fromStr, toStr);
michael@0 552 if (NS_FAILED(rv)) {
michael@0 553 delete converterChain;
michael@0 554 return rv;
michael@0 555 }
michael@0 556
michael@0 557 // connect the converter w/ the listener that should get the converted data.
michael@0 558 rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext);
michael@0 559 if (NS_FAILED(rv)) {
michael@0 560 delete converterChain;
michael@0 561 return rv;
michael@0 562 }
michael@0 563
michael@0 564 nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv));
michael@0 565 if (NS_FAILED(rv)) {
michael@0 566 delete converterChain;
michael@0 567 return rv;
michael@0 568 }
michael@0 569
michael@0 570 // the last iteration of this loop will result in finalListener
michael@0 571 // pointing to the converter that "starts" the conversion chain.
michael@0 572 // this converter's "from" type is the original "from" type. Prior
michael@0 573 // to the last iteration, finalListener will continuously be wedged
michael@0 574 // into the next listener in the chain, then be updated.
michael@0 575 finalListener = chainListener;
michael@0 576 }
michael@0 577 delete converterChain;
michael@0 578 // return the first listener in the chain.
michael@0 579 *_retval = finalListener;
michael@0 580 NS_ADDREF(*_retval);
michael@0 581
michael@0 582 } else {
michael@0 583 // we're going direct.
michael@0 584 *_retval = listener;
michael@0 585 NS_ADDREF(*_retval);
michael@0 586
michael@0 587 rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
michael@0 588 }
michael@0 589
michael@0 590 return rv;
michael@0 591
michael@0 592 }
michael@0 593
michael@0 594 nsresult
michael@0 595 NS_NewStreamConv(nsStreamConverterService** aStreamConv)
michael@0 596 {
michael@0 597 NS_PRECONDITION(aStreamConv != nullptr, "null ptr");
michael@0 598 if (!aStreamConv) return NS_ERROR_NULL_POINTER;
michael@0 599
michael@0 600 *aStreamConv = new nsStreamConverterService();
michael@0 601 NS_ADDREF(*aStreamConv);
michael@0 602
michael@0 603 return NS_OK;
michael@0 604 }

mercurial