/* -*- 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.0 (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) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "nsPluginManager.h"
#include "nsPluginInstancePeer.h"
#include "nsPluginInputStream.h"

////////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION OF NETSCAPE COMMUNICATOR PLUGINS (NEW C++ API).
//
// This superscedes the old plugin API (npapi.h, npupp.h), and 
// eliminates the need for glue files: npunix.c, npwin.cpp and npmac.cpp. 
// Correspondences to the old API are shown throughout the file.
////////////////////////////////////////////////////////////////////////////////

#include "npglue.h" 
#include "np.h" 

#ifdef OJI
#include "nsplugin.h"
#include "jvmmgr.h" 
#ifdef PRE_SERVICE_MANAGER
#include "nsILiveconnect.h"
#endif // PRE_SERVICE_MANAGER
#endif

#ifdef XP_MAC
#include "MacMemAllocator.h"
#include "asyncCursors.h"
#include "LMenuSharing.h"
#include <TArray.h>
#endif

#include "prthread.h"
/* This is a private NSPR header - cls */
#ifdef OJI
#  ifdef XP_MAC
#    include "pprthred.h"
#  else
#    include "private/pprthred.h"
#  endif
#endif
#include "prtypes.h"
#include "nsHashtable.h"
#ifdef PRE_SERVICE_MANAGER
#include "nsMalloc.h"
#include "nsICapsManager.h"
#include "nsCCapsManager.h"
#endif // PRE_SERVICE_MANAGER

static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
static NS_DEFINE_IID(kIPluginManager2IID, NS_IPLUGINMANAGER2_IID);
static NS_DEFINE_IID(kIJNIEnvIID, NS_IJNIENV_IID); 
static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID);

#include "prerror.h"

// mapping from NPError to nsresult
nsresult fromNPError[] = {
    NS_OK,                          // NPERR_NO_ERROR,
    NS_ERROR_FAILURE,               // NPERR_GENERIC_ERROR,
    NS_ERROR_FAILURE,               // NPERR_INVALID_INSTANCE_ERROR,
    NS_ERROR_NOT_INITIALIZED,       // NPERR_INVALID_FUNCTABLE_ERROR,
    NS_ERROR_FACTORY_NOT_LOADED,    // NPERR_MODULE_LOAD_FAILED_ERROR,
    NS_ERROR_OUT_OF_MEMORY,         // NPERR_OUT_OF_MEMORY_ERROR,
    NS_NOINTERFACE,                 // NPERR_INVALID_PLUGIN_ERROR,
    NS_ERROR_ILLEGAL_VALUE,         // NPERR_INVALID_PLUGIN_DIR_ERROR,
    NS_NOINTERFACE,                 // NPERR_INCOMPATIBLE_VERSION_ERROR,
    NS_ERROR_ILLEGAL_VALUE,         // NPERR_INVALID_PARAM,
    NS_ERROR_ILLEGAL_VALUE,         // NPERR_INVALID_URL,
    NS_ERROR_ILLEGAL_VALUE,         // NPERR_FILE_NOT_FOUND,
    NS_ERROR_FAILURE,               // NPERR_NO_DATA,
    NS_ERROR_FAILURE                // NPERR_STREAM_NOT_SEEKABLE,
};

////////////////////////////////////////////////////////////////////////////////
// THINGS IMPLEMENTED BY THE BROWSER...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Plugin Manager Interface
// This interface defines the minimum set of functionality that a plugin
// manager will support if it implements plugins. Plugin implementations can
// QueryInterface to determine if a plugin manager implements more specific 
// APIs for the plugin to use.

#ifdef PRE_SERVICE_MANAGER
nsPluginManager* thePluginManager = NULL;
#endif // PRE_SERVICE_MANAGER

nsPluginManager::nsPluginManager(nsISupports* outer)
    : fAllocatedMenuIDs(NULL), fWaiting(0), fOldCursor(NULL)
#ifdef PRE_SERVICE_MANAGER
    , fJVMMgr(NULL), fMalloc(NULL), fFileUtils(NULL), fCapsManager(NULL), fLiveconnect(NULL)
#endif
{
    NS_INIT_AGGREGATED(outer);
}

nsPluginManager::~nsPluginManager(void)
{
#ifdef PRE_SERVICE_MANAGER
    if (fJVMMgr) {
        fJVMMgr->Release();
        fJVMMgr = NULL;
    }
    if (fMalloc) {
        fMalloc->Release();
        fMalloc = NULL;
    }
    if (fFileUtils) {
        fFileUtils->Release();
        fFileUtils = NULL;
    }

    if (fCapsManager) {
        fCapsManager->Release();
        fCapsManager = NULL;
    }

    if (fLiveconnect) {
        fCapsManager->Release();
        fCapsManager = NULL;
    }
#endif // PRE_SERVICE_MANAGER

#ifdef XP_MAC
    if (fAllocatedMenuIDs != NULL) {
        // Fix me, delete all the elements before deleting the table.
        delete fAllocatedMenuIDs;
    }
#endif
}

NS_IMPL_AGGREGATED(nsPluginManager);

NS_METHOD
nsPluginManager::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
    if (outer && !aIID.Equals(kISupportsIID))
        return NS_NOINTERFACE;   // XXX right error?
    nsPluginManager* mgr = new nsPluginManager(outer);
    if (mgr == NULL)
        return NS_ERROR_OUT_OF_MEMORY;
    mgr->AddRef();
    *aInstancePtr = outer ? (void *)mgr->GetInner() : (void *)mgr;
    return NS_OK;
}

NS_METHOD
nsPluginManager::GetValue(nsPluginManagerVariable variable, void *value)
{
    NPError err = npn_getvalue(NULL, (NPNVariable)variable, value);
    return fromNPError[err];
}

#if 0
NS_METHOD
nsPluginManager::SetValue(nsPluginManagerVariable variable, void *value)
{
    NPError err = npn_setvalue(NULL, (NPPVariable)variable, value);
    return fromNPError[err];
}
#endif

NS_METHOD
nsPluginManager::ReloadPlugins(PRBool reloadPages)
{
    npn_reloadplugins(reloadPages);
    return NS_OK;
}

NS_METHOD
nsPluginManager::UserAgent(const char* *resultingAgentString)
{
    *resultingAgentString = npn_useragent(NULL); // we don't really need an npp here
    return NS_OK;
}

NS_METHOD
nsPluginManager::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr) 
{
    if (NULL == aInstancePtr) {                                            
        return NS_ERROR_NULL_POINTER;                                        
    }                                                                      
    if (aIID.Equals(kIPluginManagerIID) || 
        aIID.Equals(kIPluginManager2IID) ||
        aIID.Equals(kISupportsIID)) {
        *aInstancePtr = (nsIPluginManager*) this; 
        AddRef(); 
        return NS_OK; 
    }
    return NS_NOINTERFACE;
}

#ifdef PRE_SERVICE_MANAGER
nsICapsManager*
nsPluginManager::GetCapsManager(const nsIID& aIID)
{
    nsICapsManager* result = NULL;
    nsresult        err    = NS_OK;
    PRThread       *threadAttached = NULL;
#ifdef OJI
    if (fCapsManager == NULL) {
        if ( PR_GetCurrentThread() == NULL )
        {
            threadAttached = PR_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL);
        }

        NS_DEFINE_CID(kCCapsManagerCID, NS_CCAPSMANAGER_CID);
        nsresult err    = NS_OK;
        err = nsComponentManager::CreateInstance(kCCapsManagerCID, 
                                           (nsIPluginManager*)this,    /* outer */
                                           kISupportsIID,
                                           (void **)&fCapsManager);
        NS_DEFINE_IID(kICapsManagerIID, NS_ICAPSMANAGER_IID);
        if (   (err == NS_OK) 
               && (fCapsManager != NULL) 
               && (err = (fCapsManager->QueryInterface(kICapsManagerIID, (void**)&result)) == NS_OK)
            )
        {
            ((nsCCapsManager*)result)->SetSystemPrivilegeManager();
            result->Release();
        }
    }
    if (  (err == NS_OK) 
          &&(fCapsManager->QueryInterface(aIID, (void**)&result) != NS_OK)
        )
    {
        result = NULL;
    }
    if (threadAttached != NULL )
    {
        PR_DetachThread();
    }
#endif
    return result;
}

#ifdef OJI
nsILiveconnect*
nsPluginManager::GetLiveconnect(const nsIID& aIID)
{
    nsILiveconnect* result = NULL;
    PRThread       *threadAttached = NULL;
    nsresult        err    = NS_OK;
    if (fLiveconnect == NULL) {
	     if ( PR_GetCurrentThread() == NULL )
	     {
 		      threadAttached = PR_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL);
	     }
      NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
      err = nsComponentManager::CreateInstance(kCLiveconnectCID, 
                                         (nsIPluginManager*)this,    /* outer */
                                         kISupportsIID,
                                         (void **)&fLiveconnect);
     }
     if (  (err == NS_OK) 
         &&(fLiveconnect->QueryInterface(aIID, (void**)&result) != NS_OK)
        )
     {
       result = NULL;
     }
	    if (threadAttached != NULL )
     {
       PR_DetachThread();
     }
    return result;
}
#endif

#endif // PRE_SERVICE_MANAGER

NS_METHOD
nsPluginManager::BeginWaitCursor(void)
{
    if (fWaiting == 0) {
#ifdef XP_PC
        fOldCursor = (void*)GetCursor();
        HCURSOR newCursor = LoadCursor(NULL, IDC_WAIT);
        if (newCursor)
            SetCursor(newCursor);
#endif
#ifdef XP_MAC
        startAsyncCursors();
#endif
    }
    fWaiting++;
    return NS_OK;
}

NS_METHOD
nsPluginManager::EndWaitCursor(void)
{
    fWaiting--;
    if (fWaiting == 0) {
#ifdef XP_PC
        if (fOldCursor)
            SetCursor((HCURSOR)fOldCursor);
#endif
#ifdef XP_MAC
        stopAsyncCursors();
#endif
        fOldCursor = NULL;
    }
    return NS_OK;
}

NS_METHOD
nsPluginManager::SupportsURLProtocol(const char* protocol, PRBool *result)
{
    int type = NET_URL_Type(protocol);
    *result = (PRBool)(type != 0);
    return NS_OK;
}

NS_METHOD
nsPluginManager::NotifyStatusChange(nsIPlugin* plugin, nsresult errorStatus)
{
    // XXX need to shut down all instances of this plugin
    return NS_OK;
}

#if 0
static NPP getNPPFromHandler(nsIEventHandler* handler)
{
    NPP npp = NULL;
    nsIPluginInstance* pluginInst = NULL;
    if (handler->QueryInterface(kIPluginInstanceIID, (void**)&pluginInst) == NS_OK) {
        nsPluginInstancePeer* myPeer;
        nsresult err = pluginInst->GetPeer((nsIPluginInstancePeer**)&myPeer);
        PR_ASSERT(err == NS_OK);
        npp = myPeer->GetNPP();
        myPeer->Release();
        pluginInst->Release();
    }
    return npp;
}
#endif

NS_METHOD
nsPluginManager::RegisterWindow(nsIEventHandler* handler, nsPluginPlatformWindowRef window)
{
#if 1
    npn_registerwindow(handler, window);
    return NS_OK;
#else
    NPP npp = getNPPFromHandler(handler);
    if (npp != NULL) {
        npn_registerwindow(npp, window);
        return NS_OK;
    }
    return NS_ERROR_FAILURE;
#endif
}

NS_METHOD
nsPluginManager::UnregisterWindow(nsIEventHandler* handler, nsPluginPlatformWindowRef window)
{
#if 1
    npn_unregisterwindow(handler, window);
    return NS_OK;
#else
    NPP npp = getNPPFromHandler(handler);
    if (npp != NULL) {
        npn_unregisterwindow(npp, window);
        return NS_OK;
    }
    return NS_ERROR_FAILURE;
#endif
}

class nsEventHandlerKey : public nsHashKey {
public:
    nsEventHandlerKey(nsIEventHandler* handler) : mHandler(handler) {}

    virtual PRUint32 HashValue(void) const
    {
        return PRUint32(mHandler);
    }
	
    virtual PRBool Equals(const nsHashKey *aKey) const
    {
        return ((nsEventHandlerKey*)aKey)->mHandler == mHandler;
    }
	
    virtual nsHashKey *Clone(void) const
    {
        return new nsEventHandlerKey(mHandler);
    }

private:
    nsIEventHandler* mHandler;
};

NS_METHOD
nsPluginManager::AllocateMenuID(nsIEventHandler* handler, PRBool isSubmenu,
                                PRInt16 *result)
{
#ifdef XP_MAC
    PRInt16 menuID = LMenuSharingAttachment::AllocatePluginMenuID(isSubmenu);
    if (fAllocatedMenuIDs == NULL)
        fAllocatedMenuIDs  = new nsHashtable(16);
    if (fAllocatedMenuIDs != NULL) {
        nsEventHandlerKey key(handler);
        TArray<PRInt16>* menuIDs = (TArray<PRInt16>*) fAllocatedMenuIDs->Get(&key);
        if (menuIDs == NULL) {
            menuIDs = new TArray<PRInt16>;
            fAllocatedMenuIDs->Put(&key, menuIDs);
        }
        if (menuIDs != NULL)
            menuIDs->AddItem(menuID);
    }
    *result = menuID;
    return NS_OK;
#else
    return NS_ERROR_NOT_IMPLEMENTED;
#endif
}

NS_METHOD
nsPluginManager::DeallocateMenuID(nsIEventHandler* handler, PRInt16 menuID)
{
#ifdef XP_MAC
    if (fAllocatedMenuIDs != NULL) {
        nsEventHandlerKey key(handler);
        TArray<PRInt16>* menuIDs = (TArray<PRInt16>*) fAllocatedMenuIDs->Get(&key);
        if (menuIDs != NULL) {
            menuIDs->Remove(menuID);
            if (menuIDs->GetCount() == 0) {
                // let go of the vector and the hash table entry.
                fAllocatedMenuIDs->Remove(&key);
                delete menuIDs;
            }
            return NS_OK;
        }
    }
    return NS_ERROR_FAILURE;
#else
    return NS_ERROR_NOT_IMPLEMENTED;
#endif
}

NS_METHOD
nsPluginManager::HasAllocatedMenuID(nsIEventHandler* handler, PRInt16 menuID, PRBool *result)
{
#ifdef XP_MAC
    if (fAllocatedMenuIDs != NULL) {
        nsEventHandlerKey key(handler);
        TArray<PRInt16>* menuIDs = (TArray<PRInt16>*) fAllocatedMenuIDs->Get(&key);
        if (menuIDs != NULL) {
            TArray<PRInt16>& menus = *menuIDs;
            UInt32 count = menus.GetCount();
            for (UInt32 i = count; i > 0; --i) {
                if (menus[i] == menuID) {
                    *result = PR_TRUE;
                    return NS_OK;
                }
            }
        }
    }
    return PR_FALSE;
#else
    return NS_ERROR_NOT_IMPLEMENTED;
#endif
}

#if 0 // problematic
static NPL_ProcessNextEventProc npl_ProcessNextEventProc = NULL;
static void* npl_ProcessNextEventData = NULL;

PR_IMPLEMENT(void) 
NPL_InstallProcessNextEventProc(NPL_ProcessNextEventProc proc, void* data)
{
    npl_ProcessNextEventProc = proc;
    npl_ProcessNextEventData = data;
}

NS_METHOD
nsPluginManager::ProcessNextEvent(PRBool *bEventHandled)
{
#ifdef XP_MAC
    if (npl_ProcessNextEventProc != NULL)
        *bEventHandled = npl_ProcessNextEventProc(npl_ProcessNextEventData);
    return NS_OK;
#else 
    *bEventHandled = PR_FALSE;
    return NS_OK;
#endif
}
#endif

////////////////////////////////////////////////////////////////////////////////

#include "plevent.h"

extern "C" {
extern PREventQueue* mozilla_event_queue;
extern PRThread* mozilla_thread;
};

#ifdef NEW_PLUGIN_STREAM_API

struct GetURLEvent {
    PLEvent event;
    nsPluginInstancePeer* peer;
    const char* url;
    const char* target;
    void* notifyData;
    const char* altHost;
    const char* referrer;
    PRBool forceJSEnabled;
};

static void*
HandleGetURLEvent(PLEvent* event)
{
    PR_ASSERT(PR_CurrentThread() == mozilla_thread);
    GetURLEvent* e = (GetURLEvent*)event;
    NPP npp = e->peer->GetNPP();
    NPError rslt = np_geturlinternal(npp,
                                     e->url,
                                     e->target,
                                     e->altHost,
                                     e->referrer,
                                     e->forceJSEnabled,
                                     e->notifyData != NULL,
                                     e->notifyData);
    return (void*)rslt;
}

static void
DestroyGetURLEvent(PLEvent* event)
{
    GetURLEvent* e = (GetURLEvent*)event;
    PR_Free(event);
}

NS_METHOD
nsPluginManager::GetURL(nsISupports* pluginInst, 
                        const char* url, 
                        const char* target,
                        nsIPluginStreamListener* listener,
                        nsPluginStreamType streamType,
                        const char* altHost,
                        const char* referrer,
                        PRBool forceJSEnabled)
{
    void* notifyData = NULL;
    if (listener) {
        nsPluginInputStream* inStr = new nsPluginInputStream(listener, streamType);
        if (inStr == NULL) 
            return NS_ERROR_OUT_OF_MEMORY;
        inStr->AddRef();
        notifyData = inStr;
    }

    NPError rslt = NPERR_INVALID_PARAM;
    nsIPluginInstance* inst = NULL;
    if (pluginInst->QueryInterface(kIPluginInstanceIID, (void**)&inst) == NS_OK) {
        // Warning: Casting to our implementation type of plugin instance peer here:
        nsPluginInstancePeer* peer;
        nsresult err = inst->GetPeer((nsIPluginInstancePeer**)&peer);
        if (err == NS_OK) {
            if (PR_CurrentThread() == mozilla_thread) {
                NPP npp = peer->GetNPP();
                rslt = np_geturlinternal(npp,
                                         url,
                                         target,
                                         altHost,
                                         referrer,
                                         forceJSEnabled,
                                         notifyData != NULL,
                                         notifyData);
            }
            else {
                GetURLEvent* e = PR_NEW(GetURLEvent);
                if (e == NULL) {
                    rslt = NPERR_OUT_OF_MEMORY_ERROR;
                }
                else {
                    PL_InitEvent(&e->event, NULL, HandleGetURLEvent, DestroyGetURLEvent);
                    e->peer = peer;
                    e->url = url;
                    e->target = target;
                    e->notifyData = notifyData;
                    e->altHost = altHost;
                    e->referrer = referrer;
                    e->forceJSEnabled = forceJSEnabled;
                    /*rslt = (NPError)*/PL_PostSynchronousEvent(mozilla_event_queue, &e->event);
                    rslt = NPERR_NO_ERROR;  /* XXX irix c++ compiler doesn't like the above cast */
                }
            }
            peer->Release();
        }
        inst->Release();
    }
    return fromNPError[rslt];
}

struct PostURLEvent {
    PLEvent event;
    nsPluginInstancePeer* peer;
    const char* url;
    const char* target;
    PRUint32 postDataLen;
    const char* postData;
    PRBool isFile;
    void* notifyData;
    const char* altHost;
    const char* referrer;
    PRBool forceJSEnabled;
    PRUint32 postHeadersLen;
    const char* postHeaders;
};

static void*
HandlePostURLEvent(PLEvent* event)
{
    PR_ASSERT(PR_CurrentThread() == mozilla_thread);
    PostURLEvent* e = (PostURLEvent*)event;
    NPP npp = e->peer->GetNPP();
    NPError rslt = np_posturlinternal(npp,
                                      e->url, 
                                      e->target,
                                      e->altHost,
                                      e->referrer,
                                      e->forceJSEnabled,
                                      e->postDataLen,
                                      e->postData,
                                      e->isFile,
                                      e->notifyData != NULL,
                                      e->notifyData);
    return (void*)rslt;
}

static void
DestroyPostURLEvent(PLEvent* event)
{
    PostURLEvent* e = (PostURLEvent*)event;
    PR_Free(event);
}

NS_METHOD
nsPluginManager::PostURL(nsISupports* pluginInst,
                         const char* url,
                         PRUint32 postDataLen, 
                         const char* postData,
                         PRBool isFile,
                         const char* target,
                         nsIPluginStreamListener* listener,
                         nsPluginStreamType streamType,
                         const char* altHost, 
                         const char* referrer,
                         PRBool forceJSEnabled,
                         PRUint32 postHeadersLen, 
                         const char* postHeaders)
{
    void* notifyData = NULL;
    if (listener) {
        nsPluginInputStream* inStr = new nsPluginInputStream(listener, streamType);
        if (inStr == NULL) 
            return NS_ERROR_OUT_OF_MEMORY;
        inStr->AddRef();
        notifyData = inStr;
    }

    NPError rslt = NPERR_INVALID_PARAM;
    nsIPluginInstance* inst = NULL;
    if (pluginInst->QueryInterface(kIPluginInstanceIID, (void**)&inst) == NS_OK) {
        // Warning: Casting to our implementation type of plugin instance peer here:
        nsPluginInstancePeer* peer;
        nsresult err = inst->GetPeer((nsIPluginInstancePeer**)&peer);
        if (err == NS_OK) {
            if (PR_CurrentThread() == mozilla_thread) {
                NPP npp = peer->GetNPP();
                PR_ASSERT(postHeaders == NULL); // XXX need to deal with postHeaders
                rslt = np_posturlinternal(npp,
                                          url, 
                                          target,
                                          altHost,
                                          referrer,
                                          forceJSEnabled,
                                          postDataLen,
                                          postData,
                                          isFile,
                                          notifyData != NULL,
                                          notifyData);
            }
            else {
                PostURLEvent* e = PR_NEW(PostURLEvent);
                if (e == NULL) {
                    rslt = NPERR_OUT_OF_MEMORY_ERROR;
                }
                else {
                    PL_InitEvent(&e->event, NULL, HandlePostURLEvent, DestroyPostURLEvent);
                    e->peer = peer;
                    e->url = url;
                    e->target = target;
                    e->notifyData = notifyData;
                    e->altHost = altHost;
                    e->referrer = referrer;
                    e->forceJSEnabled = forceJSEnabled;
                    e->postDataLen = postDataLen;
                    e->postData = postData;
                    e->isFile = isFile;
                    e->postHeadersLen = postHeadersLen;
                    e->postHeaders = postHeaders;
                    /*rslt = (NPError)*/PL_PostSynchronousEvent(mozilla_event_queue, &e->event);
                    rslt = NPERR_NO_ERROR;  /* XXX irix c++ compiler doesn't like the above cast */
                }
            }
            peer->Release();
        }
        inst->Release();
    }
    return fromNPError[rslt];
}

#else // !NEW_PLUGIN_STREAM_API

struct GetURLEvent {
    PLEvent event;
    nsPluginInstancePeer* peer;
    const char* url;
    const char* target;
    void* notifyData;
    const char* altHost;
    const char* referrer;
    PRBool forceJSEnabled;
};

static void*
HandleGetURLEvent(PLEvent* event)
{
    PR_ASSERT(PR_CurrentThread() == mozilla_thread);
    GetURLEvent* e = (GetURLEvent*)event;
    NPP npp = e->peer->GetNPP();
    NPError rslt = np_geturlinternal(npp,
                                     e->url,
                                     e->target,
                                     e->altHost,
                                     e->referrer,
                                     e->forceJSEnabled,
                                     e->notifyData != NULL,
                                     e->notifyData);
    return (void*)rslt;
}

static void
DestroyGetURLEvent(PLEvent* event)
{
    GetURLEvent* e = (GetURLEvent*)event;
    PR_Free(event);
}

NS_METHOD
nsPluginManager::GetURL(nsISupports* pinst, const char* url, const char* target,
                        void* notifyData, const char* altHost,
                        const char* referrer, PRBool forceJSEnabled)
{
    NPError rslt = NPERR_INVALID_PARAM;
    nsIPluginInstance* inst = NULL;
    if (pinst->QueryInterface(kIPluginInstanceIID, (void**)&inst) == NS_OK) {
        // Warning: Casting to our implementation type of plugin instance peer here:
        nsPluginInstancePeer* peer;
        nsresult err = inst->GetPeer((nsIPluginInstancePeer**)&peer);
        if (err == NS_OK) {
            if (PR_CurrentThread() == mozilla_thread) {
                NPP npp = peer->GetNPP();
                rslt = np_geturlinternal(npp,
                                         url,
                                         target,
                                         altHost,
                                         referrer,
                                         forceJSEnabled,
                                         notifyData != NULL,
                                         notifyData);
            }
            else {
                GetURLEvent* e = PR_NEW(GetURLEvent);
                if (e == NULL) {
                    rslt = NPERR_OUT_OF_MEMORY_ERROR;
                }
                else {
                    PL_InitEvent(&e->event, NULL, HandleGetURLEvent, DestroyGetURLEvent);
                    e->peer = peer;
                    e->url = url;
                    e->target = target;
                    e->notifyData = notifyData;
                    e->altHost = altHost;
                    e->referrer = referrer;
                    e->forceJSEnabled = forceJSEnabled;
                    /*rslt = (NPError)*/PL_PostSynchronousEvent(mozilla_event_queue, &e->event);
                    rslt = NPERR_NO_ERROR;  /* XXX irix c++ compiler doesn't like the above cast */
                }
            }
            peer->Release();
        }
        inst->Release();
    }
    return fromNPError[rslt];
}

struct PostURLEvent {
    PLEvent event;
    nsPluginInstancePeer* peer;
    const char* url;
    const char* target;
    PRUint32 postDataLen;
    const char* postData;
    PRBool isFile;
    void* notifyData;
    const char* altHost;
    const char* referrer;
    PRBool forceJSEnabled;
    PRUint32 postHeadersLen;
    const char* postHeaders;
};

static void*
HandlePostURLEvent(PLEvent* event)
{
    PR_ASSERT(PR_CurrentThread() == mozilla_thread);
    PostURLEvent* e = (PostURLEvent*)event;
    NPP npp = e->peer->GetNPP();
    NPError rslt = np_posturlinternal(npp,
                                      e->url, 
                                      e->target,
                                      e->altHost,
                                      e->referrer,
                                      e->forceJSEnabled,
                                      e->postDataLen,
                                      e->postData,
                                      e->isFile,
                                      e->notifyData != NULL,
                                      e->notifyData);
    return (void*)rslt;
}

static void
DestroyPostURLEvent(PLEvent* event)
{
    PostURLEvent* e = (PostURLEvent*)event;
    PR_Free(event);
}

NS_METHOD
nsPluginManager::PostURL(nsISupports* pinst, const char* url, const char* target,
                         PRUint32 postDataLen, const char* postData,
                         PRBool isFile, void* notifyData,
                         const char* altHost, const char* referrer,
                         PRBool forceJSEnabled,
                         PRUint32 postHeadersLen, const char* postHeaders)
{
    NPError rslt = NPERR_INVALID_PARAM;
    nsIPluginInstance* inst = NULL;
    if (pinst->QueryInterface(kIPluginInstanceIID, (void**)&inst) == NS_OK) {
        // Warning: Casting to our implementation type of plugin instance peer here:
        nsPluginInstancePeer* peer;
        nsresult err = inst->GetPeer((nsIPluginInstancePeer**)&peer);
        if (err == NS_OK) {
            if (PR_CurrentThread() == mozilla_thread) {
                NPP npp = peer->GetNPP();
                PR_ASSERT(postHeaders == NULL); // XXX need to deal with postHeaders
                rslt = np_posturlinternal(npp,
                                          url, 
                                          target,
                                          altHost,
                                          referrer,
                                          forceJSEnabled,
                                          postDataLen,
                                          postData,
                                          isFile,
                                          notifyData != NULL,
                                          notifyData);
            }
            else {
                PostURLEvent* e = PR_NEW(PostURLEvent);
                if (e == NULL) {
                    rslt = NPERR_OUT_OF_MEMORY_ERROR;
                }
                else {
                    PL_InitEvent(&e->event, NULL, HandlePostURLEvent, DestroyPostURLEvent);
                    e->peer = peer;
                    e->url = url;
                    e->target = target;
                    e->notifyData = notifyData;
                    e->altHost = altHost;
                    e->referrer = referrer;
                    e->forceJSEnabled = forceJSEnabled;
                    e->postDataLen = postDataLen;
                    e->postData = postData;
                    e->isFile = isFile;
                    e->postHeadersLen = postHeadersLen;
                    e->postHeaders = postHeaders;
                    /*rslt = (NPError)*/PL_PostSynchronousEvent(mozilla_event_queue, &e->event);
                    rslt = NPERR_NO_ERROR;  /* XXX irix c++ compiler doesn't like the above cast */
                }
            }
            peer->Release();
        }
        inst->Release();
    }
    return fromNPError[rslt];
}

#endif // !NEW_PLUGIN_STREAM_API

extern "C" char *pacf_find_proxies_for_url(MWContext *context, 
                                           URL_Struct *URL_s);

NS_METHOD
nsPluginManager::FindProxyForURL(const char* url, char* *result)
{
    // Warning: Looking at the code in mkautocf.c, the context can
    // (fortunately) be NULL.
    URL_Struct* urls = NET_CreateURLStruct(url, NET_DONT_RELOAD);
    if (urls == NULL)
        return NS_ERROR_OUT_OF_MEMORY;
    *result = pacf_find_proxies_for_url(NULL, urls);
    NET_FreeURLStruct(urls);
    return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////

#include "nsMalloc.h"
#include "nsFileUtilities.h"

class nsPluginFactory : public nsIFactory {
public:
    NS_DECL_ISUPPORTS

    NS_IMETHOD CreateInstance(nsISupports *aOuter,
                              REFNSIID aIID,
                              void **aResult);

    NS_IMETHOD LockFactory(PRBool aLock);

    nsPluginFactory(void);
    virtual ~nsPluginFactory(void);
};

static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
NS_IMPL_ISUPPORTS(nsPluginFactory, kIFactoryIID);

nsPluginFactory::nsPluginFactory(void)
{
}

nsPluginFactory::~nsPluginFactory(void)
{
}

NS_METHOD
nsPluginFactory::CreateInstance(nsISupports *aOuter,
                                REFNSIID aIID,
                                void **aResult)
{
    nsresult err;
    err = nsPluginManager::Create(aOuter, aIID, aResult);
    if (err == NS_OK) return err;

#if 0   // Browser interface should be JNI from now on. Hence all new code
        // should be written using JNI. sudu.
    if (aIID.Equals(kIJRIEnvIID)) {
        // XXX Need to implement ISupports for JRIEnv
        *aResult = (void*) ((nsISupports*)npn_getJavaEnv()); 
//        AddRef();     // XXX should the plugin instance peer and the env be linked?
        return NS_OK; 
    } 
#endif

#ifdef OJI

    if (aIID.Equals(kIJNIEnvIID)) {
        // XXX Need to implement ISupports for JNIEnv
        *aResult = (void*) ((nsISupports*)npn_getJavaEnv());       //=-= Fix this to return a Interface XXX need JNI version
//        AddRef();     // XXX should the plugin instance peer and the env be linked?
        return NS_OK; 
    }

#endif

    err = nsMalloc::Create(aOuter, aIID, aResult);
    if (err == NS_OK) return err;

    err = nsFileUtilities::Create(aOuter, aIID, aResult);
    if (err == NS_OK) return err;

    return NS_NOINTERFACE;
}

NS_METHOD
nsPluginFactory::LockFactory(PRBool aLock)
{
    // nothing to do here since we're not a DLL (yet)
    return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// Bogus stuff that will go away when
// a) we have a persistent registry for CLSID -> Factory mappings, and
// b) when we ship a browser where all this Plugin management code is in a DLL.

static NS_DEFINE_CID(kPluginManagerCID, NS_PLUGINMANAGER_CID);
static NS_DEFINE_CID(kJNIEnvCID, NS_JNIENV_CID); 
#if 0
static NS_DEFINE_CID(kJRIEnvCID, NS_JRIENV_CID); 
#endif
static NS_DEFINE_CID(kMallocCID, NS_MALLOC_CID);
static NS_DEFINE_CID(kFileUtilitiesCID, NS_FILEUTILITIES_CID);

extern "C" nsresult
np_RegisterPluginMgr(void)
{
    nsPluginFactory* pluginFact = new nsPluginFactory();
    if (pluginFact == NULL)
        return NS_ERROR_OUT_OF_MEMORY;

    pluginFact->AddRef();
    nsComponentManager::RegisterFactory(kPluginManagerCID,    NULL, NULL, pluginFact, PR_TRUE);

    pluginFact->AddRef();
    nsComponentManager::RegisterFactory(kJNIEnvCID,           NULL, NULL, pluginFact, PR_TRUE);

#if 0
    pluginFact->AddRef();
    nsComponentManager::RegisterFactory(kJRIEnvCID,           NULL, NULL, pluginFact, PR_TRUE);
#endif

    pluginFact->AddRef();
    nsComponentManager::RegisterFactory(kMallocCID,           NULL, NULL, pluginFact, PR_TRUE);

    pluginFact->AddRef();
    nsComponentManager::RegisterFactory(kFileUtilitiesCID,    NULL, NULL, pluginFact, PR_TRUE);

    return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
