LCOV - code coverage report
Current view: directory - netwerk/streamconv/src - nsStreamConverterService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 332 34 10.2 %
Date: 2012-06-02 Functions: 20 9 45.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       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/
      10                 :  *
      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.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      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.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *
      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.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK *****
      38                 :  *
      39                 :  *
      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
      44                 :  *
      45                 :  * Modifications to Mozilla code or documentation
      46                 :  * identified per MPL Section 3.3
      47                 :  *
      48                 :  * Date         Modified by     Description of modification
      49                 :  * 03/27/2000   IBM Corp.       Added PR_CALLBACK for Optlink
      50                 :  *                               use in OS2
      51                 :  */
      52                 : 
      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"
      68                 : 
      69                 : ////////////////////////////////////////////////////////////
      70                 : // nsISupports methods
      71            5388 : NS_IMPL_ISUPPORTS1(nsStreamConverterService, nsIStreamConverterService)
      72                 : 
      73                 : 
      74                 : ////////////////////////////////////////////////////////////
      75                 : // nsIStreamConverterService methods
      76                 : 
      77                 : ////////////////////////////////////////////////////////////
      78                 : // nsStreamConverterService methods
      79              80 : nsStreamConverterService::nsStreamConverterService() : mAdjacencyList(nsnull) {
      80              80 : }
      81                 : 
      82             160 : nsStreamConverterService::~nsStreamConverterService() {
      83              80 :     NS_ASSERTION(mAdjacencyList, "init wasn't called, or the retval was ignored");
      84              80 :     delete mAdjacencyList;
      85             320 : }
      86                 : 
      87                 : // Delete all the entries in the adjacency list
      88               0 : static bool DeleteAdjacencyEntry(nsHashKey *aKey, void *aData, void* closure) {
      89               0 :     SCTableData *entry = (SCTableData*)aData;
      90               0 :     NS_ASSERTION(entry->key && entry->data.edges, "malformed adjacency list entry");
      91               0 :     delete entry->key;
      92               0 :     delete entry->data.edges;
      93                 :     delete entry;
      94               0 :     return true;   
      95                 : }
      96                 : 
      97                 : nsresult
      98              80 : nsStreamConverterService::Init() {
      99                 :     mAdjacencyList = new nsObjectHashtable(nsnull, nsnull,
     100              80 :                                            DeleteAdjacencyEntry, nsnull);
     101              80 :     if (!mAdjacencyList) return NS_ERROR_OUT_OF_MEMORY;
     102              80 :     return NS_OK;
     103                 : }
     104                 : 
     105                 : // Builds the graph represented as an adjacency list (and built up in 
     106                 : // memory using an nsObjectHashtable and nsISupportsArray combination).
     107                 : //
     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.
     111                 : // 
     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.
     117                 : //
     118                 : 
     119                 : nsresult
     120               0 : nsStreamConverterService::BuildGraph() {
     121                 : 
     122                 :     nsresult rv;
     123                 : 
     124               0 :     nsCOMPtr<nsICategoryManager> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
     125               0 :     if (NS_FAILED(rv)) return rv;
     126                 : 
     127               0 :     nsCOMPtr<nsISimpleEnumerator> entries;
     128               0 :     rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY, getter_AddRefs(entries));
     129               0 :     if (NS_FAILED(rv)) return rv;
     130                 : 
     131                 :     // go through each entry to build the graph
     132               0 :     nsCOMPtr<nsISupportsCString> entry;
     133               0 :     rv = entries->GetNext(getter_AddRefs(entry));
     134               0 :     while (NS_SUCCEEDED(rv)) {
     135                 : 
     136                 :         // get the entry string
     137               0 :         nsCAutoString entryString;
     138               0 :         rv = entry->GetData(entryString);
     139               0 :         if (NS_FAILED(rv)) return rv;
     140                 :         
     141                 :         // cobble the entry string w/ the converter key to produce a full contractID.
     142               0 :         nsCAutoString contractID(NS_ISTREAMCONVERTER_KEY);
     143               0 :         contractID.Append(entryString);
     144                 : 
     145                 :         // now we've got the CONTRACTID, let's parse it up.
     146               0 :         rv = AddAdjacency(contractID.get());
     147               0 :         if (NS_FAILED(rv)) return rv;
     148                 : 
     149               0 :         rv = entries->GetNext(getter_AddRefs(entry));
     150                 :     }
     151                 : 
     152               0 :     return NS_OK;
     153                 : }
     154                 : 
     155                 : 
     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               0 : nsStreamConverterService::AddAdjacency(const char *aContractID) {
     162                 :     nsresult rv;
     163                 :     // first parse out the FROM and TO MIME-types.
     164                 : 
     165               0 :     nsCAutoString fromStr, toStr;
     166               0 :     rv = ParseFromTo(aContractID, fromStr, toStr);
     167               0 :     if (NS_FAILED(rv)) return rv;
     168                 : 
     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.
     171                 : 
     172               0 :     nsCStringKey fromKey(fromStr);
     173               0 :     SCTableData *fromEdges = (SCTableData*)mAdjacencyList->Get(&fromKey);
     174               0 :     if (!fromEdges) {
     175                 :         // There is no fromStr vertex, create one.
     176                 : 
     177               0 :         nsCStringKey *newFromKey = new nsCStringKey(ToNewCString(fromStr), fromStr.Length(), nsCStringKey::OWN);
     178               0 :         if (!newFromKey) return NS_ERROR_OUT_OF_MEMORY;
     179                 : 
     180               0 :         SCTableData *data = new SCTableData(newFromKey);
     181               0 :         if (!data) {
     182               0 :             delete newFromKey;
     183               0 :             return NS_ERROR_OUT_OF_MEMORY;
     184                 :         }
     185                 : 
     186               0 :         nsCOMArray<nsIAtom>* edgeArray = new nsCOMArray<nsIAtom>;
     187               0 :         if (!edgeArray) {
     188               0 :             delete newFromKey;
     189               0 :             data->key = nsnull;
     190                 :             delete data;
     191               0 :             return NS_ERROR_OUT_OF_MEMORY;
     192                 :         }
     193               0 :         data->data.edges = edgeArray;
     194                 : 
     195               0 :         mAdjacencyList->Put(newFromKey, data);
     196               0 :         fromEdges = data;
     197                 :     }
     198                 : 
     199               0 :     nsCStringKey toKey(toStr);
     200               0 :     if (!mAdjacencyList->Get(&toKey)) {
     201                 :         // There is no toStr vertex, create one.
     202               0 :         nsCStringKey *newToKey = new nsCStringKey(ToNewCString(toStr), toStr.Length(), nsCStringKey::OWN);
     203               0 :         if (!newToKey) return NS_ERROR_OUT_OF_MEMORY;
     204                 : 
     205               0 :         SCTableData *data = new SCTableData(newToKey);
     206               0 :         if (!data) {
     207               0 :             delete newToKey;
     208               0 :             return NS_ERROR_OUT_OF_MEMORY;
     209                 :         }
     210                 : 
     211               0 :         nsCOMArray<nsIAtom>* edgeArray = new nsCOMArray<nsIAtom>;
     212               0 :         if (!edgeArray) {
     213               0 :             delete newToKey;
     214               0 :             data->key = nsnull;
     215                 :             delete data;
     216               0 :             return NS_ERROR_OUT_OF_MEMORY;
     217                 :         }
     218               0 :         data->data.edges = edgeArray;
     219               0 :         mAdjacencyList->Put(newToKey, data);
     220                 :     }
     221                 :     
     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.
     224                 : 
     225               0 :     nsCOMPtr<nsIAtom> vertex = do_GetAtom(toStr); 
     226               0 :     if (!vertex) return NS_ERROR_OUT_OF_MEMORY;
     227                 : 
     228               0 :     NS_ASSERTION(fromEdges, "something wrong in adjacency list construction");
     229               0 :     if (!fromEdges)
     230               0 :         return NS_ERROR_FAILURE;
     231                 : 
     232               0 :     nsCOMArray<nsIAtom> *adjacencyList = fromEdges->data.edges;
     233               0 :     return adjacencyList->AppendObject(vertex) ? NS_OK : NS_ERROR_FAILURE;
     234                 : }
     235                 : 
     236                 : nsresult
     237               0 : nsStreamConverterService::ParseFromTo(const char *aContractID, nsCString &aFromRes, nsCString &aToRes) {
     238                 : 
     239               0 :     nsCAutoString ContractIDStr(aContractID);
     240                 : 
     241               0 :     PRInt32 fromLoc = ContractIDStr.Find("from=");
     242               0 :     PRInt32 toLoc   = ContractIDStr.Find("to=");
     243               0 :     if (-1 == fromLoc || -1 == toLoc ) return NS_ERROR_FAILURE;
     244                 : 
     245               0 :     fromLoc = fromLoc + 5;
     246               0 :     toLoc = toLoc + 3;
     247                 : 
     248               0 :     nsCAutoString fromStr, toStr;
     249                 : 
     250               0 :     ContractIDStr.Mid(fromStr, fromLoc, toLoc - 4 - fromLoc);
     251               0 :     ContractIDStr.Mid(toStr, toLoc, ContractIDStr.Length() - toLoc);
     252                 : 
     253               0 :     aFromRes.Assign(fromStr);
     254               0 :     aToRes.Assign(toStr);
     255                 : 
     256               0 :     return NS_OK;
     257                 : }
     258                 : 
     259                 : // nsObjectHashtable enumerator functions.
     260                 : 
     261                 : // Initializes the BFS state table.
     262               0 : static bool InitBFSTable(nsHashKey *aKey, void *aData, void* closure) {
     263               0 :     NS_ASSERTION((SCTableData*)aData, "no data in the table enumeration");
     264                 :     
     265               0 :     nsHashtable *BFSTable = (nsHashtable*)closure;
     266               0 :     if (!BFSTable) return false;
     267                 : 
     268               0 :     BFSState *state = new BFSState;
     269               0 :     if (!state) return false;
     270                 : 
     271               0 :     state->color = white;
     272               0 :     state->distance = -1;
     273               0 :     state->predecessor = nsnull;
     274                 : 
     275               0 :     SCTableData *data = new SCTableData(static_cast<nsCStringKey*>(aKey));
     276               0 :     if (!data) {
     277               0 :         delete state;
     278               0 :         return false;
     279                 :     }
     280               0 :     data->data.state = state;
     281                 : 
     282               0 :     BFSTable->Put(aKey, data);
     283               0 :     return true;   
     284                 : }
     285                 : 
     286                 : // cleans up the BFS state table
     287               0 : static bool DeleteBFSEntry(nsHashKey *aKey, void *aData, void *closure) {
     288               0 :     SCTableData *data = (SCTableData*)aData;
     289               0 :     BFSState *state = data->data.state;
     290               0 :     delete state;
     291               0 :     data->key = nsnull;
     292                 :     delete data;
     293               0 :     return true;
     294                 : }
     295                 : 
     296               0 : class CStreamConvDeallocator : public nsDequeFunctor {
     297                 : public:
     298               0 :     virtual void* operator()(void* anObject) {
     299               0 :         nsCStringKey *key = (nsCStringKey*)anObject;
     300               0 :         delete key;
     301               0 :         return 0;
     302                 :     }
     303                 : };
     304                 : 
     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               0 : nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) {
     312                 :     nsresult rv;
     313               0 :     if (!aEdgeList) return NS_ERROR_NULL_POINTER;
     314               0 :     *aEdgeList = nsnull;
     315                 : 
     316                 :     // walk the graph in search of the appropriate converter.
     317                 : 
     318               0 :     PRInt32 vertexCount = mAdjacencyList->Count();
     319               0 :     if (0 >= vertexCount) return NS_ERROR_FAILURE;
     320                 : 
     321                 :     // Create a corresponding color table for each vertex in the graph.
     322               0 :     nsObjectHashtable lBFSTable(nsnull, nsnull, DeleteBFSEntry, nsnull);
     323               0 :     mAdjacencyList->Enumerate(InitBFSTable, &lBFSTable);
     324                 : 
     325               0 :     NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem");
     326                 : 
     327                 :     // This is our source vertex; our starting point.
     328               0 :     nsCAutoString fromC, toC;
     329               0 :     rv = ParseFromTo(aContractID, fromC, toC);
     330               0 :     if (NS_FAILED(rv)) return rv;
     331                 : 
     332               0 :     nsCStringKey *source = new nsCStringKey(fromC.get());
     333               0 :     if (!source) return NS_ERROR_OUT_OF_MEMORY;
     334                 : 
     335               0 :     SCTableData *data = (SCTableData*)lBFSTable.Get(source);
     336               0 :     if (!data) {
     337               0 :         delete source;
     338               0 :         return NS_ERROR_FAILURE;
     339                 :     }
     340                 : 
     341               0 :     BFSState *state = data->data.state;
     342                 : 
     343               0 :     state->color = gray;
     344               0 :     state->distance = 0;
     345               0 :     CStreamConvDeallocator *dtorFunc = new CStreamConvDeallocator();
     346               0 :     if (!dtorFunc) {
     347               0 :         delete source;
     348               0 :         return NS_ERROR_OUT_OF_MEMORY;
     349                 :     }
     350                 : 
     351               0 :     nsDeque grayQ(dtorFunc);
     352                 : 
     353                 :     // Now generate the shortest path tree.
     354               0 :     grayQ.Push(source);
     355               0 :     while (0 < grayQ.GetSize()) {
     356               0 :         nsCStringKey *currentHead = (nsCStringKey*)grayQ.PeekFront();
     357               0 :         SCTableData *data2 = (SCTableData*)mAdjacencyList->Get(currentHead);
     358               0 :         if (!data2) return NS_ERROR_FAILURE;
     359                 : 
     360               0 :         nsCOMArray<nsIAtom> *edges = data2->data.edges;
     361               0 :         NS_ASSERTION(edges, "something went wrong with BFS strmconv algorithm");
     362               0 :         if (!edges) return NS_ERROR_FAILURE;
     363                 : 
     364                 :         // Get the state of the current head to calculate the distance of each
     365                 :         // reachable vertex in the loop.
     366               0 :         data2 = (SCTableData*)lBFSTable.Get(currentHead);
     367               0 :         if (!data2) return NS_ERROR_FAILURE;
     368                 : 
     369               0 :         BFSState *headVertexState = data2->data.state;
     370               0 :         NS_ASSERTION(headVertexState, "problem with the BFS strmconv algorithm");
     371               0 :         if (!headVertexState) return NS_ERROR_FAILURE;
     372                 : 
     373               0 :         PRInt32 edgeCount = edges->Count();
     374                 : 
     375               0 :         for (PRInt32 i = 0; i < edgeCount; i++) {
     376               0 :             nsIAtom* curVertexAtom = edges->ObjectAt(i);
     377               0 :             nsAutoString curVertexStr;
     378               0 :             curVertexAtom->ToString(curVertexStr);
     379                 :             nsCStringKey *curVertex = new nsCStringKey(ToNewCString(curVertexStr), 
     380               0 :                                         curVertexStr.Length(), nsCStringKey::OWN);
     381               0 :             if (!curVertex) return NS_ERROR_OUT_OF_MEMORY;
     382                 : 
     383               0 :             SCTableData *data3 = (SCTableData*)lBFSTable.Get(curVertex);
     384               0 :             if (!data3) {
     385               0 :                 delete curVertex;
     386               0 :                 return NS_ERROR_FAILURE;
     387                 :             }
     388               0 :             BFSState *curVertexState = data3->data.state;
     389               0 :             NS_ASSERTION(curVertexState, "something went wrong with the BFS strmconv algorithm");
     390               0 :             if (!curVertexState) return NS_ERROR_FAILURE;
     391                 : 
     392               0 :             if (white == curVertexState->color) {
     393               0 :                 curVertexState->color = gray;
     394               0 :                 curVertexState->distance = headVertexState->distance + 1;
     395               0 :                 curVertexState->predecessor = (nsCStringKey*)currentHead->Clone();
     396               0 :                 if (!curVertexState->predecessor) {
     397               0 :                     delete curVertex;
     398               0 :                     return NS_ERROR_OUT_OF_MEMORY;
     399                 :                 }
     400               0 :                 grayQ.Push(curVertex);
     401                 :             } else {
     402               0 :                 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).
     405                 :             }
     406                 :         }
     407               0 :         headVertexState->color = black;
     408               0 :         nsCStringKey *cur = (nsCStringKey*)grayQ.PopFront();
     409               0 :         delete cur;
     410               0 :         cur = nsnull;
     411                 :     }
     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.
     414                 : 
     415                 :     // first parse out the FROM and TO MIME-types being registered.
     416                 : 
     417               0 :     nsCAutoString fromStr, toStr;
     418               0 :     rv = ParseFromTo(aContractID, fromStr, toStr);
     419               0 :     if (NS_FAILED(rv)) return rv;
     420                 : 
     421                 :     // get the root CONTRACTID
     422               0 :     nsCAutoString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY);
     423               0 :     nsTArray<nsCString> *shortestPath = new nsTArray<nsCString>();
     424               0 :     if (!shortestPath) return NS_ERROR_OUT_OF_MEMORY;
     425                 : 
     426               0 :     nsCStringKey toMIMEType(toStr);
     427               0 :     data = (SCTableData*)lBFSTable.Get(&toMIMEType);
     428               0 :     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               0 :         delete shortestPath;
     432               0 :         return NS_ERROR_FAILURE;
     433                 :     }
     434                 : 
     435               0 :     while (data) {
     436               0 :         BFSState *curState = data->data.state;
     437                 : 
     438               0 :         nsCStringKey *key = data->key;
     439                 :         
     440               0 :         if (fromStr.Equals(key->GetString())) {
     441                 :             // found it. We're done here.
     442               0 :             *aEdgeList = shortestPath;
     443               0 :             return NS_OK;
     444                 :         }
     445                 : 
     446                 :         // reconstruct the CONTRACTID.
     447                 :         // Get the predecessor.
     448               0 :         if (!curState->predecessor) break; // no predecessor
     449               0 :         SCTableData *predecessorData = (SCTableData*)lBFSTable.Get(curState->predecessor);
     450                 : 
     451               0 :         if (!predecessorData) break; // no predecessor, chain doesn't exist.
     452                 : 
     453                 :         // build out the CONTRACTID.
     454               0 :         nsCAutoString newContractID(ContractIDPrefix);
     455               0 :         newContractID.AppendLiteral("?from=");
     456                 : 
     457               0 :         nsCStringKey *predecessorKey = predecessorData->key;
     458               0 :         newContractID.Append(predecessorKey->GetString());
     459                 : 
     460               0 :         newContractID.AppendLiteral("&to=");
     461               0 :         newContractID.Append(key->GetString());
     462                 :     
     463                 :         // Add this CONTRACTID to the chain.
     464               0 :         rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE;  // XXX this method incorrectly returns a bool
     465               0 :         NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed");
     466                 : 
     467                 :         // move up the tree.
     468               0 :         data = predecessorData;
     469                 :     }
     470               0 :     delete shortestPath;
     471               0 :     return NS_ERROR_FAILURE; // couldn't find a stream converter or chain.
     472                 : }
     473                 : 
     474                 : 
     475                 : /////////////////////////////////////////////////////
     476                 : // nsIStreamConverterService methods
     477                 : NS_IMETHODIMP
     478               0 : nsStreamConverterService::CanConvert(const char* aFromType,
     479                 :                                      const char* aToType,
     480                 :                                      bool* _retval) {
     481               0 :     nsCOMPtr<nsIComponentRegistrar> reg;
     482               0 :     nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
     483               0 :     if (NS_FAILED(rv))
     484               0 :         return rv;
     485                 : 
     486               0 :     nsCAutoString contractID;
     487               0 :     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
     488               0 :     contractID.Append(aFromType);
     489               0 :     contractID.AppendLiteral("&to=");
     490               0 :     contractID.Append(aToType);
     491                 : 
     492                 :     // See if we have a direct match
     493               0 :     rv = reg->IsContractIDRegistered(contractID.get(), _retval);
     494               0 :     if (NS_FAILED(rv))
     495               0 :         return rv;
     496               0 :     if (*_retval)
     497               0 :         return NS_OK;
     498                 : 
     499                 :     // Otherwise try the graph.
     500               0 :     rv = BuildGraph();
     501               0 :     if (NS_FAILED(rv))
     502               0 :         return rv;
     503                 : 
     504               0 :     nsTArray<nsCString> *converterChain = nsnull;
     505               0 :     rv = FindConverter(contractID.get(), &converterChain);
     506               0 :     *_retval = NS_SUCCEEDED(rv);
     507                 : 
     508               0 :     delete converterChain;
     509               0 :     return NS_OK;
     510                 : }
     511                 : 
     512                 : NS_IMETHODIMP
     513               0 : nsStreamConverterService::Convert(nsIInputStream *aFromStream,
     514                 :                                   const char *aFromType, 
     515                 :                                   const char *aToType,
     516                 :                                   nsISupports *aContext,
     517                 :                                   nsIInputStream **_retval) {
     518               0 :     if (!aFromStream || !aFromType || !aToType || !_retval) return NS_ERROR_NULL_POINTER;
     519                 :     nsresult rv;
     520                 : 
     521                 :     // first determine whether we can even handle this conversion
     522                 :     // build a CONTRACTID
     523               0 :     nsCAutoString contractID;
     524               0 :     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
     525               0 :     contractID.Append(aFromType);
     526               0 :     contractID.AppendLiteral("&to=");
     527               0 :     contractID.Append(aToType);
     528               0 :     const char *cContractID = contractID.get();
     529                 : 
     530               0 :     nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv));
     531               0 :     if (NS_FAILED(rv)) {
     532                 :         // couldn't go direct, let's try walking the graph of converters.
     533               0 :         rv = BuildGraph();
     534               0 :         if (NS_FAILED(rv)) return rv;
     535                 : 
     536               0 :         nsTArray<nsCString> *converterChain = nsnull;
     537                 : 
     538               0 :         rv = FindConverter(cContractID, &converterChain);
     539               0 :         if (NS_FAILED(rv)) {
     540                 :             // can't make this conversion.
     541                 :             // XXX should have a more descriptive error code.
     542               0 :             return NS_ERROR_FAILURE;
     543                 :         }
     544                 : 
     545               0 :         PRInt32 edgeCount = PRInt32(converterChain->Length());
     546               0 :         NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
     547                 : 
     548                 : 
     549                 :         // convert the stream using each edge of the graph as a step.
     550                 :         // this is our stream conversion traversal.
     551               0 :         nsCOMPtr<nsIInputStream> dataToConvert = aFromStream;
     552               0 :         nsCOMPtr<nsIInputStream> convertedData;
     553                 : 
     554               0 :         for (PRInt32 i = edgeCount-1; i >= 0; i--) {
     555               0 :             const char *lContractID = converterChain->ElementAt(i).get();
     556                 : 
     557               0 :             converter = do_CreateInstance(lContractID, &rv);
     558                 : 
     559               0 :             if (NS_FAILED(rv)) {
     560               0 :                 delete converterChain;                
     561               0 :                 return rv;
     562                 :             }
     563                 : 
     564               0 :             nsCAutoString fromStr, toStr;
     565               0 :             rv = ParseFromTo(lContractID, fromStr, toStr);
     566               0 :             if (NS_FAILED(rv)) {
     567               0 :                 delete converterChain;
     568               0 :                 return rv;
     569                 :             }
     570                 : 
     571               0 :             rv = converter->Convert(dataToConvert, fromStr.get(), toStr.get(), aContext, getter_AddRefs(convertedData));
     572               0 :             dataToConvert = convertedData;
     573               0 :             if (NS_FAILED(rv)) {
     574               0 :                 delete converterChain;
     575               0 :                 return rv;
     576                 :             }
     577                 :         }
     578                 : 
     579               0 :         delete converterChain;
     580               0 :         *_retval = convertedData;
     581               0 :         NS_ADDREF(*_retval);
     582                 : 
     583                 :     } else {
     584                 :         // we're going direct.
     585               0 :         rv = converter->Convert(aFromStream, aFromType, aToType, aContext, _retval);
     586                 :     }
     587                 :     
     588               0 :     return rv;
     589                 : }
     590                 : 
     591                 : 
     592                 : NS_IMETHODIMP
     593             925 : nsStreamConverterService::AsyncConvertData(const char *aFromType, 
     594                 :                                            const char *aToType, 
     595                 :                                            nsIStreamListener *aListener,
     596                 :                                            nsISupports *aContext,
     597                 :                                            nsIStreamListener **_retval) {
     598             925 :     if (!aFromType || !aToType || !aListener || !_retval) return NS_ERROR_NULL_POINTER;
     599                 : 
     600                 :     nsresult rv;
     601                 : 
     602                 :     // first determine whether we can even handle this conversion
     603                 :     // build a CONTRACTID
     604            1850 :     nsCAutoString contractID;
     605             925 :     contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
     606             925 :     contractID.Append(aFromType);
     607             925 :     contractID.AppendLiteral("&to=");
     608             925 :     contractID.Append(aToType);
     609             925 :     const char *cContractID = contractID.get();
     610                 : 
     611            1850 :     nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv));
     612             925 :     if (NS_FAILED(rv)) {
     613                 :         // couldn't go direct, let's try walking the graph of converters.
     614               0 :         rv = BuildGraph();
     615               0 :         if (NS_FAILED(rv)) return rv;
     616                 : 
     617               0 :         nsTArray<nsCString> *converterChain = nsnull;
     618                 : 
     619               0 :         rv = FindConverter(cContractID, &converterChain);
     620               0 :         if (NS_FAILED(rv)) {
     621                 :             // can't make this conversion.
     622                 :             // XXX should have a more descriptive error code.
     623               0 :             return NS_ERROR_FAILURE;
     624                 :         }
     625                 : 
     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               0 :         nsCOMPtr<nsIStreamListener> finalListener = aListener;
     631                 : 
     632                 :         // convert the stream using each edge of the graph as a step.
     633                 :         // this is our stream conversion traversal.
     634               0 :         PRInt32 edgeCount = PRInt32(converterChain->Length());
     635               0 :         NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
     636               0 :         for (int i = 0; i < edgeCount; i++) {
     637               0 :             const char *lContractID = converterChain->ElementAt(i).get();
     638                 : 
     639                 :             // create the converter for this from/to pair
     640               0 :             nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID));
     641               0 :             NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't registered");
     642                 : 
     643               0 :             nsCAutoString fromStr, toStr;
     644               0 :             rv = ParseFromTo(lContractID, fromStr, toStr);
     645               0 :             if (NS_FAILED(rv)) {
     646               0 :                 delete converterChain;
     647               0 :                 return rv;
     648                 :             }
     649                 : 
     650                 :             // connect the converter w/ the listener that should get the converted data.
     651               0 :             rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext);
     652               0 :             if (NS_FAILED(rv)) {
     653               0 :                 delete converterChain;
     654               0 :                 return rv;
     655                 :             }
     656                 : 
     657               0 :             nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv));
     658               0 :             if (NS_FAILED(rv)) {
     659               0 :                 delete converterChain;
     660               0 :                 return rv;
     661                 :             }
     662                 : 
     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               0 :             finalListener = chainListener;
     669                 :         }
     670               0 :         delete converterChain;
     671                 :         // return the first listener in the chain.
     672               0 :         *_retval = finalListener;
     673               0 :         NS_ADDREF(*_retval);
     674                 : 
     675                 :     } else {
     676                 :         // we're going direct.
     677             925 :         *_retval = listener;
     678             925 :         NS_ADDREF(*_retval);
     679                 : 
     680             925 :         rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
     681                 :     }
     682                 :     
     683             925 :     return rv;
     684                 : 
     685                 : }
     686                 : 
     687                 : nsresult
     688              80 : NS_NewStreamConv(nsStreamConverterService** aStreamConv)
     689                 : {
     690              80 :     NS_PRECONDITION(aStreamConv != nsnull, "null ptr");
     691              80 :     if (!aStreamConv) return NS_ERROR_NULL_POINTER;
     692                 : 
     693              80 :     *aStreamConv = new nsStreamConverterService();
     694              80 :     if (!*aStreamConv) return NS_ERROR_OUT_OF_MEMORY;
     695                 : 
     696              80 :     NS_ADDREF(*aStreamConv);
     697              80 :     nsresult rv = (*aStreamConv)->Init();
     698              80 :     if (NS_FAILED(rv))
     699               0 :         NS_RELEASE(*aStreamConv);
     700                 : 
     701              80 :     return rv;
     702                 : }

Generated by: LCOV version 1.7