// Initializes the BFS state table. static PLDHashOperator InitBFSTable(const nsACString &aKey, nsCOMArray<nsIAtom> *aData, void* aClosure) { MOZ_ASSERT(aData, "no data in the table enumeration"); BFSHashTable *bfsTable = static_cast<BFSHashTable*>(aClosure); if (!bfsTable) return PL_DHASH_STOP; BFSTableData *data = new BFSTableData(aKey); bfsTable->Put(aKey, data); return PL_DHASH_NEXT; }
// walks the graph using a breadth-first-search algorithm which generates a discovered // verticies tree. This tree is then walked up (from destination vertex, to origin vertex) // and each link in the chain is added to an nsStringArray. A direct lookup for the given // CONTRACTID should be made prior to calling this method in an attempt to find a direct // converter rather than walking the graph. nsresult nsStreamConverterService::FindConverter(const char *aContractID, nsTArray<nsCString> **aEdgeList) { nsresult rv; if (!aEdgeList) return NS_ERROR_NULL_POINTER; *aEdgeList = nullptr; // walk the graph in search of the appropriate converter. uint32_t vertexCount = mAdjacencyList.Count(); if (0 >= vertexCount) return NS_ERROR_FAILURE; // Create a corresponding color table for each vertex in the graph. BFSHashTable lBFSTable; mAdjacencyList.EnumerateRead(InitBFSTable, &lBFSTable); NS_ASSERTION(lBFSTable.Count() == vertexCount, "strmconv BFS table init problem"); // This is our source vertex; our starting point. nsAutoCString fromC, toC; rv = ParseFromTo(aContractID, fromC, toC); if (NS_FAILED(rv)) return rv; BFSTableData *data = lBFSTable.Get(fromC); if (!data) { return NS_ERROR_FAILURE; } data->color = gray; data->distance = 0; CStreamConvDeallocator *dtorFunc = new CStreamConvDeallocator(); nsDeque grayQ(dtorFunc); // Now generate the shortest path tree. grayQ.Push(new nsCString(fromC)); while (0 < grayQ.GetSize()) { nsCString *currentHead = (nsCString*)grayQ.PeekFront(); nsCOMArray<nsIAtom> *data2 = mAdjacencyList.Get(*currentHead); if (!data2) return NS_ERROR_FAILURE; // Get the state of the current head to calculate the distance of each // reachable vertex in the loop. BFSTableData *headVertexState = lBFSTable.Get(*currentHead); if (!headVertexState) return NS_ERROR_FAILURE; int32_t edgeCount = data2->Count(); for (int32_t i = 0; i < edgeCount; i++) { nsIAtom* curVertexAtom = data2->ObjectAt(i); nsCString *curVertex = new nsCString(); curVertexAtom->ToUTF8String(*curVertex); BFSTableData *curVertexState = lBFSTable.Get(*curVertex); if (!curVertexState) { delete curVertex; return NS_ERROR_FAILURE; } if (white == curVertexState->color) { curVertexState->color = gray; curVertexState->distance = headVertexState->distance + 1; curVertexState->predecessor = new nsCString(*currentHead); grayQ.Push(curVertex); } else { delete curVertex; // if this vertex has already been discovered, we don't want // to leak it. (non-discovered vertex's get cleaned up when // they're popped). } } headVertexState->color = black; nsCString *cur = (nsCString*)grayQ.PopFront(); delete cur; cur = nullptr; } // The shortest path (if any) has been generated and is represented by the chain of // BFSTableData->predecessor keys. Start at the bottom and work our way up. // first parse out the FROM and TO MIME-types being registered. nsAutoCString fromStr, toMIMEType; rv = ParseFromTo(aContractID, fromStr, toMIMEType); if (NS_FAILED(rv)) return rv; // get the root CONTRACTID nsAutoCString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY); nsTArray<nsCString> *shortestPath = new nsTArray<nsCString>(); data = lBFSTable.Get(toMIMEType); if (!data) { // If this vertex isn't in the BFSTable, then no-one has registered for it, // therefore we can't do the conversion. delete shortestPath; return NS_ERROR_FAILURE; } while (data) { if (fromStr.Equals(data->key)) { // found it. We're done here. *aEdgeList = shortestPath; return NS_OK; } // reconstruct the CONTRACTID. // Get the predecessor. if (!data->predecessor) break; // no predecessor BFSTableData *predecessorData = lBFSTable.Get(*data->predecessor); if (!predecessorData) break; // no predecessor, chain doesn't exist. // build out the CONTRACTID. nsAutoCString newContractID(ContractIDPrefix); newContractID.AppendLiteral("?from="); newContractID.Append(predecessorData->key); newContractID.AppendLiteral("&to="); newContractID.Append(data->key); // Add this CONTRACTID to the chain. rv = shortestPath->AppendElement(newContractID) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool NS_ASSERTION(NS_SUCCEEDED(rv), "AppendElement failed"); // move up the tree. data = predecessorData; } delete shortestPath; return NS_ERROR_FAILURE; // couldn't find a stream converter or chain. }