/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1999 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "prmem.h"
#include "prerror.h"
#include "prsystem.h"           // PR_GetDirectorySeparator
#include "nsNativeComponentLoader.h"
#include "nsComponentManager.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsIModule.h"
#include "xcDll.h"
#include "nsHashtable.h"
#include "nsHashtableEnumerator.h"
#include "nsXPIDLString.h"
#include "nsCRT.h"

#ifdef  XP_MAC  // sdagley dougt fix
#include <Files.h>
#include <Errors.h>
#include "nsILocalFileMac.h"
#endif

#define PRINT_CRITICAL_ERROR_TO_SCREEN 1
#define USE_REGISTRY 1
#define XPCOM_USE_NSGETFACTORY 1

// Logging of debug output
#define FORCE_PR_LOG /* Allow logging in the release build */
#include "prlog.h"
extern PRLogModuleInfo *nsComponentManagerLog;

nsNativeComponentLoader::nsNativeComponentLoader() :
    mRegistry(nsnull), mCompMgr(nsnull), mDllStore(nsnull)
{
    NS_INIT_REFCNT();
}

static PRBool
nsDll_Destroy(nsHashKey *aKey, void *aData, void* closure)
{
    nsDll* entry = NS_STATIC_CAST(nsDll*, aData);
    delete entry;
    return PR_TRUE;
}

nsNativeComponentLoader::~nsNativeComponentLoader()
{
    mRegistry = nsnull;
    mCompMgr = nsnull;

    delete mDllStore;
}
    
NS_IMPL_ISUPPORTS(nsNativeComponentLoader, NS_GET_IID(nsIComponentLoader));

NS_IMETHODIMP
nsNativeComponentLoader::GetFactory(const nsIID & aCID,
                                    const char *aLocation,
                                    const char *aType,
                                    nsIFactory **_retval)
{
    nsresult rv;

    if (!_retval)
        return NS_ERROR_NULL_POINTER;
    
    /* use a hashtable of WeakRefs to store the factory object? */

    /* Should this all live in xcDll? */
    nsDll *dll;
    PRInt64 mod = LL_Zero(), size = LL_Zero();
    rv = CreateDll(nsnull, aLocation, &mod, &size, &dll);
    if (NS_FAILED(rv))
        return rv;

    if (!dll)
        return NS_ERROR_OUT_OF_MEMORY;

    if (!dll->IsLoaded()) {
        PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
               ("nsNativeComponentLoader: loading \"%s\"",
                dll->GetDisplayPath()));

        if (!dll->Load()) {

            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
                   ("nsNativeComponentLoader: load FAILED"));
        
            char errorMsg[1024] = "<unknown; can't get error from NSPR>";

            if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
                PR_GetErrorText(errorMsg);

            DumpLoadError(dll, "GetFactory", errorMsg);

            return NS_ERROR_FAILURE;
        }
    }

    /* Get service manager for factory */
    nsIServiceManager* serviceMgr = NULL;
    rv = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
    if (NS_FAILED(rv))
        return rv;      // XXX translate error code?
    
    rv = GetFactoryFromModule(dll, aCID, _retval);
#ifdef XPCOM_USE_NSGETFACTORY
    if (NS_FAILED(rv)) {
        if (rv == NS_ERROR_FACTORY_NOT_LOADED) {
            rv = GetFactoryFromNSGetFactory(dll, aCID, serviceMgr, _retval);
        }
    }
#endif
    PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
           ("nsNativeComponentLoader: Factory creation %s for %s",
            (NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"),
            aLocation));

    // If the dll failed to get us a factory. But the dll registered that
    // it would be able to create a factory for this CID. mmh!
    // We cannot just delete the dll as the dll could be hosting
    // other CID for which factory creation can pass.
    // We will just let it be. The effect will be next time we try
    // creating the object, we will query the dll again. Since the
    // dll is loaded, this aint a big hit. So for optimized builds
    // this is ok to limp along.
    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Factory creation failed");
    
    return rv;
}

NS_IMETHODIMP
nsNativeComponentLoader::Init(nsIComponentManager *aCompMgr, nsISupports *aReg)
{
    nsresult rv;

    mCompMgr = aCompMgr;
    mRegistry = do_QueryInterface(aReg);
    if (!mCompMgr || !mRegistry)
        return NS_ERROR_INVALID_ARG;

    rv = mRegistry->GetSubtree(nsIRegistry::Common, xpcomComponentsKeyName,
                               &mXPCOMKey);
    if (NS_FAILED(rv))
        return rv;

    if (!mDllStore) {
        mDllStore = new nsObjectHashtable(nsnull, nsnull, // never copy
                                          nsDll_Destroy, nsnull,
                                          256, /* Thead Safe */ PR_TRUE);
        if (!mDllStore)
            return NS_ERROR_OUT_OF_MEMORY;
    }


    // Read in all dll entries and populate the mDllStore
    nsCOMPtr<nsIEnumerator> dllEnum;
    rv = mRegistry->EnumerateSubtrees( mXPCOMKey, getter_AddRefs(dllEnum));
    if (NS_FAILED(rv)) return rv;

    rv = dllEnum->First();
    for (; NS_SUCCEEDED(rv) && (dllEnum->IsDone() != NS_OK); (rv = dllEnum->Next()))
    {
        nsCOMPtr<nsISupports> base;
        rv = dllEnum->CurrentItem(getter_AddRefs(base));
        if (NS_FAILED(rv))  continue;

        // Get specific interface.
        nsIID nodeIID = NS_IREGISTRYNODE_IID;
        nsCOMPtr<nsIRegistryNode> node;
        rv = base->QueryInterface( nodeIID, getter_AddRefs(node) );
        if (NS_FAILED(rv)) continue;

        // Get library name
        nsXPIDLCString library;
        rv = node->GetNameUTF8(getter_Copies(library));
        if (NS_FAILED(rv)) continue;

        // Get key associated with library
        nsRegistryKey libKey;
        rv = node->GetKey(&libKey);
        if (NS_FAILED(rv)) continue;

        // Create nsDll with this name
        nsDll *dll = NULL;
        PRInt64 lastModTime;
        PRInt64 fileSize;
        GetRegistryDllInfo(libKey, &lastModTime, &fileSize);
        rv = CreateDll(NULL, library, &lastModTime, &fileSize, &dll);
        if (NS_FAILED(rv)) continue;
    }

    return NS_OK;
}

NS_IMETHODIMP
nsNativeComponentLoader::AutoRegisterComponents(PRInt32 aWhen,
                                                nsIFile *aDirectory)
{
#ifdef DEBUG
    /* do we _really_ want to print this every time? */
    printf("nsNativeComponentLoader: autoregistering begins.\n");
#endif

    nsresult rv = RegisterComponentsInDir(aWhen, aDirectory);

#ifdef DEBUG
    printf("nsNativeComponentLoader: autoregistering %s\n",
           NS_FAILED(rv) ? "FAILED" : "succeeded");
#endif

    return rv;
}

nsresult
nsNativeComponentLoader::RegisterComponentsInDir(PRInt32 when,
                                                 nsIFile *dir)
{
    nsresult rv = NS_ERROR_FAILURE;
    PRBool isDir = PR_FALSE;

#if 0
    // Going to many of these checks is a performance hit on the mac.
    // Since these routines are called relatively infrequently and
    // we will fail anyway down the line if a directory aint there,
    // we are commenting this check out.

    // Make sure we are dealing with a directory
    rv = dir->IsDirectory(&isDir);
    if (NS_FAILED(rv)) return rv;

    if (!isDir)
        return NS_ERROR_INVALID_ARG;
#endif /* 0 */

    // Create a directory iterator
    nsCOMPtr<nsISimpleEnumerator> dirIterator;
    rv = dir->GetDirectoryEntries(getter_AddRefs(dirIterator));
    
    if (NS_FAILED(rv)) return rv;
    
    // whip through the directory to register every file
    nsCOMPtr<nsIFile> dirEntry;
    PRBool more = PR_FALSE;

    rv = dirIterator->HasMoreElements(&more);
    if (NS_FAILED(rv)) return rv;
    while (more == PR_TRUE)
    {
        rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry));
        if (NS_SUCCEEDED(rv))
        {
            rv = dirEntry->IsDirectory(&isDir);
            if (NS_SUCCEEDED(rv))
            {
                if (isDir == PR_TRUE)
                {
                    // This is a directory. Grovel for components into the directory.
                    rv = RegisterComponentsInDir(when, dirEntry);
                }
                else
                {
                    PRBool registered;
                    // This is a file. Try to register it.
                    rv = AutoRegisterComponent(when, dirEntry, &registered);
                }
            }
        }
        rv = dirIterator->HasMoreElements(&more);
        if (NS_FAILED(rv)) return rv;
    }
    
    return rv;
}

static nsresult
nsFreeLibrary(nsDll *dll, nsIServiceManager *serviceMgr, PRInt32 when)
{
    nsresult rv = NS_ERROR_FAILURE;

    if (!dll || dll->IsLoaded() == PR_FALSE)
    {
        return NS_ERROR_INVALID_ARG;
    }

    // Get if the dll was marked for unload in an earlier round
    PRBool dllMarkedForUnload = dll->IsMarkedForUnload();

    // Reset dll marking for unload just in case we return with
    // an error.
    dll->MarkForUnload(PR_FALSE);

    PRBool canUnload = PR_FALSE;

    // Get the module object
    nsCOMPtr<nsIModule> mobj;
    /* XXXshaver cheat and use the global component manager */
    rv = dll->GetModule(NS_STATIC_CAST(nsIComponentManager*, nsComponentManagerImpl::gComponentManager),
                        getter_AddRefs(mobj));
    if (NS_SUCCEEDED(rv))
    {
        rv = mobj->CanUnload(nsComponentManagerImpl::gComponentManager, &canUnload);
    }
#ifndef OBSOLETE_MODULE_LOADING
    else
    {
        // Try the old method of module unloading
        nsCanUnloadProc proc = (nsCanUnloadProc)dll->FindSymbol("NSCanUnload");
        if (proc)
        {
            canUnload = proc(serviceMgr);
            rv = NS_OK;	// No error status returned by call.
        }
        else
        {
            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
                   ("nsNativeComponentLoader: Unload cant get nsIModule or NSCanUnload for %s",
                    dll->GetDisplayPath()));
            return rv;
        }
    }
#endif /* OBSOLETE_MODULE_LOADING */

    mobj = nsnull; // Release our reference to the module object

    // When shutting down, whether we can unload the dll or not,
    // we will shutdown the dll to release any memory it has got
    if (when == nsIComponentManager::NS_Shutdown)
    {
        dll->Shutdown();
    }
        
    // Check error status on CanUnload() call
    if (NS_FAILED(rv))
    {
        PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
               ("nsNativeComponentLoader: nsIModule::CanUnload() returned error for %s.",
                dll->GetDisplayPath()));
        return rv;
    }

    if (canUnload)
    {
        if (dllMarkedForUnload)
        {
            PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, 
                   ("nsNativeComponentLoader: + Unloading \"%s\".", dll->GetDisplayPath()));
#if 0
            // XXX dlls aren't counting their outstanding instances correctly
            // XXX hence, dont unload until this gets enforced.
            rv = dll->Unload();
#endif /* 0 */
        }
        else
        {
            PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
                   ("nsNativeComponentLoader: Ready for unload \"%s\".", dll->GetDisplayPath()));
        }
    }
    else
    {
        PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, 
               ("nsNativeComponentLoader: NOT ready for unload %s", dll->GetDisplayPath()));
        rv = NS_ERROR_FAILURE;
    }
    return rv;
}

struct freeLibrariesClosure
{
    nsIServiceManager *serviceMgr;
    PRInt32 when;
};

static PRBool
nsFreeLibraryEnum(nsHashKey *aKey, void *aData, void* closure) 
{
    nsDll *dll = (nsDll *) aData;
    struct freeLibrariesClosure *callData = (struct freeLibrariesClosure *) closure;
    nsFreeLibrary(dll,
                  (callData ? callData->serviceMgr : NULL),
                  (callData ? callData->when : nsIComponentManager::NS_Timer));
    return PR_TRUE;
}

/*
 * SelfRegisterDll
 *
 * Given a dll abstraction, this will load, selfregister the dll and
 * unload the dll.
 *
 */
nsresult
nsNativeComponentLoader::SelfRegisterDll(nsDll *dll,
                                         const char *registryLocation,
                                         PRBool deferred)
{
    // Precondition: dll is not loaded already, unless we're deferred
    PR_ASSERT(deferred || dll->IsLoaded() == PR_FALSE);

    nsIServiceManager* serviceMgr = NULL;
    nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
    if (NS_FAILED(res)) return res;

    if (dll->Load() == PR_FALSE)
    {
        // Cannot load. Probably not a dll.
        char errorMsg[1024] = "Cannot get error from nspr. Not enough memory.";
        if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
            PR_GetErrorText(errorMsg);

        DumpLoadError(dll, "SelfRegisterDll", errorMsg);

        return NS_ERROR_FAILURE;
    }

    PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
           ("nsNativeComponentLoader: Loaded \"%s\".", dll->GetDisplayPath()));

    // Tell the module to self register
    nsCOMPtr<nsIModule> mobj;
    res = dll->GetModule(mCompMgr, getter_AddRefs(mobj));
    if (NS_SUCCEEDED(res))
    {
        nsCOMPtr<nsIFile> fs;
        /*************************************************************
         * WARNING: Why are use introducing 'res2' here and then     *
         * later assigning it to 'res' rather than just using 'res'? *
         * This is because this code turns up a code-generation bug  *
         * in VC6 on NT. Assigning to 'res' on the next line causes  *
         * the value of 'dll' to get nulled out! The two seem to be  *
         * getting aliased together during compilation.              *
         *************************************************************/
        nsresult res2 = dll->GetDllSpec(getter_AddRefs(fs));    // don't change 'res2' -- see warning, above
        if (NS_SUCCEEDED(res2)) {
            res = mobj->RegisterSelf(mCompMgr, fs, registryLocation,
                                     nativeComponentType);
        }
        else
        {
            res = res2;         // don't take this out -- see warning, above
            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, 
                   ("nsNativeComponentLoader: dll->GetDllSpec() on %s FAILED.",
                    dll->GetDisplayPath()));
        }
        mobj = NULL;    // Force a release of the Module object before unload()
    }
#ifndef OBSOLETE_MODULE_LOADING
    else
    {
        res = NS_ERROR_NO_INTERFACE;
        nsRegisterProc regproc = (nsRegisterProc)dll->FindSymbol("NSRegisterSelf");
        if (regproc)
        {
            // Call the NSRegisterSelfProc to enable dll registration
            res = regproc(serviceMgr, registryLocation);
            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, 
                   ("nsNativeComponentLoader: %s using OBSOLETE NSRegisterSelf()",
                    dll->GetDisplayPath()));
        }
    }
#endif /* OBSOLETE_MODULE_LOADING */

    // Update the timestamp and size of the dll in registry
    // Don't enter deferred modules in the registry, because it might only be
    // able to register on some later autoreg, after another component has been
    // installed.
    if (res != NS_ERROR_FACTORY_REGISTER_AGAIN) {
        dll->Sync();
        
        PRInt64 modTime, size;
        dll->GetLastModifiedTime(&modTime);
        dll->GetSize(&size);

        SetRegistryDllInfo(registryLocation, &modTime, &size);
    }

    return res;
}

//
// MOZ_DEMANGLE_SYMBOLS is only a linux + MOZ_DEBUG thing.
//

#if defined(MOZ_DEMANGLE_SYMBOLS)
#include "nsTraceRefcnt.h" // for nsTraceRefcnt::DemangleSymbol()
#endif

nsresult 
nsNativeComponentLoader::DumpLoadError(nsDll *dll, 
                                       const char *aCallerName,
                                       const char *aNsprErrorMsg)
{
    PR_ASSERT(aCallerName != NULL);
    
    if (nsnull == dll || nsnull == aNsprErrorMsg)
        return NS_OK;

    nsCAutoString errorMsg(aNsprErrorMsg);

#if defined(MOZ_DEMANGLE_SYMBOLS)
	// Demangle undefined symbols
	nsCAutoString undefinedMagicString("undefined symbol:");
    
	PRInt32 offset = errorMsg.Find(undefinedMagicString, PR_TRUE);
    
	if (offset != kNotFound)
	{
        nsCAutoString symbol(errorMsg);
        nsCAutoString demangledSymbol("");
        
        symbol.Cut(0,offset);
        
        symbol.Cut(0,undefinedMagicString.Length());
        
        symbol.StripWhitespace();
        
        char demangled[4096] = "\0";
        
        nsTraceRefcnt::DemangleSymbol(symbol,demangled,sizeof(demangled));
        
        if (demangled && strlen(demangled))
            demangledSymbol = demangled;
        
        if (demangledSymbol != (const char *) "")
        {
            nsCAutoString tmp(errorMsg);
            
            
            tmp.Cut(offset + undefinedMagicString.Length(),
                    tmp.Length() - offset - undefinedMagicString.Length());
            
            tmp += " \n";
            
            tmp += demangledSymbol;
            
            errorMsg = tmp;
        }	  
	}
#endif // MOZ_DEMANGLE_SYMBOLS
    
    // Do NSPR log
    PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
           ("nsNativeComponentLoader: %s(%s) Load FAILED with error:%s", 
            aCallerName,
            dll->GetDisplayPath(), 
            (const char *) errorMsg));
    

    // Dump to screen if needed
#ifdef PRINT_CRITICAL_ERROR_TO_SCREEN
    printf("**************************************************\n"
           "nsNativeComponentLoader: %s(%s) Load FAILED with error: %s\n"
           "**************************************************\n",
           aCallerName,
           dll->GetDisplayPath(), 
           (const char *) errorMsg);
#endif

    return NS_OK;
}

nsresult
nsNativeComponentLoader::SelfUnregisterDll(nsDll *dll)
{
    // Precondition: dll is not loaded
    PR_ASSERT(dll->IsLoaded() == PR_FALSE);

    nsIServiceManager* serviceMgr = NULL;
    nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
    if (NS_FAILED(res)) return res;

    if (dll->Load() == PR_FALSE)
    {
        // Cannot load. Probably not a dll.
        return(NS_ERROR_FAILURE);
    }
        
    // Tell the module to self register
    nsCOMPtr<nsIModule> mobj;
    res = dll->GetModule(mCompMgr, getter_AddRefs(mobj));
    if (NS_SUCCEEDED(res))
    {
        PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, 
               ("nsNativeComponentLoader: %s using nsIModule to unregister self.", dll->GetDisplayPath()));
        nsCOMPtr<nsIFile> fs;
        res = dll->GetDllSpec(getter_AddRefs(fs));
        if (NS_SUCCEEDED(res))
            res = mobj->UnregisterSelf(mCompMgr, fs, /* XXX location */ "");
        else
        {
            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, 
                   ("nsNativeComponentLoader: dll->GetDllSpec() on %s FAILED.", dll->GetDisplayPath()));
        }
        mobj = NULL;    // Force a release of the Module object before unload()
    }
#ifndef OBSOLETE_MODULE_LOADING
    else
    {
        res = NS_ERROR_NO_INTERFACE;
        nsUnregisterProc unregproc =
            (nsUnregisterProc) dll->FindSymbol("NSUnregisterSelf");
        if (unregproc)
        {
            // Call the NSUnregisterSelfProc to enable dll de-registration
            res = unregproc(serviceMgr, dll->GetPersistentDescriptorString());
        }
    }
#endif /* OBSOLETE_MODULE_LOADING */
    dll->Unload();
    return res;
}

nsresult
nsNativeComponentLoader::AutoRegisterComponent(PRInt32 when,
                                               nsIFile *component,
                                               PRBool *registered)
{
    nsresult rv;
    if (!registered)
        return NS_ERROR_NULL_POINTER;

    /* this should be a pref or registry entry, or something */
    const char *ValidDllExtensions[] = {
        ".dll",     /* Windows */
        ".so",      /* Unix */
        ".shlb",    /* Mac ? */
        ".dso",     /* Unix ? */
        ".dylib",   /* Unix: Rhapsody */
        ".so.1.0",  /* Unix: BSD */
        ".sl",      /* Unix: HP-UX */
#if defined(VMS)
        ".exe",     /* Open VMS */
#endif
        ".dlm",     /* new for all platforms */
        NULL
    };

    *registered = PR_FALSE;

#if 0
    // This is a performance hit on mac. Since we have already checked
    // this; plus is we dont, load will fail anyway later on, this
    // is being commented out.

    // Ensure we are dealing with a file as opposed to a dir
    PRBool b = PR_FALSE;

    rv = component->IsFile(&b);
    if (NS_FAILED(rv) || !b)
        return rv;
#endif /* 0 */

    // deal only with files that have a valid extension
    PRBool validExtension = PR_FALSE;

#ifdef  XP_MAC  // sdagley dougt fix
    // rjc - on Mac, check the file's type code (skip checking the creator code)

	nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(component);
	if (localFileMac)
	{
		OSType	type;
		OSType	creator;
		rv = localFileMac->GetFileTypeAndCreator(&type, &creator);
		if (NS_SUCCEEDED(rv))
		{
	        // on Mac, Mozilla shared libraries are of type 'shlb'
	        // Note: we don't check the creator (which for Mozilla is 'MOZZ')
	        // so that 3rd party shared libraries will be noticed!
			if (type == 'shlb')
			{
            	validExtension = PR_TRUE;
			}
		}
	}

#else
    char *leafName = NULL;
    rv = component->GetLeafName(&leafName);
    if (NS_FAILED(rv)) return rv;
    int flen = PL_strlen(leafName);
    for (int i=0; ValidDllExtensions[i] != NULL; i++)
    {
        int extlen = PL_strlen(ValidDllExtensions[i]);
            
        // Does fullname end with this extension
        if (flen >= extlen &&
            !PL_strcasecmp(&(leafName[flen - extlen]), ValidDllExtensions[i])
            )
        {
            validExtension = PR_TRUE;
            break;
        }
    }
    if (leafName) nsAllocator::Free(leafName);
#endif
        
    if (validExtension == PR_FALSE)
        // Skip invalid extensions
        return NS_OK;

    nsXPIDLCString persistentDescriptor;
    rv = mCompMgr->RegistryLocationForSpec(component, 
                                           getter_Copies(persistentDescriptor));
    if (NS_FAILED(rv))
        return rv;

    nsStringKey key(persistentDescriptor);

    // Get the registry representation of the dll, if any
    nsDll *dll;
    PRInt64 mod = LL_Zero(), size = LL_Zero();
    rv = CreateDll(component, persistentDescriptor, &mod, &size, &dll);
    if (NS_FAILED(rv))
        return rv;

    if (dll != NULL)
    {
        // Make sure the dll is OK
        if (dll->GetStatus() != NS_OK)
        {
            PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, 
                   ("nsNativeComponentLoader: + nsDll not NS_OK \"%s\". Skipping...",
                    dll->GetDisplayPath()));
            return NS_ERROR_FAILURE;
        }
            
        // We already have seen this dll. Check if this dll changed
        if (!dll->HasChanged())
        {
            // Dll hasn't changed. Skip.
            PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, 
                   ("nsNativeComponentLoader: + nsDll not changed \"%s\". Skipping...",
                    dll->GetDisplayPath()));
            return NS_OK;
        }

        // Aagh! the dll has changed since the last time we saw it.
        // re-register dll
        if (dll->IsLoaded())
        {
            // We loaded the old version of the dll and now we find that the
            // on-disk copy if newer. Try to unload the dll.
            nsIServiceManager *serviceMgr = NULL;
            nsServiceManager::GetGlobalServiceManager(&serviceMgr);
            rv = nsFreeLibrary(dll, serviceMgr, when);
            if (NS_FAILED(rv))
            {
                // THIS IS THE WORST SITUATION TO BE IN.
                // Dll doesn't want to be unloaded. Cannot re-register
                // this dll.
                PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
                       ("nsNativeComponentLoader: *** Dll already loaded. "
                        "Cannot unload either. Hence cannot re-register "
                        "\"%s\". Skipping...", dll->GetDisplayPath()));
                return rv;
            }
            else {
                                // dll doesn't have a CanUnload proc. Guess it is
                                // ok to unload it.
                dll->Unload();
                PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, 
                       ("nsNativeComponentLoader: + Unloading \"%s\". (no CanUnloadProc).",
                        dll->GetDisplayPath()));
            }
                
        } // dll isloaded
            
        // Sanity.
        if (dll->IsLoaded())
        {
            // We went through all the above to make sure the dll
            // is unloaded. And here we are with the dll still
            // loaded. Whoever taught dp programming...
            PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
                   ("nsNativeComponentLoader: Dll still loaded. Cannot re-register "
                    "\"%s\". Skipping...", dll->GetDisplayPath()));
            return NS_ERROR_FAILURE;
        }
    } // dll != NULL
    else
    {
        // Create and add the dll to the mDllStore
        // It is ok to do this even if the creation of nsDll
        // didnt succeed. That way we wont do this again
        // when we encounter the same dll.
        dll = new nsDll(persistentDescriptor);
        if (dll == NULL)
            return NS_ERROR_OUT_OF_MEMORY;
        mDllStore->Put(&key, (void *) dll);
    } // dll == NULL
        
    // Either we are seeing the dll for the first time or the dll has
    // changed since we last saw it and it is unloaded successfully.
    //
    // Now we can try register the dll for sure. 
    nsresult res = SelfRegisterDll(dll, persistentDescriptor, PR_FALSE);
    if (NS_FAILED(res))
    {
        if (res == NS_ERROR_FACTORY_REGISTER_AGAIN) {
            /* defer for later loading */
            mDeferredComponents.AppendElement(dll);
            return NS_OK;
        } else {
            PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
                   ("nsNativeComponentLoader: Autoregistration FAILED for "
                    "\"%s\". Skipping...", dll->GetDisplayPath()));
            return NS_ERROR_FACTORY_NOT_REGISTERED;
        }
    }
    else
    {
        PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
               ("nsNativeComponentLoader: Autoregistration Passed for "
                "\"%s\".", dll->GetDisplayPath()));
        // Marking dll along with modified time and size in the
        // registry happens at PlatformRegister(). No need to do it
        // here again.
    }
    return NS_OK;
}

nsresult
nsNativeComponentLoader::RegisterDeferredComponents(PRInt32 aWhen,
                                                    PRBool *aRegistered)
{
    fprintf(stderr, "nNCL: registering deferred (%d)\n",
            mDeferredComponents.Count());
    *aRegistered = PR_FALSE;
    if (!mDeferredComponents.Count())
        return NS_OK;
    
    for (int i = mDeferredComponents.Count() - 1; i >= 0; i--) {
        nsDll *dll = NS_STATIC_CAST(nsDll *, mDeferredComponents[i]);
        nsresult rv = SelfRegisterDll(dll,
                                      dll->GetRegistryLocation(),
                                      PR_TRUE);
        if (rv != NS_ERROR_FACTORY_REGISTER_AGAIN) {
            if (NS_SUCCEEDED(rv))
                *aRegistered = PR_TRUE;
            mDeferredComponents.RemoveElementAt(i);
        }
    }

    if (*aRegistered)
        fprintf(stderr, "nNCL: registered deferred, %d left\n",
                mDeferredComponents.Count());
    else
        fprintf(stderr, "nNCL: didn't register any components, %d left\n",
                mDeferredComponents.Count());
    /* are there any fatal errors? */
    return NS_OK;
}

nsresult
nsNativeComponentLoader::OnRegister(const nsIID &aCID, const char *aType,
                                    const char *aClassName,
                                    const char *aProgID, const char *aLocation,
                                    PRBool aReplace, PRBool aPersist)
{
    return NS_OK;
}

nsresult
nsNativeComponentLoader::UnloadAll(PRInt32 aWhen)
{
    PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsNativeComponentLoader: Unloading...."));

    struct freeLibrariesClosure callData;
    callData.serviceMgr = NULL; // XXX need to get this as a parameter
    callData.when = aWhen;

    // Cycle through the dlls checking to see if they want to be unloaded
    mDllStore->Enumerate(nsFreeLibraryEnum, &callData);
    return NS_OK;
}

nsresult
nsNativeComponentLoader::GetRegistryDllInfo(const char *aLocation,
                                            PRInt64 *lastModifiedTime,
                                            PRInt64 *fileSize)
{
    nsresult rv;

    nsRegistryKey key;
    rv = mRegistry->GetSubtreeRaw(mXPCOMKey, aLocation, &key);
    if (NS_FAILED(rv)) return rv;

    return GetRegistryDllInfo(key, lastModifiedTime, fileSize);
}

nsresult
nsNativeComponentLoader::GetRegistryDllInfo(nsRegistryKey key,
                                            PRInt64 *lastModifiedTime,
                                            PRInt64 *fileSize)
{
    PRInt64 lastMod;
    nsresult rv = mRegistry->GetLongLong(key, lastModValueName, &lastMod);
    if (NS_FAILED(rv)) return rv;
    *lastModifiedTime = lastMod;
        
    PRInt64 fsize;
    rv = mRegistry->GetLongLong(key, fileSizeValueName, &fsize);
    if (NS_FAILED(rv)) return rv;
    *fileSize = fsize;

    return NS_OK;
}

nsresult
nsNativeComponentLoader::SetRegistryDllInfo(const char *aLocation,
                                            PRInt64 *lastModifiedTime,
                                            PRInt64 *fileSize)
{
    nsresult rv;
    nsRegistryKey key;
    rv = mRegistry->GetSubtreeRaw(mXPCOMKey, aLocation, &key);
    if (NS_FAILED(rv)) return rv;

    rv = mRegistry->SetLongLong(key, lastModValueName, lastModifiedTime);
    if (NS_FAILED(rv)) return rv;
    rv = mRegistry->SetLongLong(key, fileSizeValueName, fileSize);
    return rv;
}

//
// CreateDll
// The only way to create a dll or get it from the dll cache. This will
// be called in multiple situations:
//
// 1. Autoregister will create one for each dll it is trying to register. This
//		call will be passing a spec in.
//		{spec, NULL, 0, 0}
//
// 2. GetFactory() This will call CreateDll() with a null spec but will give
//		the registry represented name of the dll. If modtime and size are zero,
//		we will go the registry to find the right modtime and size.
//		{NULL, rel:libpref.so, 0, 0}
//
// 3. Prepopulation of dllCache A dll object created off a registry entry.
//		Specifically dll name is stored	in rel: or abs: or lib: formats in the
//		registry along with its	lastModTime and fileSize.
//		{NULL, rel:libpref.so, 8985659, 20987}
nsresult
nsNativeComponentLoader::CreateDll(nsIFile *aSpec, const char *aLocation,
                                   PRInt64 *modificationTime, PRInt64 *fileSize,
                                   nsDll **aDll)
{
    nsDll *dll;
    nsCOMPtr<nsIFile> dllSpec;
    nsCOMPtr<nsIFile> spec;
    nsresult rv;

    nsStringKey key(aLocation);
    dll = (nsDll *)mDllStore->Get(&key);
    if (dll)
    {
        *aDll = dll;
        return NS_OK;
    }

    if (!aSpec)
    {
        if (!nsCRT::strncmp(aLocation, XPCOM_LIB_PREFIX, 4)) {
            dll = new nsDll(aLocation+4, 1 /* dumb magic flag */);
            if (!dll) return NS_ERROR_OUT_OF_MEMORY;
        } else {
            rv = mCompMgr->SpecForRegistryLocation(aLocation,
                                                   getter_AddRefs(spec));
            if (NS_FAILED(rv))
                return rv;
        }
    }
    else
    {
        spec = aSpec;
    }

    if (!dll)
    {
        PR_ASSERT(modificationTime != NULL);
        PR_ASSERT(fileSize != NULL);

        PRInt64 zit = LL_Zero();
        
        if (LL_EQ(*modificationTime,zit) && LL_EQ(*fileSize,zit))
        {
            // Get the modtime and filesize from the registry
            rv = GetRegistryDllInfo(aLocation, modificationTime, fileSize);
        }
        dll = new nsDll(spec, aLocation, modificationTime, fileSize);
    }

    if (!dll)
        return NS_ERROR_OUT_OF_MEMORY;

    *aDll = dll;
    mDllStore->Put(&key, dll);
    return NS_OK;
}

nsresult
nsNativeComponentLoader::GetFactoryFromModule(nsDll *aDll, const nsCID &aCID,
                                              nsIFactory **aFactory)
{
    nsresult rv;

    nsCOMPtr<nsIModule> module;
    rv = aDll->GetModule(mCompMgr, getter_AddRefs(module));

    if (NS_FAILED(rv))
        return rv;

    return module->GetClassObject(mCompMgr, aCID, NS_GET_IID(nsIFactory),
                                  (void **)aFactory);
}

nsresult
nsNativeComponentLoader::GetFactoryFromNSGetFactory(nsDll *aDll,
                                                    const nsCID &aCID,
                                                    nsIServiceManager *aServMgr,
                                                    nsIFactory **aFactory)
{
#ifdef XPCOM_USE_NSGETFACTORY
    nsFactoryProc getFactory =
        (nsFactoryProc) aDll->FindSymbol("NSGetFactory");

    if (!getFactory)
        return NS_ERROR_FACTORY_NOT_LOADED;
    
    PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
           ("nsNativeComponentLoader: %s using OBSOLETE NSGetFactory()\n",
            aDll->GetDisplayPath()));

    /*
     * There was a time when CLSIDToProgID was used to get className
     * and progID, but that day is long past.  This code is not long
     * for this earth, so we just pass nsnull.
     */

    return getFactory(aServMgr, aCID, nsnull /*className */,
                      nsnull /* progID */, aFactory);

#else /* !XPCOM_USE_NSGETFACTORY */
    return NS_ERROR_FACTORY_NOT_LOADED;
#endif /* XPCOM_USE_NSGETFACTORY */
}
