/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Builds the graph represented as an adjacency list (and built up in // memory using an nsObjectHashtable and nsCOMArray combination). // // :BuildGraph() consults the category manager for all stream converter // CONTRACTIDS then fills the adjacency list with edges. // An edge in this case is comprised of a FROM and TO MIME type combination. // // CONTRACTID format: // @mozilla.org/streamconv;1?from=text/html&to=text/plain // XXX curently we only handle a single from and to combo, we should repeat the // XXX registration process for any series of from-to combos. // XXX can use nsTokenizer for this. //
nsCOMPtr<nsICategoryManager> catmgr(
mozilla::components::CategoryManager::Service(&rv)); if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISimpleEnumerator> entries;
rv = catmgr->EnumerateCategory(NS_ISTREAMCONVERTER_KEY,
getter_AddRefs(entries)); if (NS_FAILED(rv)) return rv;
// go through each entry to build the graph
nsCOMPtr<nsISupports> supports;
nsCOMPtr<nsISupportsCString> entry;
rv = entries->GetNext(getter_AddRefs(supports)); while (NS_SUCCEEDED(rv)) {
entry = do_QueryInterface(supports);
// get the entry string
nsAutoCString entryString;
rv = entry->GetData(entryString); if (NS_FAILED(rv)) return rv;
// cobble the entry string w/ the converter key to produce a full // contractID.
nsAutoCString contractID(NS_ISTREAMCONVERTER_KEY);
contractID.Append(entryString);
// now we've got the CONTRACTID, let's parse it up.
rv = AddAdjacency(contractID.get()); if (NS_FAILED(rv)) return rv;
// XXX currently you can not add the same adjacency (i.e. you can't have // multiple // XXX stream converters registering to handle the same from-to combination. // It's // XXX not programatically prohibited, it's just that results are un-predictable // XXX right now.
nsresult nsStreamConverterService::AddAdjacency(constchar* aContractID) {
nsresult rv; // first parse out the FROM and TO MIME-types.
using BFSHashTable = nsClassHashtable<nsCStringHashKey, BFSTableData>;
// nsObjectHashtable enumerator functions.
class CStreamConvDeallocator : public nsDequeFunctor<nsCString> { public: voidoperator()(nsCString* anObject) override { delete anObject; }
};
// 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( constchar* 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; for (constauto& entry : mAdjacencyList) { const nsACString& key = entry.GetKey();
MOZ_ASSERT(entry.GetWeak(), "no data in the table iteration");
lBFSTable.InsertOrUpdate(key, mozilla::MakeUnique<BFSTableData>(key));
}
// Now generate the shortest path tree.
grayQ.Push(new nsCString(fromC)); while (0 < grayQ.GetSize()) {
nsCString* currentHead = (nsCString*)grayQ.PeekFront();
nsTArray<RefPtr<nsAtom>>* 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->Length();
for (int32_t i = 0; i < edgeCount; i++) {
nsAtom* curVertexAtom = data2->ElementAt(i); auto* curVertex = new nsCString();
curVertexAtom->ToUTF8String(*curVertex);
if (white == curVertexState->color) {
curVertexState->color = gray;
curVertexState->distance = headVertexState->distance + 1;
curVertexState->predecessor =
mozilla::MakeUnique<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.
// get the root CONTRACTID
nsAutoCString ContractIDPrefix(NS_ISTREAMCONVERTER_KEY); auto* 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=");
// Add this CONTRACTID to the chain. // XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier.
shortestPath->AppendElement(newContractID);
// move up the tree.
data = predecessorData;
} delete shortestPath; return NS_ERROR_FAILURE; // couldn't find a stream converter or chain.
}
// See if we have a direct match
rv = reg->IsContractIDRegistered(contractID.get(), _retval); if (NS_FAILED(rv)) return rv; if (*_retval) return NS_OK;
// Otherwise try the graph.
rv = BuildGraph(); if (NS_FAILED(rv)) return rv;
// first determine whether we can even handle this conversion // build a CONTRACTID
nsAutoCString contractID;
contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
contractID.Append(aFromType);
contractID.AppendLiteral("&to=");
contractID.Append(aToType); constchar* cContractID = contractID.get();
nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(cContractID, &rv)); if (NS_FAILED(rv)) { // couldn't go direct, let's try walking the graph of converters.
rv = BuildGraph(); if (NS_FAILED(rv)) return rv;
nsTArray<nsCString>* converterChain = nullptr;
rv = FindConverter(cContractID, &converterChain); if (NS_FAILED(rv)) { // can't make this conversion. // XXX should have a more descriptive error code. return NS_ERROR_FAILURE;
}
int32_t edgeCount = int32_t(converterChain->Length());
NS_ASSERTION(edgeCount > 0, "findConverter should have failed");
// convert the stream using each edge of the graph as a step. // this is our stream conversion traversal.
nsCOMPtr<nsIInputStream> dataToConvert = aFromStream;
nsCOMPtr<nsIInputStream> convertedData;
for (int32_t i = edgeCount - 1; i >= 0; i--) { constchar* lContractID = converterChain->ElementAt(i).get();
converter = do_CreateInstance(lContractID, &rv);
if (NS_FAILED(rv)) { delete converterChain; return rv;
}
// first determine whether we can even handle this conversion // build a CONTRACTID
nsAutoCString contractID;
contractID.AssignLiteral(NS_ISTREAMCONVERTER_KEY "?from=");
contractID.Append(aFromType);
contractID.AppendLiteral("&to=");
contractID.Append(aToType); constchar* cContractID = contractID.get();
nsCOMPtr<nsIStreamConverter> listener(do_CreateInstance(cContractID, &rv)); if (NS_FAILED(rv)) { // couldn't go direct, let's try walking the graph of converters.
rv = BuildGraph(); if (NS_FAILED(rv)) return rv;
nsTArray<nsCString>* converterChain = nullptr;
rv = FindConverter(cContractID, &converterChain); if (NS_FAILED(rv)) { // can't make this conversion. // XXX should have a more descriptive error code. return NS_ERROR_FAILURE;
}
// aListener is the listener that wants the final, converted, data. // we initialize finalListener w/ aListener so it gets put at the // tail end of the chain, which in the loop below, means the *first* // converter created.
nsCOMPtr<nsIStreamListener> finalListener = aListener;
// convert the stream using each edge of the graph as a step. // this is our stream conversion traversal.
int32_t edgeCount = int32_t(converterChain->Length());
NS_ASSERTION(edgeCount > 0, "findConverter should have failed"); for (int i = 0; i < edgeCount; i++) { constchar* lContractID = converterChain->ElementAt(i).get();
// create the converter for this from/to pair
nsCOMPtr<nsIStreamConverter> converter(do_CreateInstance(lContractID));
NS_ASSERTION(converter, "graph construction problem, built a contractid that wasn't " "registered");
// connect the converter w/ the listener that should get the converted // data.
rv = converter->AsyncConvertData(fromStr.get(), toStr.get(),
finalListener, aContext); if (NS_FAILED(rv)) { delete converterChain; return rv;
}
// the last iteration of this loop will result in finalListener // pointing to the converter that "starts" the conversion chain. // this converter's "from" type is the original "from" type. Prior // to the last iteration, finalListener will continuously be wedged // into the next listener in the chain, then be updated.
finalListener = converter;
} delete converterChain; // return the first listener in the chain.
finalListener.forget(_retval);
} else { // we're going direct.
rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
listener.forget(_retval);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.