Back to index

lightning-sunbird  0.9+nobinonly
xptiInterfaceInfoManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Mike McCabe <mccabe@netscape.com>
00024  *   John Bandhauer <jband@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /* Implementation of xptiInterfaceInfoManager. */
00041 
00042 #include "xptiprivate.h"
00043 #include "nsDependentString.h"
00044 #include "nsString.h"
00045 
00046 #define NS_ZIPLOADER_CONTRACTID NS_XPTLOADER_CONTRACTID_PREFIX "zip"
00047 
00048 NS_IMPL_THREADSAFE_ISUPPORTS2(xptiInterfaceInfoManager, 
00049                               nsIInterfaceInfoManager,
00050                               nsIInterfaceInfoSuperManager)
00051 
00052 static xptiInterfaceInfoManager* gInterfaceInfoManager = nsnull;
00053 #ifdef DEBUG
00054 static int gCallCount = 0;
00055 #endif
00056 
00057 // static
00058 xptiInterfaceInfoManager*
00059 xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef()
00060 {
00061     if(!gInterfaceInfoManager)
00062     {
00063         nsCOMPtr<nsISupportsArray> searchPath;
00064         BuildFileSearchPath(getter_AddRefs(searchPath));
00065         if(!searchPath)
00066         {
00067             NS_ERROR("can't get xpt search path!");    
00068             return nsnull;
00069         }
00070 
00071         gInterfaceInfoManager = new xptiInterfaceInfoManager(searchPath);
00072         if(gInterfaceInfoManager)
00073             NS_ADDREF(gInterfaceInfoManager);
00074         if(!gInterfaceInfoManager->IsValid())
00075         {
00076             NS_RELEASE(gInterfaceInfoManager);
00077         }
00078         else
00079         {
00080             PRBool mustAutoReg = 
00081                     !xptiManifest::Read(gInterfaceInfoManager, 
00082                                         &gInterfaceInfoManager->mWorkingSet);
00083 #ifdef DEBUG
00084             {
00085             // This sets what will be returned by GetOpenLogFile().
00086             xptiAutoLog autoLog(gInterfaceInfoManager, 
00087                                 gInterfaceInfoManager->mAutoRegLogFile, PR_TRUE);
00088             LOG_AUTOREG(("debug build forced autoreg after %s load of manifest\n", mustAutoReg ? "FAILED" : "successful"));
00089         
00090             mustAutoReg = PR_TRUE;
00091             }
00092 #endif // DEBUG
00093             if(mustAutoReg)
00094                 gInterfaceInfoManager->AutoRegisterInterfaces();
00095         }
00096     }
00097     return gInterfaceInfoManager;
00098 }
00099 
00100 void
00101 xptiInterfaceInfoManager::FreeInterfaceInfoManager()
00102 {
00103     if(gInterfaceInfoManager)
00104         gInterfaceInfoManager->LogStats();
00105 
00106     NS_IF_RELEASE(gInterfaceInfoManager);
00107 }
00108 
00109 PRBool 
00110 xptiInterfaceInfoManager::IsValid()
00111 {
00112     return mWorkingSet.IsValid() &&
00113            mResolveLock &&
00114            mAutoRegLock &&
00115            mInfoMonitor &&
00116            mAdditionalManagersLock;
00117 }        
00118 
00119 xptiInterfaceInfoManager::xptiInterfaceInfoManager(nsISupportsArray* aSearchPath)
00120     :   mWorkingSet(aSearchPath),
00121         mOpenLogFile(nsnull),
00122         mResolveLock(PR_NewLock()),
00123         mAutoRegLock(PR_NewLock()),
00124         mInfoMonitor(nsAutoMonitor::NewMonitor("xptiInfoMonitor")),
00125         mAdditionalManagersLock(PR_NewLock()),
00126         mSearchPath(aSearchPath)
00127 {
00128     const char* statsFilename = PR_GetEnv("MOZILLA_XPTI_STATS");
00129     if(statsFilename)
00130     {
00131         mStatsLogFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);         
00132         if(mStatsLogFile && 
00133            NS_SUCCEEDED(mStatsLogFile->InitWithNativePath(nsDependentCString(statsFilename))))
00134         {
00135             printf("* Logging xptinfo stats to: %s\n", statsFilename);
00136         }
00137         else
00138         {
00139             printf("* Failed to create xptinfo stats file: %s\n", statsFilename);
00140             mStatsLogFile = nsnull;
00141         }
00142     }
00143 
00144     const char* autoRegFilename = PR_GetEnv("MOZILLA_XPTI_REGLOG");
00145     if(autoRegFilename)
00146     {
00147         mAutoRegLogFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);         
00148         if(mAutoRegLogFile && 
00149            NS_SUCCEEDED(mAutoRegLogFile->InitWithNativePath(nsDependentCString(autoRegFilename))))
00150         {
00151             printf("* Logging xptinfo autoreg to: %s\n", autoRegFilename);
00152         }
00153         else
00154         {
00155             printf("* Failed to create xptinfo autoreg file: %s\n", autoRegFilename);
00156             mAutoRegLogFile = nsnull;
00157         }
00158     }
00159 }
00160 
00161 xptiInterfaceInfoManager::~xptiInterfaceInfoManager()
00162 {
00163     // We only do this on shutdown of the service.
00164     mWorkingSet.InvalidateInterfaceInfos();
00165 
00166     if(mResolveLock)
00167         PR_DestroyLock(mResolveLock);
00168     if(mAutoRegLock)
00169         PR_DestroyLock(mAutoRegLock);
00170     if(mInfoMonitor)
00171         nsAutoMonitor::DestroyMonitor(mInfoMonitor);
00172     if(mAdditionalManagersLock)
00173         PR_DestroyLock(mAdditionalManagersLock);
00174 
00175     gInterfaceInfoManager = nsnull;
00176 #ifdef DEBUG
00177     xptiInterfaceInfo::DEBUG_ShutdownNotification();
00178     gCallCount = 0;
00179 #endif
00180 }
00181 
00182 static nsresult
00183 GetDirectoryFromDirService(const char* codename, nsILocalFile** aDir)
00184 {
00185     NS_ASSERTION(codename,"loser!");
00186     NS_ASSERTION(aDir,"loser!");
00187     
00188     nsresult rv;
00189     nsCOMPtr<nsIProperties> dirService =
00190         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
00191     if (NS_FAILED(rv)) return rv;
00192 
00193     return dirService->Get(codename, NS_GET_IID(nsILocalFile), (void**) aDir);
00194 }
00195 
00196 static PRBool
00197 AppendFromDirServiceList(const char* codename, nsISupportsArray* aPath)
00198 {
00199     NS_ASSERTION(codename,"loser!");
00200     NS_ASSERTION(aPath,"loser!");
00201     
00202     nsCOMPtr<nsIProperties> dirService = 
00203         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
00204     if(!dirService)
00205         return PR_FALSE;
00206 
00207     nsCOMPtr<nsISimpleEnumerator> fileList;
00208     dirService->Get(codename, NS_GET_IID(nsISimpleEnumerator), 
00209                     getter_AddRefs(fileList));
00210     if(!fileList)
00211         return PR_FALSE;
00212     
00213     PRBool more;
00214     while(NS_SUCCEEDED(fileList->HasMoreElements(&more)) && more)
00215     {
00216         nsCOMPtr<nsILocalFile> dir;
00217         fileList->GetNext(getter_AddRefs(dir));
00218         if(!dir || !aPath->AppendElement(dir))
00219             return PR_FALSE;
00220     }
00221 
00222     return PR_TRUE;
00223 }        
00224 
00225 // static 
00226 PRBool xptiInterfaceInfoManager::BuildFileSearchPath(nsISupportsArray** aPath)
00227 {
00228 #ifdef DEBUG
00229     NS_ASSERTION(!gCallCount++, "Expected only one call!");
00230 #endif
00231 
00232     nsCOMPtr<nsISupportsArray> searchPath;
00233     NS_NewISupportsArray(getter_AddRefs(searchPath));
00234     if(!searchPath)
00235         return PR_FALSE;
00236     
00237     nsCOMPtr<nsILocalFile> compDir;
00238 
00239     // Always put components directory first
00240 
00241     if(NS_FAILED(GetDirectoryFromDirService(NS_XPCOM_COMPONENT_DIR, 
00242                                             getter_AddRefs(compDir))) ||
00243        !searchPath->AppendElement(compDir))
00244     {
00245         return PR_FALSE;
00246     }
00247 
00248     // Add additional plugins dirs
00249     // No error checking here since this is optional in some embeddings
00250     
00251     // Add the GRE's component directory to searchPath if the 
00252     // application is using an GRE.
00253     // An application indicates that it's using an GRE by returning
00254     // a valid nsIFile via it's directory service provider interface.
00255     //
00256     // Please see http://www.mozilla.org/projects/embedding/MRE.html
00257     // for more info. on GREs
00258     //
00259     nsCOMPtr<nsILocalFile> greComponentDirectory;
00260     nsresult rv = GetDirectoryFromDirService(NS_GRE_COMPONENT_DIR, 
00261                                     getter_AddRefs(greComponentDirectory));
00262     if(NS_SUCCEEDED(rv) && greComponentDirectory)
00263     {
00264         // make sure we only append a directory if its a different one
00265         PRBool equalsCompDir = PR_FALSE;
00266         greComponentDirectory->Equals(compDir, &equalsCompDir);
00267 
00268         if(!equalsCompDir)
00269             searchPath->AppendElement(greComponentDirectory);
00270     }
00271 
00272     (void)AppendFromDirServiceList(NS_XPCOM_COMPONENT_DIR_LIST, searchPath);
00273     (void)AppendFromDirServiceList(NS_APP_PLUGINS_DIR_LIST, searchPath);
00274 
00275     NS_ADDREF(*aPath = searchPath);
00276     return PR_TRUE;
00277 }
00278 
00279 PRBool 
00280 xptiInterfaceInfoManager::GetCloneOfManifestLocation(nsILocalFile** aFile)
00281 {
00282     // We *trust* that this will not change!
00283     nsCOMPtr<nsILocalFile> lf;
00284     nsresult rv = GetDirectoryFromDirService(NS_XPCOM_XPTI_REGISTRY_FILE, 
00285                                              getter_AddRefs(lf));
00286 
00287     if (NS_FAILED(rv)) return PR_FALSE;
00288 
00289     rv = xptiCloneLocalFile(lf, aFile);
00290     if (NS_FAILED(rv)) return PR_FALSE;
00291     return PR_TRUE;
00292 }
00293 
00294 PRBool 
00295 xptiInterfaceInfoManager::GetApplicationDir(nsILocalFile** aDir)
00296 {
00297     // We *trust* that this will not change!
00298     return NS_SUCCEEDED(GetDirectoryFromDirService(NS_XPCOM_CURRENT_PROCESS_DIR, aDir));
00299 }
00300 
00301 PRBool 
00302 xptiInterfaceInfoManager::BuildFileList(nsISupportsArray* aSearchPath,
00303                                         nsISupportsArray** aFileList)
00304 {
00305     NS_ASSERTION(aFileList, "loser!");
00306     
00307     nsresult rv;
00308 
00309     nsCOMPtr<nsISupportsArray> fileList = 
00310         do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
00311     if(!fileList)
00312         return PR_FALSE;
00313 
00314     PRUint32 pathCount;
00315     if(NS_FAILED(aSearchPath->Count(&pathCount)))
00316         return PR_FALSE;
00317 
00318     for(PRUint32 i = 0; i < pathCount; i++)
00319     {
00320         nsCOMPtr<nsILocalFile> dir;
00321         rv = xptiCloneElementAsLocalFile(aSearchPath, i, getter_AddRefs(dir));
00322         if(NS_FAILED(rv) || !dir)
00323             return PR_FALSE;
00324 
00325         nsCOMPtr<nsISimpleEnumerator> entries;
00326         rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
00327         if(NS_FAILED(rv) || !entries)
00328             continue;
00329 
00330         PRUint32 count = 0;
00331         PRBool hasMore;
00332         while(NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore)
00333         {
00334             nsCOMPtr<nsISupports> sup;
00335             entries->GetNext(getter_AddRefs(sup));
00336             if(!sup)
00337                 return PR_FALSE;
00338             nsCOMPtr<nsILocalFile> file = do_QueryInterface(sup);
00339             if(!file)
00340                 return PR_FALSE;
00341 
00342             PRBool isFile;
00343             if(NS_FAILED(file->IsFile(&isFile)) || !isFile)
00344             {
00345                 continue;
00346             }
00347      
00348             nsCAutoString name;
00349             if(NS_FAILED(file->GetNativeLeafName(name)))
00350                 return PR_FALSE;
00351 
00352             if(xptiFileType::IsUnknown(name.get()))
00353                 continue;
00354 
00355             LOG_AUTOREG(("found file: %s\n", name.get()));
00356 
00357             if(!fileList->InsertElementAt(file, count))
00358                 return PR_FALSE;
00359             ++count;
00360         }
00361     }
00362 
00363     NS_ADDREF(*aFileList = fileList); 
00364     return PR_TRUE;
00365 }
00366 
00367 XPTHeader* 
00368 xptiInterfaceInfoManager::ReadXPTFile(nsILocalFile* aFile, 
00369                                       xptiWorkingSet* aWorkingSet)
00370 {
00371     NS_ASSERTION(aFile, "loser!");
00372 
00373     XPTHeader *header = nsnull;
00374     char *whole = nsnull;
00375     PRFileDesc*   fd = nsnull;
00376     XPTState *state = nsnull;
00377     XPTCursor cursor;
00378     PRInt32 flen;
00379     PRInt64 fileSize;
00380     
00381     PRBool saveFollowLinks;
00382     aFile->GetFollowLinks(&saveFollowLinks);
00383     aFile->SetFollowLinks(PR_TRUE);
00384 
00385     if(NS_FAILED(aFile->GetFileSize(&fileSize)) || !(flen = nsInt64(fileSize)))
00386     {
00387         aFile->SetFollowLinks(saveFollowLinks);
00388         return nsnull;
00389     }
00390 
00391     whole = new char[flen];
00392     if (!whole)
00393     {
00394         aFile->SetFollowLinks(saveFollowLinks);
00395         return nsnull;
00396     }
00397 
00398     // all exits from on here should be via 'goto out' 
00399 
00400     if(NS_FAILED(aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd)) || !fd)
00401     {
00402         goto out;
00403     }
00404 
00405     if(flen > PR_Read(fd, whole, flen))
00406     {
00407         goto out;
00408     }
00409 
00410     if(!(state = XPT_NewXDRState(XPT_DECODE, whole, flen)))
00411     {
00412         goto out;
00413     }
00414     
00415     if(!XPT_MakeCursor(state, XPT_HEADER, 0, &cursor))
00416     {
00417         goto out;
00418     }
00419     
00420     if (!XPT_DoHeader(aWorkingSet->GetStructArena(), &cursor, &header))
00421     {
00422         header = nsnull;
00423         goto out;
00424     }
00425 
00426  out:
00427     if(fd)
00428         PR_Close(fd);
00429     if(state)
00430         XPT_DestroyXDRState(state);
00431     if(whole)
00432         delete [] whole;
00433     aFile->SetFollowLinks(saveFollowLinks);
00434     return header;
00435 }
00436 
00437 PRBool 
00438 xptiInterfaceInfoManager::LoadFile(const xptiTypelib& aTypelibRecord,
00439                                  xptiWorkingSet* aWorkingSet)
00440 {
00441     if(!aWorkingSet)
00442         aWorkingSet = &mWorkingSet;
00443 
00444     if(!aWorkingSet->IsValid())
00445         return PR_FALSE;
00446 
00447     xptiFile* fileRecord = &aWorkingSet->GetFileAt(aTypelibRecord.GetFileIndex());
00448     xptiZipItem* zipItem = nsnull;
00449 
00450     nsCOMPtr<nsILocalFile> file;
00451     if(NS_FAILED(aWorkingSet->GetCloneOfDirectoryAt(fileRecord->GetDirectory(),
00452                                     getter_AddRefs(file))) || !file)
00453         return PR_FALSE;
00454 
00455     if(NS_FAILED(file->AppendNative(nsDependentCString(fileRecord->GetName()))))
00456         return PR_FALSE;
00457 
00458     XPTHeader* header;
00459 
00460     if(aTypelibRecord.IsZip())
00461     {
00462         zipItem = &aWorkingSet->GetZipItemAt(aTypelibRecord.GetZipItemIndex());
00463         
00464         // See the big comment below in the 'non-zip' case...
00465         if(zipItem->GetGuts())
00466         {
00467             NS_ERROR("Trying to load an xpt file from a zip twice");    
00468             
00469             // Force an autoreg on next run
00470             (void) xptiManifest::Delete(this);
00471 
00472             return PR_FALSE;
00473         }
00474         
00475         LOG_LOAD(("# loading zip item %s::%s\n", fileRecord->GetName(), zipItem->GetName()));
00476         
00477         nsCOMPtr<nsIXPTLoader> loader =
00478             do_GetService(NS_ZIPLOADER_CONTRACTID);
00479 
00480         if (loader) {
00481             nsresult rv;
00482             
00483             nsCOMPtr<nsIInputStream> stream;
00484             rv = loader->LoadEntry(file, zipItem->GetName(),
00485                                    getter_AddRefs(stream));
00486 
00487             if (NS_FAILED(rv))
00488                 return PR_FALSE;
00489             
00490             header =
00491                 xptiZipLoader::ReadXPTFileFromInputStream(stream, aWorkingSet);
00492         } else {
00493             header = nsnull;
00494             NS_WARNING("Could not load XPT Zip loader");
00495         }
00496     } 
00497     else
00498     {
00499         // The file would only have guts already if we previously failed to 
00500         // find an interface info in a file where the manifest claimed it was 
00501         // going to be. 
00502         //
00503         // Normally, when the file gets loaded (and the guts set) then all 
00504         // interfaces would also be resolved. So, if we are here again for
00505         // the same file then there must have been some interface that was 
00506         // expected but not present. Now we are explicitly trying to find it
00507         // and it isn't going to be there this time either.
00508         //
00509         // This is an assertion style error in a DEBUG build because it shows 
00510         // that we failed to detect this in autoreg. For release builds (where
00511         // autoreg is not run on every startup) it is just bad. But by returning
00512         // PR_FALSE we mark this interface as RESOLVE_FAILED and get on with 
00513         // things without crashing or anything.
00514         //
00515         // We don't want to do an autoreg here because this is too much of an 
00516         // edge case (and in that odd case it might autoreg multiple times if
00517         // many interfaces had been removed). But, by deleting the manifest we 
00518         // force the system to get it right on the next run.
00519         
00520         if(fileRecord->GetGuts())
00521         {
00522             NS_ERROR("Trying to load an xpt file twice");    
00523             
00524             // Force an autoreg on next run
00525             (void) xptiManifest::Delete(this);
00526 
00527             return PR_FALSE;
00528         }
00529 
00530         LOG_LOAD(("^ loading file %s\n", fileRecord->GetName()));
00531         header = ReadXPTFile(file, aWorkingSet);
00532     } 
00533 
00534     if(!header)
00535         return PR_FALSE;
00536 
00537 
00538     if(aTypelibRecord.IsZip())
00539     {
00540         // This also allocs zipItem.GetGuts() used below.
00541         if(!zipItem->SetHeader(header, aWorkingSet))
00542             return PR_FALSE;
00543     }
00544     else
00545     {
00546         // This also allocs fileRecord.GetGuts() used below.
00547         if(!fileRecord->SetHeader(header, aWorkingSet))
00548             return PR_FALSE;
00549     }
00550 
00551     // For each interface in the header we want to find the xptiInterfaceInfo
00552     // object and set its resolution info.
00553 
00554     for(PRUint16 i = 0; i < header->num_interfaces; i++)
00555     {
00556         static const nsID zeroIID =
00557             { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
00558 
00559         XPTInterfaceDirectoryEntry* iface = header->interface_directory + i;
00560         
00561         xptiHashEntry* hashEntry;
00562 
00563         if(!iface->iid.Equals(zeroIID))
00564         {
00565             hashEntry = (xptiHashEntry*)
00566                 PL_DHashTableOperate(aWorkingSet->mIIDTable, 
00567                                      &iface->iid, PL_DHASH_LOOKUP);
00568         }
00569         else
00570         {
00571             hashEntry = (xptiHashEntry*)
00572                 PL_DHashTableOperate(aWorkingSet->mNameTable, 
00573                                      iface->name, PL_DHASH_LOOKUP);
00574         }
00575 
00576         xptiInterfaceEntry* entry = 
00577             PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
00578 
00579         if(!entry)
00580         {
00581             // This one is just not resolved anywhere!
00582             continue;    
00583         }
00584 
00585         if(aTypelibRecord.IsZip())
00586             zipItem->GetGuts()->SetEntryAt(i, entry);
00587         else
00588             fileRecord->GetGuts()->SetEntryAt(i, entry);
00589 
00590         XPTInterfaceDescriptor* descriptor = iface->interface_descriptor;
00591 
00592         if(descriptor && aTypelibRecord.Equals(entry->GetTypelibRecord()))
00593             entry->PartiallyResolveLocked(descriptor, aWorkingSet);
00594     }
00595     return PR_TRUE;
00596 }
00597 
00598 static int
00599 IndexOfFileWithName(const char* aName, const xptiWorkingSet* aWorkingSet)
00600 {
00601     NS_ASSERTION(aName, "loser!");
00602 
00603     for(PRUint32 i = 0; i < aWorkingSet->GetFileCount(); ++i)
00604     {
00605         if(0 == PL_strcmp(aName, aWorkingSet->GetFileAt(i).GetName()))
00606             return i;     
00607     }
00608     return -1;        
00609 }        
00610 
00611 static int
00612 IndexOfDirectoryOfFile(nsISupportsArray* aSearchPath, nsILocalFile* aFile)
00613 {
00614     nsCOMPtr<nsIFile> parent;
00615     aFile->GetParent(getter_AddRefs(parent));
00616     if(parent)
00617     {
00618         PRUint32 count = 0;
00619         aSearchPath->Count(&count);
00620         NS_ASSERTION(count, "broken search path! bad count");
00621         for(PRUint32 i = 0; i < count; i++)
00622         {
00623             nsCOMPtr<nsIFile> current;
00624             aSearchPath->QueryElementAt(i, NS_GET_IID(nsIFile), 
00625                                         getter_AddRefs(current));
00626             NS_ASSERTION(current, "broken search path! bad element");
00627             PRBool same;
00628             if(NS_SUCCEEDED(parent->Equals(current, &same)) && same)
00629                 return (int) i;
00630         }
00631     }
00632     NS_ERROR("file not in search directory!");
00633     return -1;
00634 }        
00635 
00636 struct SortData
00637 {
00638     nsISupportsArray* mSearchPath;
00639     xptiWorkingSet*   mWorkingSet;
00640 };
00641 
00642 PR_STATIC_CALLBACK(int)
00643 xptiSortFileList(const void * p1, const void *p2, void * closure)
00644 {
00645     nsILocalFile* pFile1 = *((nsILocalFile**) p1);
00646     nsILocalFile* pFile2 = *((nsILocalFile**) p2);
00647     SortData* data = (SortData*) closure;
00648     
00649     nsCAutoString name1;
00650     nsCAutoString name2;
00651         
00652     if(NS_FAILED(pFile1->GetNativeLeafName(name1)))
00653     {
00654         NS_ERROR("way bad, with no happy out!");
00655         return 0;    
00656     }    
00657     if(NS_FAILED(pFile2->GetNativeLeafName(name2)))
00658     {
00659         NS_ERROR("way bad, with no happy out!");
00660         return 0;    
00661     }    
00662 
00663     int index1 = IndexOfFileWithName(name1.get(), data->mWorkingSet); 
00664     int index2 = IndexOfFileWithName(name2.get(), data->mWorkingSet); 
00665    
00666     // Get these now in case we need them later.
00667     PRBool isXPT1 = xptiFileType::IsXPT(name1.get());
00668     PRBool isXPT2 = xptiFileType::IsXPT(name2.get());
00669     int nameOrder = Compare(name1, name2);
00670     
00671     // both in workingSet, preserve old order
00672     if(index1 != -1 && index2 != -1)
00673         return index1 - index2;
00674 
00675     if(index1 != -1)
00676         return 1;
00677 
00678     if(index2 != -1)
00679         return -1;
00680 
00681     // neither is in workingset
00682 
00683     // check how they compare in search path order
00684     
00685     int dirIndex1 = IndexOfDirectoryOfFile(data->mSearchPath, pFile1);
00686     int dirIndex2 = IndexOfDirectoryOfFile(data->mSearchPath, pFile2);
00687 
00688     if(dirIndex1 != dirIndex2)
00689         return dirIndex1 - dirIndex2;
00690 
00691     // .xpt files come before archives (.zip, .jar, etc)
00692     if(isXPT1 &&!isXPT2)
00693         return -1;
00694         
00695     if(!isXPT1 && isXPT2)
00696         return 1;
00697     
00698     // neither element is in the workingSet and both are same type and in 
00699     // the same directory, sort by size
00700 
00701     PRInt64 size1;
00702     PRInt64 size2;
00703 
00704     if(NS_FAILED(pFile1->GetFileSize(&size1)))
00705     {
00706         NS_ERROR("way bad, with no happy out!");
00707         return 0;    
00708     }    
00709     if(NS_FAILED(pFile2->GetFileSize(&size2)))
00710     {
00711         NS_ERROR("way bad, with no happy out!");
00712         return 0;    
00713     }    
00714 
00715     // by size with largest first, or by name if size is the same
00716     int sizeDiff = int(PRInt32(nsInt64(size2) - nsInt64(size1)));
00717     return sizeDiff != 0  ? sizeDiff : nameOrder;
00718 }        
00719 
00720 nsILocalFile** 
00721 xptiInterfaceInfoManager::BuildOrderedFileArray(nsISupportsArray* aSearchPath,
00722                                                 nsISupportsArray* aFileList,
00723                                                 xptiWorkingSet* aWorkingSet)
00724 {
00725     // We want to end up with a file list that starts with the files from
00726     // aWorkingSet (but only those that are in aFileList) in the order in 
00727     // which they appeared in aWorkingSet-> Following those files will be those
00728     // files in aFileList which are not in aWorkingSet-> These additional
00729     // files will be ordered by file size (larger first) but all .xpt files
00730     // will preceed all zipfile of those files not already in the working set.
00731     // To do this we will do a fancy sort on a copy of aFileList.
00732 
00733     nsILocalFile** orderedFileList = nsnull;
00734     PRUint32 countOfFilesInFileList;
00735     PRUint32 i;
00736 
00737     NS_ASSERTION(aFileList, "loser!");
00738     NS_ASSERTION(aWorkingSet, "loser!");
00739     NS_ASSERTION(aWorkingSet->IsValid(), "loser!");
00740 
00741     if(NS_FAILED(aFileList->Count(&countOfFilesInFileList)) || 
00742        0 == countOfFilesInFileList)
00743         return nsnull;
00744 
00745     orderedFileList = (nsILocalFile**) 
00746         XPT_MALLOC(aWorkingSet->GetStructArena(),
00747                    sizeof(nsILocalFile*) * countOfFilesInFileList);
00748     
00749     if(!orderedFileList)
00750         return nsnull;
00751 
00752     // fill our list for sorting
00753     for(i = 0; i < countOfFilesInFileList; ++i)
00754     {
00755         nsCOMPtr<nsILocalFile> file;
00756         aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
00757         NS_ASSERTION(file, "loser!");
00758 
00759         // Intentionally NOT addref'd cuz we know these are pinned in aFileList.
00760         orderedFileList[i] = file.get();
00761     }
00762 
00763     // sort the filelist
00764 
00765     SortData sortData = {aSearchPath, aWorkingSet};
00766     NS_QuickSort(orderedFileList, countOfFilesInFileList, sizeof(nsILocalFile*),
00767                  xptiSortFileList, &sortData);
00768      
00769     return orderedFileList;
00770 }
00771 
00772 xptiInterfaceInfoManager::AutoRegMode 
00773 xptiInterfaceInfoManager::DetermineAutoRegStrategy(nsISupportsArray* aSearchPath,
00774                                                    nsISupportsArray* aFileList,
00775                                                    xptiWorkingSet* aWorkingSet)
00776 {
00777     NS_ASSERTION(aFileList, "loser!");
00778     NS_ASSERTION(aWorkingSet, "loser!");
00779     NS_ASSERTION(aWorkingSet->IsValid(), "loser!");
00780 
00781     PRUint32 countOfFilesInWorkingSet = aWorkingSet->GetFileCount();
00782     PRUint32 countOfFilesInFileList;
00783     PRUint32 i;
00784     PRUint32 k;
00785 
00786     if(0 == countOfFilesInWorkingSet)
00787     {
00788         // Loading manifest might have failed. Better safe...     
00789         return FULL_VALIDATION_REQUIRED;
00790     }
00791 
00792     if(NS_FAILED(aFileList->Count(&countOfFilesInFileList)))
00793     {
00794         NS_ERROR("unexpected!");
00795         return FULL_VALIDATION_REQUIRED;
00796     }       
00797 
00798     if(countOfFilesInFileList == countOfFilesInWorkingSet)
00799     {
00800         // try to determine if *no* files are new or changed.
00801      
00802         PRBool same = PR_TRUE;
00803         for(i = 0; i < countOfFilesInFileList && same; ++i)
00804         {
00805             nsCOMPtr<nsILocalFile> file;
00806             aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
00807             NS_ASSERTION(file, "loser!");
00808 
00809             PRInt64 size;
00810             PRInt64 date;
00811             nsCAutoString name;
00812             PRUint32 directory;
00813 
00814             if(NS_FAILED(file->GetFileSize(&size)) ||
00815                NS_FAILED(file->GetLastModifiedTime(&date)) ||
00816                NS_FAILED(file->GetNativeLeafName(name)) ||
00817                !aWorkingSet->FindDirectoryOfFile(file, &directory))
00818             {
00819                 NS_ERROR("unexpected!");
00820                 return FULL_VALIDATION_REQUIRED;
00821             }    
00822 
00823             for(k = 0; k < countOfFilesInWorkingSet; ++k)
00824             {
00825                 xptiFile& target = aWorkingSet->GetFileAt(k);
00826                 
00827                 if(directory == target.GetDirectory() &&
00828                    name.Equals(target.GetName()))
00829                 {
00830                     if(nsInt64(size) != target.GetSize() ||
00831                        nsInt64(date) != target.GetDate())
00832                         same = PR_FALSE;
00833                     break;        
00834                 }
00835             }
00836             // failed to find our file in the workingset?
00837             if(k == countOfFilesInWorkingSet)
00838                 same = PR_FALSE;
00839         }
00840         if(same)
00841             return NO_FILES_CHANGED;
00842     }
00843     else if(countOfFilesInFileList > countOfFilesInWorkingSet)
00844     {
00845         // try to determine if the only changes are additional new files
00846         // XXX Wimping out and doing this as a separate walk through the lists.
00847 
00848         PRBool same = PR_TRUE;
00849 
00850         for(i = 0; i < countOfFilesInWorkingSet && same; ++i)
00851         {
00852             xptiFile& target = aWorkingSet->GetFileAt(i);
00853             
00854             for(k = 0; k < countOfFilesInFileList; ++k)
00855             {
00856                 nsCOMPtr<nsILocalFile> file;
00857                 aFileList->QueryElementAt(k, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
00858                 NS_ASSERTION(file, "loser!");
00859                 
00860                 nsCAutoString name;
00861                 PRInt64 size;
00862                 PRInt64 date;
00863                 if(NS_FAILED(file->GetFileSize(&size)) ||
00864                    NS_FAILED(file->GetLastModifiedTime(&date)) ||
00865                    NS_FAILED(file->GetNativeLeafName(name)))
00866                 {
00867                     NS_ERROR("unexpected!");
00868                     return FULL_VALIDATION_REQUIRED;
00869                 }    
00870             
00871                 PRBool sameName = name.Equals(target.GetName());
00872                 if(sameName)
00873                 {
00874                     if(nsInt64(size) != target.GetSize() ||
00875                        nsInt64(date) != target.GetDate())
00876                         same = PR_FALSE;
00877                     break;        
00878                 }
00879             }
00880             // failed to find our file in the file list?
00881             if(k == countOfFilesInFileList)
00882                 same = PR_FALSE;
00883         }
00884         if(same)
00885             return FILES_ADDED_ONLY;
00886     }
00887 
00888     return FULL_VALIDATION_REQUIRED; 
00889 }
00890 
00891 PRBool 
00892 xptiInterfaceInfoManager::AddOnlyNewFilesFromFileList(nsISupportsArray* aSearchPath,
00893                                                       nsISupportsArray* aFileList,
00894                                                       xptiWorkingSet* aWorkingSet)
00895 {
00896     nsILocalFile** orderedFileArray;
00897     PRUint32 countOfFilesInFileList;
00898     PRUint32 i;
00899 
00900     NS_ASSERTION(aFileList, "loser!");
00901     NS_ASSERTION(aWorkingSet, "loser!");
00902     NS_ASSERTION(aWorkingSet->IsValid(), "loser!");
00903 
00904     if(NS_FAILED(aFileList->Count(&countOfFilesInFileList)))
00905         return PR_FALSE;
00906     NS_ASSERTION(countOfFilesInFileList, "loser!");
00907     NS_ASSERTION(countOfFilesInFileList > aWorkingSet->GetFileCount(), "loser!");
00908 
00909     orderedFileArray = BuildOrderedFileArray(aSearchPath, aFileList, aWorkingSet);
00910 
00911     if(!orderedFileArray)
00912         return PR_FALSE;
00913 
00914     // Make enough space in aWorkingset for additions to xptiFile array.
00915 
00916     if(!aWorkingSet->ExtendFileArray(countOfFilesInFileList))   
00917         return PR_FALSE;
00918 
00919     // For each file that is not already in our working set, add any valid 
00920     // interfaces that don't conflict with previous interfaces added.
00921     for(i = 0; i < countOfFilesInFileList; i++)
00922     {
00923         nsILocalFile* file = orderedFileArray[i];
00924 
00925         nsCAutoString name;
00926         PRInt64 size;
00927         PRInt64 date;
00928         PRUint32 dir;
00929         if(NS_FAILED(file->GetFileSize(&size)) ||
00930            NS_FAILED(file->GetLastModifiedTime(&date)) ||
00931            NS_FAILED(file->GetNativeLeafName(name)) ||
00932            !aWorkingSet->FindDirectoryOfFile(file, &dir))
00933         {
00934             return PR_FALSE;
00935         }    
00936     
00937 
00938         if(xptiWorkingSet::NOT_FOUND != aWorkingSet->FindFile(dir, name.get()))
00939         {
00940             // This file was found in the working set, so skip it.       
00941             continue;
00942         }
00943 
00944         LOG_AUTOREG(("  finding interfaces in new file: %s\n", name.get()));
00945 
00946         xptiFile fileRecord;
00947         fileRecord = xptiFile(nsInt64(size), nsInt64(date), dir,
00948                               name.get(), aWorkingSet);
00949 
00950         if(xptiFileType::IsXPT(fileRecord.GetName()))
00951         {
00952             XPTHeader* header = ReadXPTFile(file, aWorkingSet);
00953             if(!header)
00954             {
00955                 // XXX do something!
00956                 NS_ERROR("");    
00957                 continue;
00958             }
00959 
00960     
00961             xptiTypelib typelibRecord;
00962             typelibRecord.Init(aWorkingSet->GetFileCount());
00963     
00964             PRBool AddedFile = PR_FALSE;
00965 
00966             if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION)
00967             {
00968                 NS_ASSERTION(!header->num_interfaces,"bad libxpt");
00969                 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));
00970             }
00971 
00972             for(PRUint16 k = 0; k < header->num_interfaces; k++)
00973             {
00974                 xptiInterfaceEntry* entry = nsnull;
00975     
00976                 if(!VerifyAndAddEntryIfNew(aWorkingSet,
00977                                            header->interface_directory + k,
00978                                            typelibRecord,
00979                                            &entry))
00980                     return PR_FALSE;    
00981     
00982                 if(!entry)
00983                     continue;
00984                 
00985                 // If this is the first interface we found for this file then
00986                 // setup the fileRecord for the header and infos.
00987                 if(!AddedFile)
00988                 {
00989                     if(!fileRecord.SetHeader(header, aWorkingSet))
00990                     {
00991                         // XXX that would be bad.
00992                         return PR_FALSE;    
00993                     }
00994                     AddedFile = PR_TRUE;
00995                 }
00996                 fileRecord.GetGuts()->SetEntryAt(k, entry);
00997             }
00998             
00999             // This will correspond to typelibRecord above.
01000             aWorkingSet->AppendFile(fileRecord);
01001         }
01002         else // its another kind of archive
01003         {
01004             nsCOMPtr<nsIXPTLoader> loader =
01005                 do_GetService(NS_ZIPLOADER_CONTRACTID);
01006             
01007             if (loader) {
01008                 nsresult rv;
01009                 
01010                 nsCOMPtr<nsIXPTLoaderSink> sink =
01011                     new xptiZipLoaderSink(this, aWorkingSet);
01012                 if (!sink)
01013                     return PR_FALSE;
01014                 
01015                 rv = loader->EnumerateEntries(file, sink);
01016                 if (NS_FAILED(rv))
01017                     return PR_FALSE;
01018                 // This will correspond to typelibRecord used in
01019                 // xptiInterfaceInfoManager::FoundEntry.
01020                 aWorkingSet->AppendFile(fileRecord);
01021             } else {
01022                 NS_WARNING("Could not load XPT Zip loader");
01023             }
01024         }
01025     }
01026 
01027     return PR_TRUE;
01028 }        
01029 
01030 PRBool 
01031 xptiInterfaceInfoManager::DoFullValidationMergeFromFileList(nsISupportsArray* aSearchPath,
01032                                                             nsISupportsArray* aFileList,
01033                                                             xptiWorkingSet* aWorkingSet)
01034 {
01035     nsILocalFile** orderedFileArray;
01036     PRUint32 countOfFilesInFileList;
01037     PRUint32 i;
01038 
01039     NS_ASSERTION(aFileList, "loser!");
01040 
01041     if(!aWorkingSet->IsValid())
01042         return PR_FALSE;
01043 
01044     if(NS_FAILED(aFileList->Count(&countOfFilesInFileList)))
01045         return PR_FALSE;
01046 
01047     if(!countOfFilesInFileList)
01048     {
01049         // maybe there are no xpt files to register.  
01050         // a minimal install would have this case.
01051         return PR_TRUE;
01052     }
01053 
01054     orderedFileArray = BuildOrderedFileArray(aSearchPath, aFileList, aWorkingSet);
01055 
01056     if(!orderedFileArray)
01057         return PR_FALSE;
01058 
01059     // DEBUG_DumpFileArray(orderedFileArray, countOfFilesInFileList);
01060 
01061     // Make space in aWorkingset for a new xptiFile array.
01062 
01063     if(!aWorkingSet->NewFileArray(countOfFilesInFileList))   
01064         return PR_FALSE;
01065 
01066     aWorkingSet->ClearZipItems();
01067     aWorkingSet->ClearHashTables();
01068 
01069     // For each file, add any valid interfaces that don't conflict with 
01070     // previous interfaces added.
01071     for(i = 0; i < countOfFilesInFileList; i++)
01072     {
01073         nsILocalFile* file = orderedFileArray[i];
01074 
01075         nsCAutoString name;
01076         PRInt64 size;
01077         PRInt64 date;
01078         PRUint32 dir;
01079         if(NS_FAILED(file->GetFileSize(&size)) ||
01080            NS_FAILED(file->GetLastModifiedTime(&date)) ||
01081            NS_FAILED(file->GetNativeLeafName(name)) ||
01082            !aWorkingSet->FindDirectoryOfFile(file, &dir))
01083         {
01084             return PR_FALSE;
01085         }    
01086 
01087         LOG_AUTOREG(("  finding interfaces in file: %s\n", name.get()));
01088     
01089         xptiFile fileRecord;
01090         fileRecord = xptiFile(nsInt64(size), nsInt64(date), dir,
01091                               name.get(), aWorkingSet);
01092 
01093 //        printf("* found %s\n", fileRecord.GetName());
01094 
01095 
01096         if(xptiFileType::IsXPT(fileRecord.GetName()))
01097         {
01098             XPTHeader* header = ReadXPTFile(file, aWorkingSet);
01099             if(!header)
01100             {
01101                 // XXX do something!
01102                 NS_ERROR("Unable to read an XPT file, turn logging on to see which file");    
01103                 LOG_AUTOREG(("      unable to read file\n"));
01104                 continue;
01105             }
01106     
01107             xptiTypelib typelibRecord;
01108             typelibRecord.Init(aWorkingSet->GetFileCount());
01109     
01110             PRBool AddedFile = PR_FALSE;
01111 
01112             if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION)
01113             {
01114                 NS_ASSERTION(!header->num_interfaces,"bad libxpt");
01115                 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));
01116             }
01117 
01118             for(PRUint16 k = 0; k < header->num_interfaces; k++)
01119             {
01120                 xptiInterfaceEntry* entry = nsnull;
01121     
01122                 if(!VerifyAndAddEntryIfNew(aWorkingSet,
01123                                            header->interface_directory + k,
01124                                            typelibRecord,
01125                                            &entry))
01126                     return PR_FALSE;    
01127     
01128                 if(!entry)
01129                     continue;
01130                 
01131                 // If this is the first interface we found for this file then
01132                 // setup the fileRecord for the header and infos.
01133                 if(!AddedFile)
01134                 {
01135                     if(!fileRecord.SetHeader(header, aWorkingSet))
01136                     {
01137                         // XXX that would be bad.
01138                         return PR_FALSE;    
01139                     }
01140                     AddedFile = PR_TRUE;
01141                 }
01142                 fileRecord.GetGuts()->SetEntryAt(k, entry);
01143             }
01144             
01145             // This will correspond to typelibRecord above.
01146             aWorkingSet->AppendFile(fileRecord);
01147         }
01148 
01149         else
01150         {
01151             nsCOMPtr<nsIXPTLoader> loader =
01152                 do_GetService(NS_ZIPLOADER_CONTRACTID);
01153             
01154             if (loader) {
01155                 nsresult rv;
01156                 
01157                 nsCOMPtr<nsIXPTLoaderSink> sink =
01158                     new xptiZipLoaderSink(this, aWorkingSet);
01159                 if (!sink)
01160                     return PR_FALSE;
01161                 
01162                 rv = loader->EnumerateEntries(file, sink);
01163                 if (NS_FAILED(rv))
01164                     return PR_FALSE;
01165                 // This will correspond to typelibRecord used in
01166                 // xptiInterfaceInfoManager::FoundEntry.
01167                 aWorkingSet->AppendFile(fileRecord);
01168             } else {
01169                 NS_WARNING("Could not load XPT Zip loader");
01170             }
01171         }
01172     }
01173     return PR_TRUE;
01174 }        
01175 
01176 NS_IMPL_ISUPPORTS1(xptiZipLoaderSink, nsIXPTLoaderSink)
01177 
01178 // implement nsIXPTLoader
01179 NS_IMETHODIMP
01180 xptiZipLoaderSink::FoundEntry(const char* entryName,
01181                               PRInt32 index,
01182                               nsIInputStream *aStream)
01183 {
01184     XPTHeader *header =
01185         xptiZipLoader::ReadXPTFileFromInputStream(aStream, mWorkingSet);
01186     if (!header)
01187         return NS_ERROR_OUT_OF_MEMORY;
01188     
01189     if (!mManager->FoundZipEntry(entryName, index, header, mWorkingSet))
01190         return NS_ERROR_FAILURE;
01191     
01192     return NS_OK;
01193 }
01194 
01195 // implement xptiEntrySink
01196 PRBool 
01197 xptiInterfaceInfoManager::FoundZipEntry(const char* entryName,
01198                                         int index,
01199                                         XPTHeader* header,
01200                                         xptiWorkingSet* aWorkingSet)
01201 {
01202 
01203     NS_ASSERTION(entryName, "loser!");
01204     NS_ASSERTION(header, "loser!");
01205     NS_ASSERTION(aWorkingSet, "loser!");
01206 
01207     int countOfInterfacesAddedForItem = 0;
01208     xptiZipItem zipItemRecord(entryName, aWorkingSet);
01209     
01210     LOG_AUTOREG(("    finding interfaces in file: %s\n", entryName));
01211 
01212     if(header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION)
01213     {
01214         NS_ASSERTION(!header->num_interfaces,"bad libxpt");
01215         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));
01216     }
01217 
01218     if(!header->num_interfaces)
01219     {
01220         // We are not interested in files without interfaces.
01221         return PR_TRUE;
01222     }
01223     
01224     xptiTypelib typelibRecord;
01225     typelibRecord.Init(aWorkingSet->GetFileCount(),
01226                        aWorkingSet->GetZipItemCount());
01227 
01228     for(PRUint16 k = 0; k < header->num_interfaces; k++)
01229     {
01230         xptiInterfaceEntry* entry = nsnull;
01231     
01232         if(!VerifyAndAddEntryIfNew(aWorkingSet,
01233                                    header->interface_directory + k,
01234                                    typelibRecord,
01235                                    &entry))
01236             return PR_FALSE;    
01237     
01238         if(!entry)
01239             continue;
01240 
01241         // If this is the first interface we found for this item
01242         // then setup the zipItemRecord for the header and infos.
01243         if(!countOfInterfacesAddedForItem)
01244         {
01245             // XXX fix this!
01246             if(!zipItemRecord.SetHeader(header, aWorkingSet))
01247             {
01248                 // XXX that would be bad.
01249                 return PR_FALSE;    
01250             }
01251         }
01252 
01253         // zipItemRecord.GetGuts()->SetEntryAt(k, entry);
01254         ++countOfInterfacesAddedForItem;
01255     }   
01256 
01257     if(countOfInterfacesAddedForItem)
01258     {
01259         if(!aWorkingSet->GetZipItemFreeSpace())
01260         {
01261             if(!aWorkingSet->ExtendZipItemArray(
01262                 aWorkingSet->GetZipItemCount() + 20))
01263             {        
01264                 // out of space!
01265                 return PR_FALSE;    
01266             }
01267         }
01268         aWorkingSet->AppendZipItem(zipItemRecord);
01269     } 
01270     return PR_TRUE;
01271 }
01272 
01273 PRBool 
01274 xptiInterfaceInfoManager::VerifyAndAddEntryIfNew(xptiWorkingSet* aWorkingSet,
01275                                                  XPTInterfaceDirectoryEntry* iface,
01276                                                  const xptiTypelib& typelibRecord,
01277                                                  xptiInterfaceEntry** entryAdded)
01278 {
01279     NS_ASSERTION(iface, "loser!");
01280     NS_ASSERTION(entryAdded, "loser!");
01281 
01282     *entryAdded = nsnull;
01283 
01284     if(!iface->interface_descriptor)
01285     {
01286         // Not resolved, ignore this one.
01287         // XXX full logging might note this...
01288         return PR_TRUE;
01289     }
01290     
01291     xptiHashEntry* hashEntry = (xptiHashEntry*)
01292         PL_DHashTableOperate(aWorkingSet->mIIDTable, &iface->iid, PL_DHASH_LOOKUP);
01293 
01294     xptiInterfaceEntry* entry = 
01295         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01296     
01297     if(entry)
01298     {
01299         // XXX validate this info to find possible inconsistencies
01300         LOG_AUTOREG(("      ignoring repeated interface: %s\n", iface->name));
01301         return PR_TRUE;
01302     }
01303     
01304     // Build a new xptiInterfaceEntry object and hook it up. 
01305 
01306     entry = xptiInterfaceEntry::NewEntry(iface->name, strlen(iface->name),
01307                                          iface->iid,
01308                                          typelibRecord, aWorkingSet);
01309     if(!entry)
01310     {
01311         // XXX bad!
01312         return PR_FALSE;    
01313     }
01314 
01315     //XXX  We should SetHeader too as part of the validation, no?
01316     entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags));
01317 
01318     // Add our entry to the iid hashtable.
01319 
01320     hashEntry = (xptiHashEntry*)
01321         PL_DHashTableOperate(aWorkingSet->mNameTable, 
01322                              entry->GetTheName(), PL_DHASH_ADD);
01323     if(hashEntry)
01324         hashEntry->value = entry;
01325     
01326     // Add our entry to the name hashtable.
01327 
01328     hashEntry = (xptiHashEntry*)
01329         PL_DHashTableOperate(aWorkingSet->mIIDTable, 
01330                              entry->GetTheIID(), PL_DHASH_ADD);
01331     if(hashEntry)
01332         hashEntry->value = entry;
01333     
01334     *entryAdded = entry;
01335 
01336     LOG_AUTOREG(("      added interface: %s\n", iface->name));
01337 
01338     return PR_TRUE;
01339 }
01340 
01341 // local struct used to pass two pointers as one pointer
01342 struct TwoWorkingSets
01343 {
01344     TwoWorkingSets(xptiWorkingSet* src, xptiWorkingSet* dest)
01345         : aSrcWorkingSet(src), aDestWorkingSet(dest) {}
01346 
01347     xptiWorkingSet* aSrcWorkingSet;
01348     xptiWorkingSet* aDestWorkingSet;
01349 };        
01350 
01351 PR_STATIC_CALLBACK(PLDHashOperator)
01352 xpti_Merger(PLDHashTable *table, PLDHashEntryHdr *hdr,
01353             PRUint32 number, void *arg)
01354 {
01355     xptiInterfaceEntry* srcEntry = ((xptiHashEntry*)hdr)->value;
01356     xptiWorkingSet* aSrcWorkingSet = ((TwoWorkingSets*)arg)->aSrcWorkingSet;
01357     xptiWorkingSet* aDestWorkingSet = ((TwoWorkingSets*)arg)->aDestWorkingSet;
01358 
01359     xptiHashEntry* hashEntry = (xptiHashEntry*)
01360         PL_DHashTableOperate(aDestWorkingSet->mIIDTable, 
01361                              srcEntry->GetTheIID(), PL_DHASH_LOOKUP);
01362 
01363     xptiInterfaceEntry* destEntry = 
01364         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01365     
01366     if(destEntry)
01367     {
01368         // Let's see if this is referring to the same exact typelib
01369          
01370         const char* destFilename = 
01371             aDestWorkingSet->GetTypelibFileName(destEntry->GetTypelibRecord());
01372         
01373         const char* srcFilename = 
01374             aSrcWorkingSet->GetTypelibFileName(srcEntry->GetTypelibRecord());
01375     
01376         if(0 == PL_strcmp(destFilename, srcFilename) && 
01377            (destEntry->GetTypelibRecord().GetZipItemIndex() ==
01378             srcEntry->GetTypelibRecord().GetZipItemIndex()))
01379         {
01380             // This is the same item.
01381             // But... Let's make sure they didn't change the interface name.
01382             // There are wacky developers that do stuff like that!
01383             if(0 == PL_strcmp(destEntry->GetTheName(), srcEntry->GetTheName()))
01384                 return PL_DHASH_NEXT;
01385         }
01386     }
01387     
01388     // Clone the xptiInterfaceEntry into our destination WorkingSet.
01389 
01390     xptiTypelib typelibRecord;
01391 
01392     uint16 fileIndex = srcEntry->GetTypelibRecord().GetFileIndex();
01393     uint16 zipItemIndex = srcEntry->GetTypelibRecord().GetZipItemIndex();
01394     
01395     fileIndex += aDestWorkingSet->mFileMergeOffsetMap[fileIndex];
01396     
01397     // If it is not a zipItem, then the original index is fine.
01398     if(srcEntry->GetTypelibRecord().IsZip())
01399         zipItemIndex += aDestWorkingSet->mZipItemMergeOffsetMap[zipItemIndex];
01400 
01401     typelibRecord.Init(fileIndex, zipItemIndex);
01402                 
01403     destEntry = xptiInterfaceEntry::NewEntry(*srcEntry, typelibRecord, 
01404                                              aDestWorkingSet);
01405     if(!destEntry)
01406     {
01407         // XXX bad! should log
01408         return PL_DHASH_NEXT;
01409     }
01410 
01411 
01412     // Add our entry to the iid hashtable.
01413 
01414     hashEntry = (xptiHashEntry*)
01415         PL_DHashTableOperate(aDestWorkingSet->mNameTable, 
01416                              destEntry->GetTheName(), PL_DHASH_ADD);
01417     if(hashEntry)
01418         hashEntry->value = destEntry;
01419     
01420     // Add our entry to the name hashtable.
01421 
01422     hashEntry = (xptiHashEntry*)
01423         PL_DHashTableOperate(aDestWorkingSet->mIIDTable, 
01424                              destEntry->GetTheIID(), PL_DHASH_ADD);
01425     if(hashEntry)
01426         hashEntry->value = destEntry;
01427 
01428     return PL_DHASH_NEXT;
01429 }       
01430 
01431 PRBool 
01432 xptiInterfaceInfoManager::MergeWorkingSets(xptiWorkingSet* aDestWorkingSet,
01433                                            xptiWorkingSet* aSrcWorkingSet)
01434 {
01435 
01436     PRUint32 i;
01437 
01438     // Combine file lists.
01439 
01440     PRUint32 originalFileCount = aDestWorkingSet->GetFileCount();
01441     PRUint32 additionalFileCount = aSrcWorkingSet->GetFileCount();
01442 
01443     // Create a new array big enough to hold both lists and copy existing files
01444 
01445     if(additionalFileCount)
01446     {
01447         if(!aDestWorkingSet->ExtendFileArray(originalFileCount +
01448                                              additionalFileCount))
01449             return PR_FALSE;
01450     
01451         // Now we are where we started, but we know we have enough space.
01452     
01453         // Prepare offset array for later fixups. 
01454         // NOTE: Storing with dest, but alloc'ing from src. This is intentional.
01455         aDestWorkingSet->mFileMergeOffsetMap = (PRUint32*)
01456             XPT_CALLOC(aSrcWorkingSet->GetStructArena(),
01457                        additionalFileCount * sizeof(PRUint32)); 
01458         if(!aDestWorkingSet->mFileMergeOffsetMap)
01459             return PR_FALSE;
01460     }
01461 
01462     for(i = 0; i < additionalFileCount; ++i)
01463     {
01464         xptiFile& srcFile = aSrcWorkingSet->GetFileAt(i);
01465         PRUint32 k;
01466         for(k = 0; k < originalFileCount; ++k)
01467         {
01468             // If file (with same name, date, and time) is in both lists
01469             // then reuse that record.
01470             xptiFile& destFile = aDestWorkingSet->GetFileAt(k);
01471             if(srcFile.Equals(destFile))
01472             {
01473                 aDestWorkingSet->mFileMergeOffsetMap[i] = k - i;
01474                 break;    
01475             }
01476         }
01477         if(k == originalFileCount)
01478         {
01479             // No match found, tack it on the end.
01480 
01481             PRUint32 newIndex = aDestWorkingSet->GetFileCount();
01482 
01483             aDestWorkingSet->AppendFile(xptiFile(srcFile, aDestWorkingSet));
01484 
01485             // Fixup the merge offset map.
01486             aDestWorkingSet->mFileMergeOffsetMap[i] = newIndex - i;
01487         }
01488     }
01489 
01490     // Combine ZipItem lists.
01491 
01492     PRUint32 originalZipItemCount = aDestWorkingSet->GetZipItemCount();
01493     PRUint32 additionalZipItemCount = aSrcWorkingSet->GetZipItemCount();
01494 
01495     // Create a new array big enough to hold both lists and copy existing ZipItems
01496 
01497     if(additionalZipItemCount)
01498     {
01499         if(!aDestWorkingSet->ExtendZipItemArray(originalZipItemCount +
01500                                                 additionalZipItemCount))
01501             return PR_FALSE;
01502     
01503         // Now we are where we started, but we know we have enough space.
01504     
01505         // Prepare offset array for later fixups. 
01506         // NOTE: Storing with dest, but alloc'ing from src. This is intentional.
01507         aDestWorkingSet->mZipItemMergeOffsetMap = (PRUint32*)
01508             XPT_CALLOC(aSrcWorkingSet->GetStructArena(),
01509                        additionalZipItemCount * sizeof(PRUint32)); 
01510         if(!aDestWorkingSet->mZipItemMergeOffsetMap)
01511             return PR_FALSE;
01512     }
01513 
01514     for(i = 0; i < additionalZipItemCount; ++i)
01515     {
01516         xptiZipItem& srcZipItem = aSrcWorkingSet->GetZipItemAt(i);
01517         PRUint32 k;
01518         for(k = 0; k < originalZipItemCount; ++k)
01519         {
01520             // If ZipItem (with same name) is in both lists
01521             // then reuse that record.
01522             xptiZipItem& destZipItem = aDestWorkingSet->GetZipItemAt(k);
01523             if(srcZipItem.Equals(destZipItem))
01524             {
01525                 aDestWorkingSet->mZipItemMergeOffsetMap[i] = k - i;
01526                 break;    
01527             }
01528         }
01529         if(k == originalZipItemCount)
01530         {
01531             // No match found, tack it on the end.
01532 
01533             PRUint32 newIndex = aDestWorkingSet->GetZipItemCount();
01534 
01535             aDestWorkingSet->AppendZipItem(
01536                     xptiZipItem(srcZipItem, aDestWorkingSet));
01537 
01538             // Fixup the merge offset map.
01539             aDestWorkingSet->mZipItemMergeOffsetMap[i] = newIndex - i;
01540         }
01541     }
01542 
01543     // Migrate xptiInterfaceInfos
01544 
01545     TwoWorkingSets sets(aSrcWorkingSet, aDestWorkingSet);
01546 
01547     PL_DHashTableEnumerate(aSrcWorkingSet->mNameTable, xpti_Merger, &sets);
01548 
01549     return PR_TRUE;
01550 }        
01551 
01552 PRBool 
01553 xptiInterfaceInfoManager::DEBUG_DumpFileList(nsISupportsArray* aFileList)
01554 {
01555     PRUint32 count;
01556 
01557     if(NS_FAILED(aFileList->Count(&count)))
01558         return PR_FALSE;
01559     
01560     for(PRUint32 i = 0; i < count; i++)
01561     {
01562         nsCOMPtr<nsIFile> file;
01563         aFileList->QueryElementAt(i, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
01564         if(!file)
01565             return PR_FALSE;
01566 
01567         nsCAutoString name;
01568         if(NS_FAILED(file->GetNativeLeafName(name)))
01569             return PR_FALSE;
01570 
01571         printf("* found %s\n", name.get());
01572     }
01573     return PR_TRUE;
01574 }
01575 
01576 PRBool 
01577 xptiInterfaceInfoManager::DEBUG_DumpFileListInWorkingSet(xptiWorkingSet* aWorkingSet)
01578 {
01579     for(PRUint16 i = 0; i < aWorkingSet->GetFileCount(); ++i)
01580     {
01581         xptiFile& record = aWorkingSet->GetFileAt(i);
01582     
01583         printf("! has %s\n", record.GetName());
01584     }        
01585     return PR_TRUE;
01586 }        
01587 
01588 PRBool 
01589 xptiInterfaceInfoManager::DEBUG_DumpFileArray(nsILocalFile** aFileArray, 
01590                                               PRUint32 count)
01591 {
01592     // dump the sorted list
01593     for(PRUint32 i = 0; i < count; ++i)
01594     {
01595         nsILocalFile* file = aFileArray[i];
01596     
01597         nsCAutoString name;
01598         if(NS_FAILED(file->GetNativeLeafName(name)))
01599             return PR_FALSE;
01600 
01601         printf("found file: %s\n", name.get());
01602     }        
01603     return PR_TRUE;        
01604 }        
01605 
01606 /***************************************************************************/
01607 
01608 // static 
01609 void 
01610 xptiInterfaceInfoManager::WriteToLog(const char *fmt, ...)
01611 {
01612     if(!gInterfaceInfoManager)
01613         return;
01614 
01615     PRFileDesc* fd = gInterfaceInfoManager->GetOpenLogFile();
01616     if(fd)
01617     {
01618         va_list ap;
01619         va_start(ap, fmt);
01620         PR_vfprintf(fd, fmt, ap);
01621         va_end(ap);
01622     }
01623 }        
01624 
01625 PR_STATIC_CALLBACK(PLDHashOperator)
01626 xpti_ResolvedFileNameLogger(PLDHashTable *table, PLDHashEntryHdr *hdr,
01627                             PRUint32 number, void *arg)
01628 {
01629     xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value;
01630     xptiInterfaceInfoManager* mgr = (xptiInterfaceInfoManager*) arg;
01631 
01632     if(entry->IsFullyResolved())
01633     {
01634         xptiWorkingSet*  aWorkingSet = mgr->GetWorkingSet();
01635         PRFileDesc* fd = mgr->GetOpenLogFile();
01636 
01637         const xptiTypelib& typelib = entry->GetTypelibRecord();
01638         const char* filename = 
01639                 aWorkingSet->GetFileAt(typelib.GetFileIndex()).GetName();
01640         
01641         if(typelib.IsZip())
01642         {
01643             const char* zipItemName = 
01644                 aWorkingSet->GetZipItemAt(typelib.GetZipItemIndex()).GetName();
01645             PR_fprintf(fd, "xpti used interface: %s from %s::%s\n", 
01646                        entry->GetTheName(), filename, zipItemName);
01647         }    
01648         else
01649         {
01650             PR_fprintf(fd, "xpti used interface: %s from %s\n", 
01651                        entry->GetTheName(), filename);
01652         }
01653     }
01654     return PL_DHASH_NEXT;
01655 }
01656 
01657 void   
01658 xptiInterfaceInfoManager::LogStats()
01659 {
01660     PRUint32 i;
01661     
01662     // This sets what will be returned by GetOpenLogFile().
01663     xptiAutoLog autoLog(this, mStatsLogFile, PR_FALSE);
01664 
01665     PRFileDesc* fd = GetOpenLogFile();
01666     if(!fd)
01667         return;
01668 
01669     // Show names of xpt (only) files from which at least one interface 
01670     // was resolved.
01671 
01672     PRUint32 fileCount = mWorkingSet.GetFileCount();
01673     for(i = 0; i < fileCount; ++i)
01674     {
01675         xptiFile& f = mWorkingSet.GetFileAt(i);
01676         if(f.GetGuts())
01677             PR_fprintf(fd, "xpti used file: %s\n", f.GetName());
01678     }
01679 
01680     PR_fprintf(fd, "\n");
01681 
01682     // Show names of xptfiles loaded from zips from which at least 
01683     // one interface was resolved.
01684 
01685     PRUint32 zipItemCount = mWorkingSet.GetZipItemCount();
01686     for(i = 0; i < zipItemCount; ++i)
01687     {
01688         xptiZipItem& zi = mWorkingSet.GetZipItemAt(i);
01689         if(zi.GetGuts())                           
01690             PR_fprintf(fd, "xpti used file from zip: %s\n", zi.GetName());
01691     }
01692 
01693     PR_fprintf(fd, "\n");
01694 
01695     // Show name of each interface that was fully resolved and the name
01696     // of the file and (perhaps) zip from which it was loaded.
01697 
01698     PL_DHashTableEnumerate(mWorkingSet.mNameTable, 
01699                            xpti_ResolvedFileNameLogger, this);
01700 
01701 } 
01702 
01703 /***************************************************************************/
01704 
01705 // this is a private helper
01706 static nsresult 
01707 EntryToInfo(xptiInterfaceEntry* entry, nsIInterfaceInfo **_retval)
01708 {
01709     xptiInterfaceInfo* info;
01710     nsresult rv;
01711 
01712     if(!entry)
01713     {
01714         *_retval = nsnull;
01715         return NS_ERROR_FAILURE;    
01716     }
01717 
01718     rv = entry->GetInterfaceInfo(&info);
01719     if(NS_FAILED(rv))
01720         return rv;
01721 
01722     // Transfer the AddRef done by GetInterfaceInfo.
01723     *_retval = NS_STATIC_CAST(nsIInterfaceInfo*, info);
01724     return NS_OK;    
01725 }
01726 
01727 /* nsIInterfaceInfo getInfoForIID (in nsIIDPtr iid); */
01728 NS_IMETHODIMP xptiInterfaceInfoManager::GetInfoForIID(const nsIID * iid, nsIInterfaceInfo **_retval)
01729 {
01730     NS_ASSERTION(iid, "bad param");
01731     NS_ASSERTION(_retval, "bad param");
01732 
01733     xptiHashEntry* hashEntry = (xptiHashEntry*)
01734         PL_DHashTableOperate(mWorkingSet.mIIDTable, iid, PL_DHASH_LOOKUP);
01735 
01736     xptiInterfaceEntry* entry = 
01737         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01738 
01739     return EntryToInfo(entry, _retval);
01740 }
01741 
01742 /* nsIInterfaceInfo getInfoForName (in string name); */
01743 NS_IMETHODIMP xptiInterfaceInfoManager::GetInfoForName(const char *name, nsIInterfaceInfo **_retval)
01744 {
01745     NS_ASSERTION(name, "bad param");
01746     NS_ASSERTION(_retval, "bad param");
01747 
01748     xptiHashEntry* hashEntry = (xptiHashEntry*)
01749         PL_DHashTableOperate(mWorkingSet.mNameTable, name, PL_DHASH_LOOKUP);
01750 
01751     xptiInterfaceEntry* entry = 
01752         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01753 
01754     return EntryToInfo(entry, _retval);
01755 }
01756 
01757 /* nsIIDPtr getIIDForName (in string name); */
01758 NS_IMETHODIMP xptiInterfaceInfoManager::GetIIDForName(const char *name, nsIID * *_retval)
01759 {
01760     NS_ASSERTION(name, "bad param");
01761     NS_ASSERTION(_retval, "bad param");
01762 
01763     xptiHashEntry* hashEntry = (xptiHashEntry*)
01764         PL_DHashTableOperate(mWorkingSet.mNameTable, name, PL_DHASH_LOOKUP);
01765 
01766     xptiInterfaceEntry* entry = 
01767         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01768 
01769     if(!entry)
01770     {
01771         *_retval = nsnull;
01772         return NS_ERROR_FAILURE;    
01773     }
01774 
01775     return entry->GetIID(_retval);
01776 }
01777 
01778 /* string getNameForIID (in nsIIDPtr iid); */
01779 NS_IMETHODIMP xptiInterfaceInfoManager::GetNameForIID(const nsIID * iid, char **_retval)
01780 {
01781     NS_ASSERTION(iid, "bad param");
01782     NS_ASSERTION(_retval, "bad param");
01783 
01784     xptiHashEntry* hashEntry = (xptiHashEntry*)
01785         PL_DHashTableOperate(mWorkingSet.mIIDTable, iid, PL_DHASH_LOOKUP);
01786 
01787     xptiInterfaceEntry* entry = 
01788         PL_DHASH_ENTRY_IS_FREE(hashEntry) ? nsnull : hashEntry->value;
01789 
01790     if(!entry)
01791     {
01792         *_retval = nsnull;
01793         return NS_ERROR_FAILURE;    
01794     }
01795 
01796     return entry->GetName(_retval);
01797 }
01798 
01799 PR_STATIC_CALLBACK(PLDHashOperator)
01800 xpti_ArrayAppender(PLDHashTable *table, PLDHashEntryHdr *hdr,
01801                    PRUint32 number, void *arg)
01802 {
01803     xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value;
01804     nsISupportsArray* array = (nsISupportsArray*) arg;
01805 
01806     nsCOMPtr<nsIInterfaceInfo> ii;
01807     if(NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii))))
01808         array->AppendElement(ii);
01809     return PL_DHASH_NEXT;
01810 }
01811 
01812 /* nsIEnumerator enumerateInterfaces (); */
01813 NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateInterfaces(nsIEnumerator **_retval)
01814 {
01815     // I didn't want to incur the size overhead of using nsHashtable just to
01816     // make building an enumerator easier. So, this code makes a snapshot of 
01817     // the table using an nsISupportsArray and builds an enumerator for that.
01818     // We can afford this transient cost.
01819 
01820     nsCOMPtr<nsISupportsArray> array;
01821     NS_NewISupportsArray(getter_AddRefs(array));
01822     if(!array)
01823         return NS_ERROR_UNEXPECTED;
01824 
01825     PL_DHashTableEnumerate(mWorkingSet.mNameTable, xpti_ArrayAppender, array);
01826     
01827     return array->Enumerate(_retval);
01828 }
01829 
01830 struct ArrayAndPrefix
01831 {
01832     nsISupportsArray* array;
01833     const char*       prefix;
01834     PRUint32          length;
01835 };
01836 
01837 PR_STATIC_CALLBACK(PLDHashOperator)
01838 xpti_ArrayPrefixAppender(PLDHashTable *table, PLDHashEntryHdr *hdr,
01839                          PRUint32 number, void *arg)
01840 {
01841     xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value;
01842     ArrayAndPrefix* args = (ArrayAndPrefix*) arg;
01843 
01844     const char* name = entry->GetTheName();
01845     if(name != PL_strnstr(name, args->prefix, args->length))
01846         return PL_DHASH_NEXT;
01847 
01848     nsCOMPtr<nsIInterfaceInfo> ii;
01849     if(NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii))))
01850         args->array->AppendElement(ii);
01851     return PL_DHASH_NEXT;
01852 }
01853 
01854 /* nsIEnumerator enumerateInterfacesWhoseNamesStartWith (in string prefix); */
01855 NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateInterfacesWhoseNamesStartWith(const char *prefix, nsIEnumerator **_retval)
01856 {
01857     nsCOMPtr<nsISupportsArray> array;
01858     NS_NewISupportsArray(getter_AddRefs(array));
01859     if(!array)
01860         return NS_ERROR_UNEXPECTED;
01861 
01862     ArrayAndPrefix args = {array, prefix, PL_strlen(prefix)};
01863     PL_DHashTableEnumerate(mWorkingSet.mNameTable, xpti_ArrayPrefixAppender, &args);
01864     
01865     return array->Enumerate(_retval);
01866 }
01867 
01868 /* void autoRegisterInterfaces (); */
01869 NS_IMETHODIMP xptiInterfaceInfoManager::AutoRegisterInterfaces()
01870 {
01871     nsCOMPtr<nsISupportsArray> fileList;
01872     AutoRegMode mode;
01873     PRBool ok;
01874 
01875     nsAutoLock lock(xptiInterfaceInfoManager::GetAutoRegLock(this));
01876 
01877     xptiWorkingSet workingSet(mSearchPath);
01878     if(!workingSet.IsValid())
01879         return NS_ERROR_UNEXPECTED;
01880 
01881     // This sets what will be returned by GetOpenLogFile().
01882     xptiAutoLog autoLog(this, mAutoRegLogFile, PR_TRUE);
01883 
01884     LOG_AUTOREG(("start AutoRegister\n"));
01885 
01886     // We re-read the manifest rather than muck with the 'live' one.
01887     // It is OK if this fails.
01888     // XXX But we should track failure as a warning.
01889     ok = xptiManifest::Read(this, &workingSet);
01890 
01891     LOG_AUTOREG(("read of manifest %s\n", ok ? "successful" : "FAILED"));
01892 
01893     // Grovel for all the typelibs we can find (in .xpt or .zip, .jar,...).
01894     if(!BuildFileList(mSearchPath, getter_AddRefs(fileList)) || !fileList)
01895         return NS_ERROR_UNEXPECTED;
01896     
01897     // DEBUG_DumpFileList(fileList);
01898 
01899     // Check to see how much work we need to do.
01900     mode = DetermineAutoRegStrategy(mSearchPath, fileList, &workingSet);
01901 
01902     switch(mode)
01903     {
01904     case NO_FILES_CHANGED:
01905         LOG_AUTOREG(("autoreg strategy: no files changed\n"));
01906         LOG_AUTOREG(("successful end of AutoRegister\n"));
01907         return NS_OK;
01908     case FILES_ADDED_ONLY:
01909         LOG_AUTOREG(("autoreg strategy: files added only\n"));
01910         if(!AddOnlyNewFilesFromFileList(mSearchPath, fileList, &workingSet))
01911         {
01912             LOG_AUTOREG(("FAILED to add new files\n"));
01913             return NS_ERROR_UNEXPECTED;
01914         }
01915         break;
01916     case FULL_VALIDATION_REQUIRED:
01917         LOG_AUTOREG(("autoreg strategy: doing full validation merge\n"));
01918         if(!DoFullValidationMergeFromFileList(mSearchPath, fileList, &workingSet))
01919         {
01920             LOG_AUTOREG(("FAILED to do full validation\n"));
01921             return NS_ERROR_UNEXPECTED;
01922         }
01923         break;
01924     default:
01925         NS_ERROR("switch missing a case");
01926         return NS_ERROR_UNEXPECTED;
01927     }
01928 
01929     // Failure to write the manifest is not fatal in production builds.
01930     // However, this would require the next startup to find and read all the
01931     // xpt files. This will make that startup slower. If this ever becomes a 
01932     // chronic problem for anyone, then we'll want to figure out why!
01933     
01934     if(!xptiManifest::Write(this, &workingSet))
01935     {
01936         LOG_AUTOREG(("FAILED to write manifest\n"));
01937         NS_ERROR("Failed to write xpti manifest!");
01938     }
01939     
01940     if(!MergeWorkingSets(&mWorkingSet, &workingSet))
01941     {
01942         LOG_AUTOREG(("FAILED to merge into live workingset\n"));
01943         return NS_ERROR_UNEXPECTED;
01944     }
01945 
01946 //    DEBUG_DumpFileListInWorkingSet(mWorkingSet);
01947 
01948     LOG_AUTOREG(("successful end of AutoRegister\n"));
01949 
01950     return NS_OK;
01951 }
01952 
01953 /***************************************************************************/
01954 
01955 class xptiAdditionalManagersEnumerator : public nsISimpleEnumerator 
01956 {
01957 public:
01958     NS_DECL_ISUPPORTS
01959     NS_DECL_NSISIMPLEENUMERATOR
01960 
01961     xptiAdditionalManagersEnumerator();
01962 
01963     PRBool SizeTo(PRUint32 likelyCount) {return mArray.SizeTo(likelyCount);}
01964     PRBool AppendElement(nsIInterfaceInfoManager* element);
01965 
01966 private:
01967     ~xptiAdditionalManagersEnumerator() {}
01968 
01969     nsSupportsArray mArray;
01970     PRUint32        mIndex;
01971     PRUint32        mCount;
01972 };
01973 
01974 NS_IMPL_ISUPPORTS1(xptiAdditionalManagersEnumerator, nsISimpleEnumerator)
01975 
01976 xptiAdditionalManagersEnumerator::xptiAdditionalManagersEnumerator()
01977     : mIndex(0), mCount(0)
01978 {
01979 }
01980 
01981 PRBool xptiAdditionalManagersEnumerator::AppendElement(nsIInterfaceInfoManager* element)
01982 {
01983     if(!mArray.AppendElement(NS_STATIC_CAST(nsISupports*, element)))
01984         return PR_FALSE;
01985     mCount++;
01986     return PR_TRUE;
01987 }
01988 
01989 /* boolean hasMoreElements (); */
01990 NS_IMETHODIMP xptiAdditionalManagersEnumerator::HasMoreElements(PRBool *_retval)
01991 {
01992     *_retval = mIndex < mCount;
01993     return NS_OK;
01994 }
01995 
01996 /* nsISupports getNext (); */
01997 NS_IMETHODIMP xptiAdditionalManagersEnumerator::GetNext(nsISupports **_retval)
01998 {
01999     if(!(mIndex < mCount))
02000     {
02001         NS_ERROR("Bad nsISimpleEnumerator caller!");
02002         return NS_ERROR_FAILURE;    
02003     }
02004 
02005     *_retval = mArray.ElementAt(mIndex++);
02006     return *_retval ? NS_OK : NS_ERROR_FAILURE;
02007 }
02008 
02009 /***************************************************************************/
02010 
02011 /* void addAdditionalManager (in nsIInterfaceInfoManager manager); */
02012 NS_IMETHODIMP xptiInterfaceInfoManager::AddAdditionalManager(nsIInterfaceInfoManager *manager)
02013 {
02014     nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(manager);
02015     nsISupports* ptrToAdd = weakRef ? 
02016                     NS_STATIC_CAST(nsISupports*, weakRef) :
02017                     NS_STATIC_CAST(nsISupports*, manager);
02018     { // scoped lock...
02019         nsAutoLock lock(mAdditionalManagersLock);
02020         PRInt32 index;
02021         nsresult rv = mAdditionalManagers.GetIndexOf(ptrToAdd, &index);
02022         if(NS_FAILED(rv) || -1 != index)
02023             return NS_ERROR_FAILURE;
02024         if(!mAdditionalManagers.AppendElement(ptrToAdd))
02025             return NS_ERROR_OUT_OF_MEMORY;
02026     }
02027     return NS_OK;
02028 }
02029 
02030 /* void removeAdditionalManager (in nsIInterfaceInfoManager manager); */
02031 NS_IMETHODIMP xptiInterfaceInfoManager::RemoveAdditionalManager(nsIInterfaceInfoManager *manager)
02032 {
02033     nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(manager);
02034     nsISupports* ptrToRemove = weakRef ? 
02035                     NS_STATIC_CAST(nsISupports*, weakRef) :
02036                     NS_STATIC_CAST(nsISupports*, manager);
02037     { // scoped lock...
02038         nsAutoLock lock(mAdditionalManagersLock);
02039         if(!mAdditionalManagers.RemoveElement(ptrToRemove))
02040             return NS_ERROR_FAILURE;
02041     }
02042     return NS_OK;
02043 }
02044 
02045 /* PRBool hasAdditionalManagers (); */
02046 NS_IMETHODIMP xptiInterfaceInfoManager::HasAdditionalManagers(PRBool *_retval)
02047 {
02048     PRUint32 count;
02049     nsresult rv = mAdditionalManagers.Count(&count);
02050     *_retval = count != 0;
02051     return rv;
02052 }
02053 
02054 /* nsISimpleEnumerator enumerateAdditionalManagers (); */
02055 NS_IMETHODIMP xptiInterfaceInfoManager::EnumerateAdditionalManagers(nsISimpleEnumerator **_retval)
02056 {
02057     nsAutoLock lock(mAdditionalManagersLock);
02058 
02059     PRUint32 count;
02060     nsresult rv = mAdditionalManagers.Count(&count);
02061     if(NS_FAILED(rv))
02062         return rv;
02063 
02064     nsCOMPtr<xptiAdditionalManagersEnumerator> enumerator = 
02065         new xptiAdditionalManagersEnumerator();
02066     if(!enumerator)
02067         return NS_ERROR_OUT_OF_MEMORY;
02068 
02069     enumerator->SizeTo(count);
02070 
02071     for(PRUint32 i = 0; i < count; /* i incremented in the loop body */)
02072     {
02073         nsCOMPtr<nsISupports> raw = 
02074             dont_AddRef(mAdditionalManagers.ElementAt(i++));
02075         if(!raw)
02076             return NS_ERROR_FAILURE;
02077         nsCOMPtr<nsIWeakReference> weakRef = do_QueryInterface(raw);
02078         if(weakRef)
02079         {
02080             nsCOMPtr<nsIInterfaceInfoManager> manager = 
02081                 do_QueryReferent(weakRef);
02082             if(manager)
02083             {
02084                 if(!enumerator->AppendElement(manager))
02085                     return NS_ERROR_FAILURE;
02086             }
02087             else
02088             {
02089                 // The manager is no more. Remove the element.
02090                 if(!mAdditionalManagers.RemoveElementAt(--i))
02091                     return NS_ERROR_FAILURE;
02092                 count--;
02093             }
02094         }
02095         else
02096         {
02097             // We *know* we put a pointer to either a nsIWeakReference or
02098             // an nsIInterfaceInfoManager into the array, so we can avoid an
02099             // extra QI here and just do a cast.
02100             if(!enumerator->AppendElement(
02101                     NS_REINTERPRET_CAST(nsIInterfaceInfoManager*, raw.get())))
02102                 return NS_ERROR_FAILURE;
02103         }
02104     }
02105     
02106     NS_ADDREF(*_retval = enumerator);
02107     return NS_OK;
02108 }
02109 
02110 /***************************************************************************/
02111 
02112 XPTI_PUBLIC_API(nsIInterfaceInfoManager*)
02113 XPTI_GetInterfaceInfoManager()
02114 {
02115     nsIInterfaceInfoManager* iim =
02116         xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef();
02117     NS_IF_ADDREF(iim);
02118     return iim;
02119 }
02120 
02121 XPTI_PUBLIC_API(void)
02122 XPTI_FreeInterfaceInfoManager()
02123 {
02124     xptiInterfaceInfoManager::FreeInterfaceInfoManager();
02125 }
02126