/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mike McCabe * John Bandhauer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* Implementation of xptiInterfaceInfoManager. */ #include "xptiprivate.h" #include "nsDependentString.h" #include "nsString.h" #define NS_ZIPLOADER_CONTRACTID NS_XPTLOADER_CONTRACTID_PREFIX "zip" NS_IMPL_THREADSAFE_ISUPPORTS2(xptiInterfaceInfoManager, nsIInterfaceInfoManager, nsIInterfaceInfoSuperManager) static xptiInterfaceInfoManager* gInterfaceInfoManager = nsnull; #ifdef DEBUG static int gCallCount = 0; #endif // static xptiInterfaceInfoManager* xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef() { if(!gInterfaceInfoManager) { nsCOMPtr searchPath; BuildFileSearchPath(getter_AddRefs(searchPath)); if(!searchPath) { NS_ERROR("can't get xpt search path!"); return nsnull; } gInterfaceInfoManager = new xptiInterfaceInfoManager(searchPath); if(!gInterfaceInfoManager) { NS_ERROR("can't instantiate xptiInterfaceInfoManager"); return nsnull; } NS_ADDREF(gInterfaceInfoManager); if(!gInterfaceInfoManager->IsValid()) { NS_RELEASE(gInterfaceInfoManager); } else { PRBool mustAutoReg = !xptiManifest::Read(gInterfaceInfoManager, &gInterfaceInfoManager->mWorkingSet); #ifdef DEBUG { // This sets what will be returned by GetOpenLogFile(). xptiAutoLog autoLog(gInterfaceInfoManager, gInterfaceInfoManager->mAutoRegLogFile, PR_TRUE); LOG_AUTOREG(("debug build forced autoreg after %s load of manifest\n", mustAutoReg ? "FAILED" : "successful")); mustAutoReg = PR_TRUE; } #endif // DEBUG if(mustAutoReg) gInterfaceInfoManager->AutoRegisterInterfaces(); } } return gInterfaceInfoManager; } void xptiInterfaceInfoManager::FreeInterfaceInfoManager() { if(gInterfaceInfoManager) gInterfaceInfoManager->LogStats(); NS_IF_RELEASE(gInterfaceInfoManager); } PRBool xptiInterfaceInfoManager::IsValid() { return mWorkingSet.IsValid() && mResolveLock && mAutoRegLock && mInfoMonitor && mAdditionalManagersLock; } xptiInterfaceInfoManager::xptiInterfaceInfoManager(nsISupportsArray* aSearchPath) : mWorkingSet(aSearchPath), mOpenLogFile(nsnull), mResolveLock(PR_NewLock()), mAutoRegLock(PR_NewLock()), mInfoMonitor(nsAutoMonitor::NewMonitor("xptiInfoMonitor")), mAdditionalManagersLock(PR_NewLock()), mSearchPath(aSearchPath) { const char* statsFilename = PR_GetEnv("MOZILLA_XPTI_STATS"); if(statsFilename && *statsFilename) { mStatsLogFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); if(mStatsLogFile && NS_SUCCEEDED(mStatsLogFile->InitWithNativePath(nsDependentCString(statsFilename)))) { printf("* Logging xptinfo stats to: %s\n", statsFilename); } else { printf("* Failed to create xptinfo stats file: %s\n", statsFilename); mStatsLogFile = nsnull; } } const char* autoRegFilename = PR_GetEnv("MOZILLA_XPTI_REGLOG"); if(autoRegFilename && *autoRegFilename) { mAutoRegLogFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); if(mAutoRegLogFile && NS_SUCCEEDED(mAutoRegLogFile->InitWithNativePath(nsDependentCString(autoRegFilename)))) { printf("* Logging xptinfo autoreg to: %s\n", autoRegFilename); } else { printf("* Failed to create xptinfo autoreg file: %s\n", autoRegFilename); mAutoRegLogFile = nsnull; } } } xptiInterfaceInfoManager::~xptiInterfaceInfoManager() { // We only do this on shutdown of the service. mWorkingSet.InvalidateInterfaceInfos(); if(mResolveLock) PR_DestroyLock(mResolveLock); if(mAutoRegLock) PR_DestroyLock(mAutoRegLock); if(mInfoMonitor) nsAutoMonitor::DestroyMonitor(mInfoMonitor); if(mAdditionalManagersLock) PR_DestroyLock(mAdditionalManagersLock); gInterfaceInfoManager = nsnull; #ifdef DEBUG xptiInterfaceInfo::DEBUG_ShutdownNotification(); gCallCount = 0; #endif } static nsresult GetDirectoryFromDirService(const char* codename, nsILocalFile** aDir) { NS_ASSERTION(codename,"loser!"); NS_ASSERTION(aDir,"loser!"); nsresult rv; nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; return dirService->Get(codename, NS_GET_IID(nsILocalFile), (void**) aDir); } static PRBool AppendFromDirServiceList(const char* codename, nsISupportsArray* aPath) { NS_ASSERTION(codename,"loser!"); NS_ASSERTION(aPath,"loser!"); nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); if(!dirService) return PR_FALSE; nsCOMPtr fileList; dirService->Get(codename, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(fileList)); if(!fileList) return PR_FALSE; PRBool more; while(NS_SUCCEEDED(fileList->HasMoreElements(&more)) && more) { nsCOMPtr dir; fileList->GetNext(getter_AddRefs(dir)); if(!dir || !aPath->AppendElement(dir)) return PR_FALSE; } return PR_TRUE; } // static PRBool xptiInterfaceInfoManager::BuildFileSearchPath(nsISupportsArray** aPath) { #ifdef DEBUG NS_ASSERTION(!gCallCount++, "Expected only one call!"); #endif nsCOMPtr searchPath; NS_NewISupportsArray(getter_AddRefs(searchPath)); if(!searchPath) return PR_FALSE; nsCOMPtr compDir; // Always put components directory first if(NS_FAILED(GetDirectoryFromDirService(NS_XPCOM_COMPONENT_DIR, getter_AddRefs(compDir))) || !searchPath->AppendElement(compDir)) { return PR_FALSE; } // Add additional plugins dirs // No error checking here since this is optional in some embeddings // Add the GRE's component directory to searchPath if the // application is using an GRE. // An application indicates that it's using an GRE by returning // a valid nsIFile via it's directory service provider interface. // // Please see http://www.mozilla.org/projects/embedding/MRE.html // for more info. on GREs // nsCOMPtr greComponentDirectory; nsresult rv = GetDirectoryFromDirService(NS_GRE_COMPONENT_DIR, getter_AddRefs(greComponentDirectory)); if(NS_SUCCEEDED(rv) && greComponentDirectory) { // make sure we only append a directory if its a different one PRBool equalsCompDir = PR_FALSE; greComponentDirectory->Equals(compDir, &equalsCompDir); if(!equalsCompDir) searchPath->AppendElement(greComponentDirectory); } (void)AppendFromDirServiceList(NS_XPCOM_COMPONENT_DIR_LIST, searchPath); (void)AppendFromDirServiceList(NS_APP_PLUGINS_DIR_LIST, searchPath); NS_ADDREF(*aPath = searchPath); return PR_TRUE; } PRBool xptiInterfaceInfoManager::GetCloneOfManifestLocation(nsILocalFile** aFile) { // We *trust* that this will not change! nsCOMPtr lf; nsresult rv = GetDirectoryFromDirService(NS_XPCOM_XPTI_REGISTRY_FILE, getter_AddRefs(lf)); if (NS_FAILED(rv)) return PR_FALSE; rv = xptiCloneLocalFile(lf, aFile); if (NS_FAILED(rv)) return PR_FALSE; return PR_TRUE; } PRBool xptiInterfaceInfoManager::GetApplicationDir(nsILocalFile** aDir) { // We *trust* that this will not change! return NS_SUCCEEDED(GetDirectoryFromDirService(NS_XPCOM_CURRENT_PROCESS_DIR, aDir)); } PRBool xptiInterfaceInfoManager::BuildFileList(nsISupportsArray* aSearchPath, nsISupportsArray** aFileList) { NS_ASSERTION(aFileList, "loser!"); nsresult rv; nsCOMPtr fileList = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID); if(!fileList) return PR_FALSE; PRUint32 pathCount; if(NS_FAILED(aSearchPath->Count(&pathCount))) return PR_FALSE; for(PRUint32 i = 0; i < pathCount; i++) { nsCOMPtr dir; rv = xptiCloneElementAsLocalFile(aSearchPath, i, getter_AddRefs(dir)); if(NS_FAILED(rv) || !dir) return PR_FALSE; nsCOMPtr entries; rv = dir->GetDirectoryEntries(getter_AddRefs(entries)); if(NS_FAILED(rv) || !entries) continue; PRUint32 count = 0; PRBool hasMore; while(NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr sup; entries->GetNext(getter_AddRefs(sup)); if(!sup) return PR_FALSE; nsCOMPtr file = do_QueryInterface(sup); if(!file) return PR_FALSE; PRBool isFile; if(NS_FAILED(file->IsFile(&isFile)) || !isFile) { continue; } nsCAutoString name; if(NS_FAILED(file->GetNativeLeafName(name))) return PR_FALSE; if(xptiFileType::IsUnknown(name.get())) continue; LOG_AUTOREG(("found file: %s\n", name.get())); if(!fileList->InsertElementAt(file, count)) return PR_FALSE; ++count; } } NS_ADDREF(*aFileList = fileList); return PR_TRUE; } XPTHeader* xptiInterfaceInfoManager::ReadXPTFile(nsILocalFile* aFile, xptiWorkingSet* aWorkingSet) { NS_ASSERTION(aFile, "loser!"); XPTHeader *header = nsnull; char *whole = nsnull; PRFileDesc* fd = nsnull; XPTState *state = nsnull; XPTCursor cursor; PRInt32 flen; PRInt64 fileSize; PRBool saveFollowLinks; aFile->GetFollowLinks(&saveFollowLinks); aFile->SetFollowLinks(PR_TRUE); if(NS_FAILED(aFile->GetFileSize(&fileSize)) || !(flen = nsInt64(fileSize))) { aFile->SetFollowLinks(saveFollowLinks); return nsnull; } whole = new char[flen]; if (!whole) { aFile->SetFollowLinks(saveFollowLinks); return nsnull; } // all exits from on here should be via 'goto out' if(NS_FAILED(aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd)) || !fd) { goto out; } if(flen > PR_Read(fd, whole, flen)) { goto out; } if(!(state = XPT_NewXDRState(XPT_DECODE, whole, flen))) { goto out; } if(!XPT_MakeCursor(state, XPT_HEADER, 0, &cursor)) { goto out; } if (!XPT_DoHeader(aWorkingSet->GetStructArena(), &cursor, &header)) { header = nsnull; goto out; } out: if(fd) PR_Close(fd); if(state) XPT_DestroyXDRState(state); if(whole) delete [] whole; aFile->SetFollowLinks(saveFollowLinks); return header; } PRBool xptiInterfaceInfoManager::LoadFile(const xptiTypelib& aTypelibRecord, xptiWorkingSet* aWorkingSet) { if(!aWorkingSet) aWorkingSet = &mWorkingSet; if(!aWorkingSet->IsValid()) return PR_FALSE; xptiFile* fileRecord = &aWorkingSet->GetFileAt(aTypelibRecord.GetFileIndex()); xptiZipItem* zipItem = nsnull; nsCOMPtr file; if(NS_FAILED(aWorkingSet->GetCloneOfDirectoryAt(fileRecord->GetDirectory(), getter_AddRefs(file))) || !file) return PR_FALSE; if(NS_FAILED(file->AppendNative(nsDependentCString(fileRecord->GetName())))) return PR_FALSE; XPTHeader* header; if(aTypelibRecord.IsZip()) { zipItem = &aWorkingSet->GetZipItemAt(aTypelibRecord.GetZipItemIndex()); // See the big comment below in the 'non-zip' case... if(zipItem->GetGuts()) { NS_ERROR("Trying to load an xpt file from a zip twice"); // Force an autoreg on next run (void) xptiManifest::Delete(this); return PR_FALSE; } LOG_LOAD(("# loading zip item %s::%s\n", fileRecord->GetName(), zipItem->GetName())); nsCOMPtr loader = do_GetService(NS_ZIPLOADER_CONTRACTID); if (loader) { nsresult rv; nsCOMPtr stream; rv = loader->LoadEntry(file, zipItem->GetName(), getter_AddRefs(stream)); if (NS_FAILED(rv)) return PR_FALSE; header = xptiZipLoader::ReadXPTFileFromInputStream(stream, aWorkingSet); } else { header = nsnull; NS_WARNING("Could not load XPT Zip loader"); } } else { // The file would only have guts already if we previously failed to // find an interface info in a file where the manifest claimed it was // going to be. // // Normally, when the file gets loaded (and the guts set) then all // interfaces would also be resolved. So, if we are here again for // the same file then there must have been some interface that was // expected but not present. Now we are explicitly trying to find it // and it isn't going to be there this time either. // // This is an assertion style error in a DEBUG build because it shows // that we failed to detect this in autoreg. For release builds (where // autoreg is not run on every startup) it is just bad. But by returning // PR_FALSE we mark this interface as RESOLVE_FAILED and get on with // things without crashing or anything. // // We don't want to do an autoreg here because this is too much of an // edge case (and in that odd case it might autoreg multiple times if // many interfaces had been removed). But, by deleting the manifest we // force the system to get it right on the next run. if(fileRecord->GetGuts()) { NS_ERROR("Trying to load an xpt file twice"); // Force an autoreg on next run (void) xptiManifest::Delete(this); return PR_FALSE; } LOG_LOAD(("^ loading file %s\n", fileRecord->GetName())); header = ReadXPTFile(file, aWorkingSet); } if(!header) return PR_FALSE; if(aTypelibRecord.IsZip()) { // This also allocs zipItem.GetGuts() used below. if(!zipItem->SetHeader(header, aWorkingSet)) return PR_FALSE; } else { // This also allocs fileRecord.GetGuts() used below. if(!fileRecord->SetHeader(header, aWorkingSet)) return PR_FALSE; } // For each interface in the header we want to find the xptiInterfaceInfo // object and set its resolution info. for(PRUint16 i = 0; i < header->num_interfaces; i++) { static const nsID zeroIID = { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }; XPTInterfaceDirectoryEntry* iface = header->interface_directory + i; xptiHashEntry* hashEntry; if(!iface->iid.Equals(zeroIID)) { hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mIIDTable, &iface->iid, PL_DHASH_LOOKUP); } else { hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mNameTable, iface->name, PL_DHASH_LOOKUP); } xptiInterfaceEntry* entry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; if(!entry) { // This one is just not resolved anywhere! continue; } if(aTypelibRecord.IsZip()) zipItem->GetGuts()->SetEntryAt(i, entry); else fileRecord->GetGuts()->SetEntryAt(i, entry); XPTInterfaceDescriptor* descriptor = iface->interface_descriptor; if(descriptor && aTypelibRecord.Equals(entry->GetTypelibRecord())) entry->PartiallyResolveLocked(descriptor, aWorkingSet); } return PR_TRUE; } static int IndexOfFileWithName(const char* aName, const xptiWorkingSet* aWorkingSet) { NS_ASSERTION(aName, "loser!"); for(PRUint32 i = 0; i < aWorkingSet->GetFileCount(); ++i) { if(0 == PL_strcmp(aName, aWorkingSet->GetFileAt(i).GetName())) return i; } return -1; } static int IndexOfDirectoryOfFile(nsISupportsArray* aSearchPath, nsILocalFile* aFile) { nsCOMPtr parent; aFile->GetParent(getter_AddRefs(parent)); if(parent) { PRUint32 count = 0; aSearchPath->Count(&count); NS_ASSERTION(count, "broken search path! bad count"); for(PRUint32 i = 0; i < count; i++) { nsCOMPtr current; aSearchPath->QueryElementAt(i, NS_GET_IID(nsIFile), getter_AddRefs(current)); NS_ASSERTION(current, "broken search path! bad element"); PRBool same; if(NS_SUCCEEDED(parent->Equals(current, &same)) && same) return (int) i; } } NS_ERROR("file not in search directory!"); return -1; } struct SortData { nsISupportsArray* mSearchPath; xptiWorkingSet* mWorkingSet; }; PR_STATIC_CALLBACK(int) xptiSortFileList(const void * p1, const void *p2, void * closure) { nsILocalFile* pFile1 = *((nsILocalFile**) p1); nsILocalFile* pFile2 = *((nsILocalFile**) p2); SortData* data = (SortData*) closure; nsCAutoString name1; nsCAutoString name2; if(NS_FAILED(pFile1->GetNativeLeafName(name1))) { NS_ERROR("way bad, with no happy out!"); return 0; } if(NS_FAILED(pFile2->GetNativeLeafName(name2))) { NS_ERROR("way bad, with no happy out!"); return 0; } int index1 = IndexOfFileWithName(name1.get(), data->mWorkingSet); int index2 = IndexOfFileWithName(name2.get(), data->mWorkingSet); // Get these now in case we need them later. PRBool isXPT1 = xptiFileType::IsXPT(name1.get()); PRBool isXPT2 = xptiFileType::IsXPT(name2.get()); int nameOrder = Compare(name1, name2); // both in workingSet, preserve old order if(index1 != -1 && index2 != -1) return index1 - index2; if(index1 != -1) return 1; if(index2 != -1) return -1; // neither is in workingset // check how they compare in search path order int dirIndex1 = IndexOfDirectoryOfFile(data->mSearchPath, pFile1); int dirIndex2 = IndexOfDirectoryOfFile(data->mSearchPath, pFile2); if(dirIndex1 != dirIndex2) return dirIndex1 - dirIndex2; // .xpt files come before archives (.zip, .jar, etc) if(isXPT1 &&!isXPT2) return -1; if(!isXPT1 && isXPT2) return 1; // neither element is in the workingSet and both are same type and in // the same directory, sort by size PRInt64 size1; PRInt64 size2; if(NS_FAILED(pFile1->GetFileSize(&size1))) { NS_ERROR("way bad, with no happy out!"); return 0; } if(NS_FAILED(pFile2->GetFileSize(&size2))) { NS_ERROR("way bad, with no happy out!"); return 0; } // by size with largest first, or by name if size is the same int sizeDiff = int(PRInt32(nsInt64(size2) - nsInt64(size1))); return sizeDiff != 0 ? sizeDiff : nameOrder; } nsILocalFile** xptiInterfaceInfoManager::BuildOrderedFileArray(nsISupportsArray* aSearchPath, nsISupportsArray* aFileList, xptiWorkingSet* aWorkingSet) { // We want to end up with a file list that starts with the files from // aWorkingSet (but only those that are in aFileList) in the order in // which they appeared in aWorkingSet-> Following those files will be those // files in aFileList which are not in aWorkingSet-> These additional // files will be ordered by file size (larger first) but all .xpt files // will preceed all zipfile of those files not already in the working set. // To do this we will do a fancy sort on a copy of aFileList. nsILocalFile** orderedFileList = nsnull; PRUint32 countOfFilesInFileList; PRUint32 i; NS_ASSERTION(aFileList, "loser!"); NS_ASSERTION(aWorkingSet, "loser!"); NS_ASSERTION(aWorkingSet->IsValid(), "loser!"); if(NS_FAILED(aFileList->Count(&countOfFilesInFileList)) || 0 == countOfFilesInFileList) return nsnull; orderedFileList = (nsILocalFile**) XPT_MALLOC(aWorkingSet->GetStructArena(), sizeof(nsILocalFile*) * countOfFilesInFileList); if(!orderedFileList) return nsnull; // fill our list for sorting for(i = 0; i < countOfFilesInFileList; ++i) { nsCOMPtr file; aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file)); NS_ASSERTION(file, "loser!"); // Intentionally NOT addref'd cuz we know these are pinned in aFileList. orderedFileList[i] = file.get(); } // sort the filelist SortData sortData = {aSearchPath, aWorkingSet}; NS_QuickSort(orderedFileList, countOfFilesInFileList, sizeof(nsILocalFile*), xptiSortFileList, &sortData); return orderedFileList; } xptiInterfaceInfoManager::AutoRegMode xptiInterfaceInfoManager::DetermineAutoRegStrategy(nsISupportsArray* aSearchPath, nsISupportsArray* aFileList, xptiWorkingSet* aWorkingSet) { NS_ASSERTION(aFileList, "loser!"); NS_ASSERTION(aWorkingSet, "loser!"); NS_ASSERTION(aWorkingSet->IsValid(), "loser!"); PRUint32 countOfFilesInWorkingSet = aWorkingSet->GetFileCount(); PRUint32 countOfFilesInFileList; PRUint32 i; PRUint32 k; if(0 == countOfFilesInWorkingSet) { // Loading manifest might have failed. Better safe... return FULL_VALIDATION_REQUIRED; } if(NS_FAILED(aFileList->Count(&countOfFilesInFileList))) { NS_ERROR("unexpected!"); return FULL_VALIDATION_REQUIRED; } if(countOfFilesInFileList == countOfFilesInWorkingSet) { // try to determine if *no* files are new or changed. PRBool same = PR_TRUE; for(i = 0; i < countOfFilesInFileList && same; ++i) { nsCOMPtr file; aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file)); NS_ASSERTION(file, "loser!"); PRInt64 size; PRInt64 date; nsCAutoString name; PRUint32 directory; if(NS_FAILED(file->GetFileSize(&size)) || NS_FAILED(file->GetLastModifiedTime(&date)) || NS_FAILED(file->GetNativeLeafName(name)) || !aWorkingSet->FindDirectoryOfFile(file, &directory)) { NS_ERROR("unexpected!"); return FULL_VALIDATION_REQUIRED; } for(k = 0; k < countOfFilesInWorkingSet; ++k) { xptiFile& target = aWorkingSet->GetFileAt(k); if(directory == target.GetDirectory() && name.Equals(target.GetName())) { if(nsInt64(size) != target.GetSize() || nsInt64(date) != target.GetDate()) same = PR_FALSE; break; } } // failed to find our file in the workingset? if(k == countOfFilesInWorkingSet) same = PR_FALSE; } if(same) return NO_FILES_CHANGED; } else if(countOfFilesInFileList > countOfFilesInWorkingSet) { // try to determine if the only changes are additional new files // XXX Wimping out and doing this as a separate walk through the lists. PRBool same = PR_TRUE; for(i = 0; i < countOfFilesInWorkingSet && same; ++i) { xptiFile& target = aWorkingSet->GetFileAt(i); for(k = 0; k < countOfFilesInFileList; ++k) { nsCOMPtr file; aFileList->QueryElementAt(k, NS_GET_IID(nsILocalFile), getter_AddRefs(file)); NS_ASSERTION(file, "loser!"); nsCAutoString name; PRInt64 size; PRInt64 date; if(NS_FAILED(file->GetFileSize(&size)) || NS_FAILED(file->GetLastModifiedTime(&date)) || NS_FAILED(file->GetNativeLeafName(name))) { NS_ERROR("unexpected!"); return FULL_VALIDATION_REQUIRED; } PRBool sameName = name.Equals(target.GetName()); if(sameName) { if(nsInt64(size) != target.GetSize() || nsInt64(date) != target.GetDate()) same = PR_FALSE; break; } } // failed to find our file in the file list? if(k == countOfFilesInFileList) same = PR_FALSE; } if(same) return FILES_ADDED_ONLY; } return FULL_VALIDATION_REQUIRED; } PRBool xptiInterfaceInfoManager::AddOnlyNewFilesFromFileList(nsISupportsArray* aSearchPath, nsISupportsArray* aFileList, xptiWorkingSet* aWorkingSet) { nsILocalFile** orderedFileArray; PRUint32 countOfFilesInFileList; PRUint32 i; NS_ASSERTION(aFileList, "loser!"); NS_ASSERTION(aWorkingSet, "loser!"); NS_ASSERTION(aWorkingSet->IsValid(), "loser!"); if(NS_FAILED(aFileList->Count(&countOfFilesInFileList))) return PR_FALSE; NS_ASSERTION(countOfFilesInFileList, "loser!"); NS_ASSERTION(countOfFilesInFileList > aWorkingSet->GetFileCount(), "loser!"); orderedFileArray = BuildOrderedFileArray(aSearchPath, aFileList, aWorkingSet); if(!orderedFileArray) return PR_FALSE; // Make enough space in aWorkingset for additions to xptiFile array. if(!aWorkingSet->ExtendFileArray(countOfFilesInFileList)) return PR_FALSE; // For each file that is not already in our working set, add any valid // interfaces that don't conflict with previous interfaces added. for(i = 0; i < countOfFilesInFileList; i++) { nsILocalFile* file = orderedFileArray[i]; nsCAutoString name; PRInt64 size; PRInt64 date; PRUint32 dir; if(NS_FAILED(file->GetFileSize(&size)) || NS_FAILED(file->GetLastModifiedTime(&date)) || NS_FAILED(file->GetNativeLeafName(name)) || !aWorkingSet->FindDirectoryOfFile(file, &dir)) { return PR_FALSE; } if(xptiWorkingSet::NOT_FOUND != aWorkingSet->FindFile(dir, name.get())) { // This file was found in the working set, so skip it. continue; } LOG_AUTOREG((" finding interfaces in new file: %s\n", name.get())); xptiFile fileRecord; fileRecord = xptiFile(nsInt64(size), nsInt64(date), dir, name.get(), aWorkingSet); if(xptiFileType::IsXPT(fileRecord.GetName())) { XPTHeader* header = ReadXPTFile(file, aWorkingSet); if(!header) { // XXX do something! NS_ERROR(""); continue; } xptiTypelib typelibRecord; typelibRecord.Init(aWorkingSet->GetFileCount()); PRBool AddedFile = PR_FALSE; if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) { NS_ASSERTION(!header->num_interfaces,"bad libxpt"); LOG_AUTOREG((" file is version %d.%d Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION)); } for(PRUint16 k = 0; k < header->num_interfaces; k++) { xptiInterfaceEntry* entry = nsnull; if(!VerifyAndAddEntryIfNew(aWorkingSet, header->interface_directory + k, typelibRecord, &entry)) return PR_FALSE; if(!entry) continue; // If this is the first interface we found for this file then // setup the fileRecord for the header and infos. if(!AddedFile) { if(!fileRecord.SetHeader(header, aWorkingSet)) { // XXX that would be bad. return PR_FALSE; } AddedFile = PR_TRUE; } fileRecord.GetGuts()->SetEntryAt(k, entry); } // This will correspond to typelibRecord above. aWorkingSet->AppendFile(fileRecord); } else // its another kind of archive { nsCOMPtr loader = do_GetService(NS_ZIPLOADER_CONTRACTID); if (loader) { nsresult rv; nsCOMPtr sink = new xptiZipLoaderSink(this, aWorkingSet); if (!sink) return PR_FALSE; rv = loader->EnumerateEntries(file, sink); if (NS_FAILED(rv)) return PR_FALSE; // This will correspond to typelibRecord used in // xptiInterfaceInfoManager::FoundEntry. aWorkingSet->AppendFile(fileRecord); } else { NS_WARNING("Could not load XPT Zip loader"); } } } return PR_TRUE; } PRBool xptiInterfaceInfoManager::DoFullValidationMergeFromFileList(nsISupportsArray* aSearchPath, nsISupportsArray* aFileList, xptiWorkingSet* aWorkingSet) { nsILocalFile** orderedFileArray; PRUint32 countOfFilesInFileList; PRUint32 i; NS_ASSERTION(aFileList, "loser!"); if(!aWorkingSet->IsValid()) return PR_FALSE; if(NS_FAILED(aFileList->Count(&countOfFilesInFileList))) return PR_FALSE; if(!countOfFilesInFileList) { // maybe there are no xpt files to register. // a minimal install would have this case. return PR_TRUE; } orderedFileArray = BuildOrderedFileArray(aSearchPath, aFileList, aWorkingSet); if(!orderedFileArray) return PR_FALSE; // DEBUG_DumpFileArray(orderedFileArray, countOfFilesInFileList); // Make space in aWorkingset for a new xptiFile array. if(!aWorkingSet->NewFileArray(countOfFilesInFileList)) return PR_FALSE; aWorkingSet->ClearZipItems(); aWorkingSet->ClearHashTables(); // For each file, add any valid interfaces that don't conflict with // previous interfaces added. for(i = 0; i < countOfFilesInFileList; i++) { nsILocalFile* file = orderedFileArray[i]; nsCAutoString name; PRInt64 size; PRInt64 date; PRUint32 dir; if(NS_FAILED(file->GetFileSize(&size)) || NS_FAILED(file->GetLastModifiedTime(&date)) || NS_FAILED(file->GetNativeLeafName(name)) || !aWorkingSet->FindDirectoryOfFile(file, &dir)) { return PR_FALSE; } LOG_AUTOREG((" finding interfaces in file: %s\n", name.get())); xptiFile fileRecord; fileRecord = xptiFile(nsInt64(size), nsInt64(date), dir, name.get(), aWorkingSet); // printf("* found %s\n", fileRecord.GetName()); if(xptiFileType::IsXPT(fileRecord.GetName())) { XPTHeader* header = ReadXPTFile(file, aWorkingSet); if(!header) { // XXX do something! NS_ERROR("Unable to read an XPT file, turn logging on to see which file"); LOG_AUTOREG((" unable to read file\n")); continue; } xptiTypelib typelibRecord; typelibRecord.Init(aWorkingSet->GetFileCount()); PRBool AddedFile = PR_FALSE; if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) { NS_ASSERTION(!header->num_interfaces,"bad libxpt"); LOG_AUTOREG((" file is version %d.%d Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION)); } for(PRUint16 k = 0; k < header->num_interfaces; k++) { xptiInterfaceEntry* entry = nsnull; if(!VerifyAndAddEntryIfNew(aWorkingSet, header->interface_directory + k, typelibRecord, &entry)) return PR_FALSE; if(!entry) continue; // If this is the first interface we found for this file then // setup the fileRecord for the header and infos. if(!AddedFile) { if(!fileRecord.SetHeader(header, aWorkingSet)) { // XXX that would be bad. return PR_FALSE; } AddedFile = PR_TRUE; } fileRecord.GetGuts()->SetEntryAt(k, entry); } // This will correspond to typelibRecord above. aWorkingSet->AppendFile(fileRecord); } else { nsCOMPtr loader = do_GetService(NS_ZIPLOADER_CONTRACTID); if (loader) { nsresult rv; nsCOMPtr sink = new xptiZipLoaderSink(this, aWorkingSet); if (!sink) return PR_FALSE; rv = loader->EnumerateEntries(file, sink); if (NS_FAILED(rv)) return PR_FALSE; // This will correspond to typelibRecord used in // xptiInterfaceInfoManager::FoundEntry. aWorkingSet->AppendFile(fileRecord); } else { NS_WARNING("Could not load XPT Zip loader"); } } } return PR_TRUE; } NS_IMPL_ISUPPORTS1(xptiZipLoaderSink, nsIXPTLoaderSink) // implement nsIXPTLoader NS_IMETHODIMP xptiZipLoaderSink::FoundEntry(const char* entryName, PRInt32 index, nsIInputStream *aStream) { XPTHeader *header = xptiZipLoader::ReadXPTFileFromInputStream(aStream, mWorkingSet); if (!header) return NS_ERROR_OUT_OF_MEMORY; if (!mManager->FoundZipEntry(entryName, index, header, mWorkingSet)) return NS_ERROR_FAILURE; return NS_OK; } // implement xptiEntrySink PRBool xptiInterfaceInfoManager::FoundZipEntry(const char* entryName, int index, XPTHeader* header, xptiWorkingSet* aWorkingSet) { NS_ASSERTION(entryName, "loser!"); NS_ASSERTION(header, "loser!"); NS_ASSERTION(aWorkingSet, "loser!"); int countOfInterfacesAddedForItem = 0; xptiZipItem zipItemRecord(entryName, aWorkingSet); LOG_AUTOREG((" finding interfaces in file: %s\n", entryName)); if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) { NS_ASSERTION(!header->num_interfaces,"bad libxpt"); LOG_AUTOREG((" file is version %d.%d. Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION)); } if(!header->num_interfaces) { // We are not interested in files without interfaces. return PR_TRUE; } xptiTypelib typelibRecord; typelibRecord.Init(aWorkingSet->GetFileCount(), aWorkingSet->GetZipItemCount()); for(PRUint16 k = 0; k < header->num_interfaces; k++) { xptiInterfaceEntry* entry = nsnull; if(!VerifyAndAddEntryIfNew(aWorkingSet, header->interface_directory + k, typelibRecord, &entry)) return PR_FALSE; if(!entry) continue; // If this is the first interface we found for this item // then setup the zipItemRecord for the header and infos. if(!countOfInterfacesAddedForItem) { // XXX fix this! if(!zipItemRecord.SetHeader(header, aWorkingSet)) { // XXX that would be bad. return PR_FALSE; } } // zipItemRecord.GetGuts()->SetEntryAt(k, entry); ++countOfInterfacesAddedForItem; } if(countOfInterfacesAddedForItem) { if(!aWorkingSet->GetZipItemFreeSpace()) { if(!aWorkingSet->ExtendZipItemArray( aWorkingSet->GetZipItemCount() + 20)) { // out of space! return PR_FALSE; } } aWorkingSet->AppendZipItem(zipItemRecord); } return PR_TRUE; } PRBool xptiInterfaceInfoManager::VerifyAndAddEntryIfNew(xptiWorkingSet* aWorkingSet, XPTInterfaceDirectoryEntry* iface, const xptiTypelib& typelibRecord, xptiInterfaceEntry** entryAdded) { NS_ASSERTION(iface, "loser!"); NS_ASSERTION(entryAdded, "loser!"); *entryAdded = nsnull; if(!iface->interface_descriptor) { // Not resolved, ignore this one. // XXX full logging might note this... return PR_TRUE; } xptiHashEntry* hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mIIDTable, &iface->iid, PL_DHASH_LOOKUP); xptiInterfaceEntry* entry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; if(entry) { // XXX validate this info to find possible inconsistencies LOG_AUTOREG((" ignoring repeated interface: %s\n", iface->name)); return PR_TRUE; } // Build a new xptiInterfaceEntry object and hook it up. entry = xptiInterfaceEntry::NewEntry(iface->name, strlen(iface->name), iface->iid, typelibRecord, aWorkingSet); if(!entry) { // XXX bad! return PR_FALSE; } //XXX We should SetHeader too as part of the validation, no? entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags)); // Add our entry to the iid hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mNameTable, entry->GetTheName(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; // Add our entry to the name hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mIIDTable, entry->GetTheIID(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; *entryAdded = entry; LOG_AUTOREG((" added interface: %s\n", iface->name)); return PR_TRUE; } // local struct used to pass two pointers as one pointer struct TwoWorkingSets { TwoWorkingSets(xptiWorkingSet* src, xptiWorkingSet* dest) : aSrcWorkingSet(src), aDestWorkingSet(dest) {} xptiWorkingSet* aSrcWorkingSet; xptiWorkingSet* aDestWorkingSet; }; PR_STATIC_CALLBACK(PLDHashOperator) xpti_Merger(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { xptiInterfaceEntry* srcEntry = ((xptiHashEntry*)hdr)->value; xptiWorkingSet* aSrcWorkingSet = ((TwoWorkingSets*)arg)->aSrcWorkingSet; xptiWorkingSet* aDestWorkingSet = ((TwoWorkingSets*)arg)->aDestWorkingSet; xptiHashEntry* hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aDestWorkingSet->mIIDTable, srcEntry->GetTheIID(), PL_DHASH_LOOKUP); xptiInterfaceEntry* destEntry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; if(destEntry) { // Let's see if this is referring to the same exact typelib const char* destFilename = aDestWorkingSet->GetTypelibFileName(destEntry->GetTypelibRecord()); const char* srcFilename = aSrcWorkingSet->GetTypelibFileName(srcEntry->GetTypelibRecord()); if(0 == PL_strcmp(destFilename, srcFilename) && (destEntry->GetTypelibRecord().GetZipItemIndex() == srcEntry->GetTypelibRecord().GetZipItemIndex())) { // This is the same item. // But... Let's make sure they didn't change the interface name. // There are wacky developers that do stuff like that! if(0 == PL_strcmp(destEntry->GetTheName(), srcEntry->GetTheName())) return PL_DHASH_NEXT; } } // Clone the xptiInterfaceEntry into our destination WorkingSet. xptiTypelib typelibRecord; uint16 fileIndex = srcEntry->GetTypelibRecord().GetFileIndex(); uint16 zipItemIndex = srcEntry->GetTypelibRecord().GetZipItemIndex(); fileIndex += aDestWorkingSet->mFileMergeOffsetMap[fileIndex]; // If it is not a zipItem, then the original index is fine. if(srcEntry->GetTypelibRecord().IsZip()) zipItemIndex += aDestWorkingSet->mZipItemMergeOffsetMap[zipItemIndex]; typelibRecord.Init(fileIndex, zipItemIndex); destEntry = xptiInterfaceEntry::NewEntry(*srcEntry, typelibRecord, aDestWorkingSet); if(!destEntry) { // XXX bad! should log return PL_DHASH_NEXT; } // Add our entry to the iid hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aDestWorkingSet->mNameTable, destEntry->GetTheName(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = destEntry; // Add our entry to the name hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aDestWorkingSet->mIIDTable, destEntry->GetTheIID(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = destEntry; return PL_DHASH_NEXT; } PRBool xptiInterfaceInfoManager::MergeWorkingSets(xptiWorkingSet* aDestWorkingSet, xptiWorkingSet* aSrcWorkingSet) { PRUint32 i; // Combine file lists. PRUint32 originalFileCount = aDestWorkingSet->GetFileCount(); PRUint32 additionalFileCount = aSrcWorkingSet->GetFileCount(); // Create a new array big enough to hold both lists and copy existing files if(additionalFileCount) { if(!aDestWorkingSet->ExtendFileArray(originalFileCount + additionalFileCount)) return PR_FALSE; // Now we are where we started, but we know we have enough space. // Prepare offset array for later fixups. // NOTE: Storing with dest, but alloc'ing from src. This is intentional. aDestWorkingSet->mFileMergeOffsetMap = (PRUint32*) XPT_CALLOC(aSrcWorkingSet->GetStructArena(), additionalFileCount * sizeof(PRUint32)); if(!aDestWorkingSet->mFileMergeOffsetMap) return PR_FALSE; } for(i = 0; i < additionalFileCount; ++i) { xptiFile& srcFile = aSrcWorkingSet->GetFileAt(i); PRUint32 k; for(k = 0; k < originalFileCount; ++k) { // If file (with same name, date, and time) is in both lists // then reuse that record. xptiFile& destFile = aDestWorkingSet->GetFileAt(k); if(srcFile.Equals(destFile)) { aDestWorkingSet->mFileMergeOffsetMap[i] = k - i; break; } } if(k == originalFileCount) { // No match found, tack it on the end. PRUint32 newIndex = aDestWorkingSet->GetFileCount(); aDestWorkingSet->AppendFile(xptiFile(srcFile, aDestWorkingSet)); // Fixup the merge offset map. aDestWorkingSet->mFileMergeOffsetMap[i] = newIndex - i; } } // Combine ZipItem lists. PRUint32 originalZipItemCount = aDestWorkingSet->GetZipItemCount(); PRUint32 additionalZipItemCount = aSrcWorkingSet->GetZipItemCount(); // Create a new array big enough to hold both lists and copy existing ZipItems if(additionalZipItemCount) { if(!aDestWorkingSet->ExtendZipItemArray(originalZipItemCount + additionalZipItemCount)) return PR_FALSE; // Now we are where we started, but we know we have enough space. // Prepare offset array for later fixups. // NOTE: Storing with dest, but alloc'ing from src. This is intentional. aDestWorkingSet->mZipItemMergeOffsetMap = (PRUint32*) XPT_CALLOC(aSrcWorkingSet->GetStructArena(), additionalZipItemCount * sizeof(PRUint32)); if(!aDestWorkingSet->mZipItemMergeOffsetMap) return PR_FALSE; } for(i = 0; i < additionalZipItemCount; ++i) { xptiZipItem& srcZipItem = aSrcWorkingSet->GetZipItemAt(i); PRUint32 k; for(k = 0; k < originalZipItemCount; ++k) { // If ZipItem (with same name) is in both lists // then reuse that record. xptiZipItem& destZipItem = aDestWorkingSet->GetZipItemAt(k); if(srcZipItem.Equals(destZipItem)) { aDestWorkingSet->mZipItemMergeOffsetMap[i] = k - i; break; } } if(k == originalZipItemCount) { // No match found, tack it on the end. PRUint32 newIndex = aDestWorkingSet->GetZipItemCount(); aDestWorkingSet->AppendZipItem( xptiZipItem(srcZipItem, aDestWorkingSet)); // Fixup the merge offset map. aDestWorkingSet->mZipItemMergeOffsetMap[i] = newIndex - i; } } // Migrate xptiInterfaceInfos TwoWorkingSets sets(aSrcWorkingSet, aDestWorkingSet); PL_DHashTableEnumerate(aSrcWorkingSet->mNameTable, xpti_Merger, &sets); return PR_TRUE; } PRBool xptiInterfaceInfoManager::DEBUG_DumpFileList(nsISupportsArray* aFileList) { PRUint32 count; if(NS_FAILED(aFileList->Count(&count))) return PR_FALSE; for(PRUint32 i = 0; i < count; i++) { nsCOMPtr file; aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file)); if(!file) return PR_FALSE; nsCAutoString name; if(NS_FAILED(file->GetNativeLeafName(name))) return PR_FALSE; printf("* found %s\n", name.get()); } return PR_TRUE; } PRBool xptiInterfaceInfoManager::DEBUG_DumpFileListInWorkingSet(xptiWorkingSet* aWorkingSet) { for(PRUint16 i = 0; i < aWorkingSet->GetFileCount(); ++i) { xptiFile& record = aWorkingSet->GetFileAt(i); printf("! has %s\n", record.GetName()); } return PR_TRUE; } PRBool xptiInterfaceInfoManager::DEBUG_DumpFileArray(nsILocalFile** aFileArray, PRUint32 count) { // dump the sorted list for(PRUint32 i = 0; i < count; ++i) { nsILocalFile* file = aFileArray[i]; nsCAutoString name; if(NS_FAILED(file->GetNativeLeafName(name))) return PR_FALSE; printf("found file: %s\n", name.get()); } return PR_TRUE; } /***************************************************************************/ // static void xptiInterfaceInfoManager::WriteToLog(const char *fmt, ...) { if(!gInterfaceInfoManager) return; PRFileDesc* fd = gInterfaceInfoManager->GetOpenLogFile(); if(fd) { va_list ap; va_start(ap, fmt); PR_vfprintf(fd, fmt, ap); va_end(ap); } } PR_STATIC_CALLBACK(PLDHashOperator) xpti_ResolvedFileNameLogger(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value; xptiInterfaceInfoManager* mgr = (xptiInterfaceInfoManager*) arg; if(entry->IsFullyResolved()) { xptiWorkingSet* aWorkingSet = mgr->GetWorkingSet(); PRFileDesc* fd = mgr->GetOpenLogFile(); const xptiTypelib& typelib = entry->GetTypelibRecord(); const char* filename = aWorkingSet->GetFileAt(typelib.GetFileIndex()).GetName(); if(typelib.IsZip()) { const char* zipItemName = aWorkingSet->GetZipItemAt(typelib.GetZipItemIndex()).GetName(); PR_fprintf(fd, "xpti used interface: %s from %s::%s\n", entry->GetTheName(), filename, zipItemName); } else { PR_fprintf(fd, "xpti used interface: %s from %s\n", entry->GetTheName(), filename); } } return PL_DHASH_NEXT; } void xptiInterfaceInfoManager::LogStats() { PRUint32 i; // This sets what will be returned by GetOpenLogFile(). xptiAutoLog autoLog(this, mStatsLogFile, PR_FALSE); PRFileDesc* fd = GetOpenLogFile(); if(!fd) return; // Show names of xpt (only) files from which at least one interface // was resolved. PRUint32 fileCount = mWorkingSet.GetFileCount(); for(i = 0; i < fileCount; ++i) { xptiFile& f = mWorkingSet.GetFileAt(i); if(f.GetGuts()) PR_fprintf(fd, "xpti used file: %s\n", f.GetName()); } PR_fprintf(fd, "\n"); // Show names of xptfiles loaded from zips from which at least // one interface was resolved. PRUint32 zipItemCount = mWorkingSet.GetZipItemCount(); for(i = 0; i < zipItemCount; ++i) { xptiZipItem& zi = mWorkingSet.GetZipItemAt(i); if(zi.GetGuts()) PR_fprintf(fd, "xpti used file from zip: %s\n", zi.GetName()); } PR_fprintf(fd, "\n"); // Show name of each interface that was fully resolved and the name // of the file and (perhaps) zip from which it was loaded. PL_DHashTableEnumerate(mWorkingSet.mNameTable, xpti_ResolvedFileNameLogger, this); } /***************************************************************************/ // this is a private helper static nsresult EntryToInfo(xptiInterfaceEntry* entry, nsIInterfaceInfo **_retval) { xptiInterfaceInfo* info; nsresult rv; if(!entry) { *_retval = nsnull; return NS_ERROR_FAILURE; } rv = entry->GetInterfaceInfo(&info); if(NS_FAILED(rv)) return rv; // Transfer the AddRef done by GetInterfaceInfo. *_retval = static_cast(info); return NS_OK; } xptiInterfaceEntry* xptiInterfaceInfoManager::GetInterfaceEntryForIID(const nsIID *iid) { xptiHashEntry *hashEntry = (xptiHashEntry*) PL_DHashTableOperate(mWorkingSet.mIIDTable, iid, PL_DHASH_LOOKUP); return PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; } /* nsIInterfaceInfo getInfoForIID (in nsIIDPtr iid); */ NS_IMETHODIMP xptiInterfaceInfoManager::GetInfoForIID(const nsIID * iid, nsIInterfaceInfo **_retval) { NS_ASSERTION(iid, "bad param"); NS_ASSERTION(_retval, "bad param"); xptiInterfaceEntry* entry = GetInterfaceEntryForIID(iid); return EntryToInfo(entry, _retval); } /* nsIInterfaceInfo getInfoForName (in string name); */ NS_IMETHODIMP xptiInterfaceInfoManager::GetInfoForName(const char *name, nsIInterfaceInfo **_retval) { NS_ASSERTION(name, "bad param"); NS_ASSERTION(_retval, "bad param"); xptiHashEntry* hashEntry = (xptiHashEntry*) PL_DHashTableOperate(mWorkingSet.mNameTable, name, PL_DHASH_LOOKUP); xptiInterfaceEntry* entry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; return EntryToInfo(entry, _retval); } /* nsIIDPtr getIIDForName (in string name); */ NS_IMETHODIMP xptiInterfaceInfoManager::GetIIDForName(const char *name, nsIID * *_retval) { NS_ASSERTION(name, "bad param"); NS_ASSERTION(_retval, "bad param"); xptiHashEntry* hashEntry = (xptiHashEntry*) PL_DHashTableOperate(mWorkingSet.mNameTable, name, PL_DHASH_LOOKUP); xptiInterfaceEntry* entry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; if(!entry) { *_retval = nsnull; return NS_ERROR_FAILURE; } return entry->GetIID(_retval); } /* string getNameForIID (in nsIIDPtr iid); */ NS_IMETHODIMP xptiInterfaceInfoManager::GetNameForIID(const nsIID * iid, char **_retval) { NS_ASSERTION(iid, "bad param"); NS_ASSERTION(_retval, "bad param"); xptiHashEntry* hashEntry = (xptiHashEntry*) PL_DHashTableOperate(mWorkingSet.mIIDTable, iid, PL_DHASH_LOOKUP); xptiInterfaceEntry* entry = PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value; if(!entry) { *_retval = nsnull; return NS_ERROR_FAILURE; } return entry->GetName(_retval); } PR_STATIC_CALLBACK(PLDHashOperator) xpti_ArrayAppender(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value; nsISupportsArray* array = (nsISupportsArray*) arg; nsCOMPtr ii; if(NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) array->AppendElement(ii); return PL_DHASH_NEXT; } /* nsIEnumerator enumerateInterfaces (); */ NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateInterfaces(nsIEnumerator **_retval) { // I didn't want to incur the size overhead of using nsHashtable just to // make building an enumerator easier. So, this code makes a snapshot of // the table using an nsISupportsArray and builds an enumerator for that. // We can afford this transient cost. nsCOMPtr array; NS_NewISupportsArray(getter_AddRefs(array)); if(!array) return NS_ERROR_UNEXPECTED; PL_DHashTableEnumerate(mWorkingSet.mNameTable, xpti_ArrayAppender, array); return array->Enumerate(_retval); } struct ArrayAndPrefix { nsISupportsArray* array; const char* prefix; PRUint32 length; }; PR_STATIC_CALLBACK(PLDHashOperator) xpti_ArrayPrefixAppender(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value; ArrayAndPrefix* args = (ArrayAndPrefix*) arg; const char* name = entry->GetTheName(); if(name != PL_strnstr(name, args->prefix, args->length)) return PL_DHASH_NEXT; nsCOMPtr ii; if(NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) args->array->AppendElement(ii); return PL_DHASH_NEXT; } /* nsIEnumerator enumerateInterfacesWhoseNamesStartWith (in string prefix); */ NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateInterfacesWhoseNamesStartWith(const char *prefix, nsIEnumerator **_retval) { nsCOMPtr array; NS_NewISupportsArray(getter_AddRefs(array)); if(!array) return NS_ERROR_UNEXPECTED; ArrayAndPrefix args = {array, prefix, PL_strlen(prefix)}; PL_DHashTableEnumerate(mWorkingSet.mNameTable, xpti_ArrayPrefixAppender, &args); return array->Enumerate(_retval); } /* void autoRegisterInterfaces (); */ NS_IMETHODIMP xptiInterfaceInfoManager::AutoRegisterInterfaces() { nsCOMPtr fileList; AutoRegMode mode; PRBool ok; nsAutoLock lock(xptiInterfaceInfoManager::GetAutoRegLock(this)); xptiWorkingSet workingSet(mSearchPath); if(!workingSet.IsValid()) return NS_ERROR_UNEXPECTED; // This sets what will be returned by GetOpenLogFile(). xptiAutoLog autoLog(this, mAutoRegLogFile, PR_TRUE); LOG_AUTOREG(("start AutoRegister\n")); // We re-read the manifest rather than muck with the 'live' one. // It is OK if this fails. // XXX But we should track failure as a warning. ok = xptiManifest::Read(this, &workingSet); LOG_AUTOREG(("read of manifest %s\n", ok ? "successful" : "FAILED")); // Grovel for all the typelibs we can find (in .xpt or .zip, .jar,...). if(!BuildFileList(mSearchPath, getter_AddRefs(fileList)) || !fileList) return NS_ERROR_UNEXPECTED; // DEBUG_DumpFileList(fileList); // Check to see how much work we need to do. mode = DetermineAutoRegStrategy(mSearchPath, fileList, &workingSet); switch(mode) { case NO_FILES_CHANGED: LOG_AUTOREG(("autoreg strategy: no files changed\n")); LOG_AUTOREG(("successful end of AutoRegister\n")); return NS_OK; case FILES_ADDED_ONLY: LOG_AUTOREG(("autoreg strategy: files added only\n")); if(!AddOnlyNewFilesFromFileList(mSearchPath, fileList, &workingSet)) { LOG_AUTOREG(("FAILED to add new files\n")); return NS_ERROR_UNEXPECTED; } break; case FULL_VALIDATION_REQUIRED: LOG_AUTOREG(("autoreg strategy: doing full validation merge\n")); if(!DoFullValidationMergeFromFileList(mSearchPath, fileList, &workingSet)) { LOG_AUTOREG(("FAILED to do full validation\n")); return NS_ERROR_UNEXPECTED; } break; default: NS_ERROR("switch missing a case"); return NS_ERROR_UNEXPECTED; } // Failure to write the manifest is not fatal in production builds. // However, this would require the next startup to find and read all the // xpt files. This will make that startup slower. If this ever becomes a // chronic problem for anyone, then we'll want to figure out why! if(!xptiManifest::Write(this, &workingSet)) { LOG_AUTOREG(("FAILED to write manifest\n")); NS_ERROR("Failed to write xpti manifest!"); } if(!MergeWorkingSets(&mWorkingSet, &workingSet)) { LOG_AUTOREG(("FAILED to merge into live workingset\n")); return NS_ERROR_UNEXPECTED; } // DEBUG_DumpFileListInWorkingSet(mWorkingSet); LOG_AUTOREG(("successful end of AutoRegister\n")); return NS_OK; } /***************************************************************************/ class xptiAdditionalManagersEnumerator : public nsISimpleEnumerator { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR xptiAdditionalManagersEnumerator(); PRBool SizeTo(PRUint32 likelyCount) {return mArray.SizeTo(likelyCount);} PRBool AppendElement(nsIInterfaceInfoManager* element); private: ~xptiAdditionalManagersEnumerator() {} nsSupportsArray mArray; PRUint32 mIndex; PRUint32 mCount; }; NS_IMPL_ISUPPORTS1(xptiAdditionalManagersEnumerator, nsISimpleEnumerator) xptiAdditionalManagersEnumerator::xptiAdditionalManagersEnumerator() : mIndex(0), mCount(0) { } PRBool xptiAdditionalManagersEnumerator::AppendElement(nsIInterfaceInfoManager* element) { if(!mArray.AppendElement(static_cast(element))) return PR_FALSE; mCount++; return PR_TRUE; } /* boolean hasMoreElements (); */ NS_IMETHODIMP xptiAdditionalManagersEnumerator::HasMoreElements(PRBool *_retval) { *_retval = mIndex < mCount; return NS_OK; } /* nsISupports getNext (); */ NS_IMETHODIMP xptiAdditionalManagersEnumerator::GetNext(nsISupports **_retval) { if(!(mIndex < mCount)) { NS_ERROR("Bad nsISimpleEnumerator caller!"); return NS_ERROR_FAILURE; } *_retval = mArray.ElementAt(mIndex++); return *_retval ? NS_OK : NS_ERROR_FAILURE; } /***************************************************************************/ /* void addAdditionalManager (in nsIInterfaceInfoManager manager); */ NS_IMETHODIMP xptiInterfaceInfoManager::AddAdditionalManager(nsIInterfaceInfoManager *manager) { nsCOMPtr weakRef = do_GetWeakReference(manager); nsISupports* ptrToAdd = weakRef ? static_cast(weakRef) : static_cast(manager); { // scoped lock... nsAutoLock lock(mAdditionalManagersLock); PRInt32 index; nsresult rv = mAdditionalManagers.GetIndexOf(ptrToAdd, &index); if(NS_FAILED(rv) || -1 != index) return NS_ERROR_FAILURE; if(!mAdditionalManagers.AppendElement(ptrToAdd)) return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } /* void removeAdditionalManager (in nsIInterfaceInfoManager manager); */ NS_IMETHODIMP xptiInterfaceInfoManager::RemoveAdditionalManager(nsIInterfaceInfoManager *manager) { nsCOMPtr weakRef = do_GetWeakReference(manager); nsISupports* ptrToRemove = weakRef ? static_cast(weakRef) : static_cast(manager); { // scoped lock... nsAutoLock lock(mAdditionalManagersLock); if(!mAdditionalManagers.RemoveElement(ptrToRemove)) return NS_ERROR_FAILURE; } return NS_OK; } /* PRBool hasAdditionalManagers (); */ NS_IMETHODIMP xptiInterfaceInfoManager::HasAdditionalManagers(PRBool *_retval) { PRUint32 count; nsresult rv = mAdditionalManagers.Count(&count); *_retval = count != 0; return rv; } /* nsISimpleEnumerator enumerateAdditionalManagers (); */ NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateAdditionalManagers(nsISimpleEnumerator **_retval) { nsAutoLock lock(mAdditionalManagersLock); PRUint32 count; nsresult rv = mAdditionalManagers.Count(&count); if(NS_FAILED(rv)) return rv; nsCOMPtr enumerator = new xptiAdditionalManagersEnumerator(); if(!enumerator) return NS_ERROR_OUT_OF_MEMORY; enumerator->SizeTo(count); for(PRUint32 i = 0; i < count; /* i incremented in the loop body */) { nsCOMPtr raw = dont_AddRef(mAdditionalManagers.ElementAt(i++)); if(!raw) return NS_ERROR_FAILURE; nsCOMPtr weakRef = do_QueryInterface(raw); if(weakRef) { nsCOMPtr manager = do_QueryReferent(weakRef); if(manager) { if(!enumerator->AppendElement(manager)) return NS_ERROR_FAILURE; } else { // The manager is no more. Remove the element. if(!mAdditionalManagers.RemoveElementAt(--i)) return NS_ERROR_FAILURE; count--; } } else { // We *know* we put a pointer to either a nsIWeakReference or // an nsIInterfaceInfoManager into the array, so we can avoid an // extra QI here and just do a cast. if(!enumerator->AppendElement( reinterpret_cast(raw.get()))) return NS_ERROR_FAILURE; } } NS_ADDREF(*_retval = enumerator); return NS_OK; }