/*
 * Copyright 2010 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdarg.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
#include "shlobj.h"

#include "mshtml_private.h"
#include "pluginhost.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(mshtml);

/* Parts of npapi.h */

#define NP_VERSION_MAJOR 0
#define NP_VERSION_MINOR 25

typedef unsigned char NPBool;
typedef INT16 NPError;
typedef INT16 NPReason;
typedef char *NPMIMEType;

typedef struct _NPP {
    void *pdata;
    void *ndata;
} NPP_t, *NPP;

typedef struct _NPStream {
    void *pdata;
    void *ndata;
    const char *url;
    UINT32 end;
    UINT32 lastmodified;
    void *notifyData;
    const char *headers;
} NPStream;

typedef struct _NPSavedData {
    INT32 len;
    void *buf;
} NPSavedData;

typedef struct _NPRect {
    UINT16 top;
    UINT16 left;
    UINT16 bottom;
    UINT16 right;
} NPRect;

typedef enum {
    NPFocusNext = 0,
    NPFocusPrevious = 1
} NPFocusDirection;

#define NP_ABI_MASK 0

typedef enum {
    NPPVpluginNameString = 1,
    NPPVpluginDescriptionString,
    NPPVpluginWindowBool,
    NPPVpluginTransparentBool,
    NPPVjavaClass,
    NPPVpluginWindowSize,
    NPPVpluginTimerInterval,
    NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
    NPPVpluginScriptableIID = 11,
    NPPVjavascriptPushCallerBool = 12,
    NPPVpluginKeepLibraryInMemory = 13,
    NPPVpluginNeedsXEmbed = 14,
    NPPVpluginScriptableNPObject = 15,
    NPPVformValue = 16,
    NPPVpluginUrlRequestsDisplayedBool = 17,
    NPPVpluginWantsAllNetworkStreams = 18,
    NPPVpluginNativeAccessibleAtkPlugId = 19,
    NPPVpluginCancelSrcStream = 20,
    NPPVSupportsAdvancedKeyHandling = 21
} NPPVariable;

typedef enum {
    NPNVxDisplay = 1,
    NPNVxtAppContext,
    NPNVnetscapeWindow,
    NPNVjavascriptEnabledBool,
    NPNVasdEnabledBool,
    NPNVisOfflineBool,
    NPNVserviceManager = (10 | NP_ABI_MASK),
    NPNVDOMElement     = (11 | NP_ABI_MASK),
    NPNVDOMWindow      = (12 | NP_ABI_MASK),
    NPNVToolkit        = (13 | NP_ABI_MASK),
    NPNVSupportsXEmbedBool = 14,
    NPNVWindowNPObject = 15,
    NPNVPluginElementNPObject = 16,
    NPNVSupportsWindowless = 17,
    NPNVprivateModeBool = 18,
    NPNVsupportsAdvancedKeyHandling = 21
} NPNVariable;

typedef enum {
    NPWindowTypeWindow = 1,
    NPWindowTypeDrawable
} NPWindowType;

typedef struct _NPWindow {
    void *window;
    INT32 x;
    INT32 y;
    UINT32 width;
    UINT32 height;
    NPRect clipRect;
    NPWindowType type;
} NPWindow;

typedef struct _NPFullPrint {
    NPBool pluginPrinted;
    NPBool printOne;
    void *platformPrint;
} NPFullPrint;

typedef struct _NPEmbedPrint {
    NPWindow window;
    void *platformPrint;
} NPEmbedPrint;

typedef struct _NPPrint {
    UINT16 mode;
    union {
        NPFullPrint fullPrint;
        NPEmbedPrint embedPrint;
    } print;
} NPPrint;

typedef HRGN NPRegion;

#define NPERR_BASE                         0
#define NPERR_NO_ERROR                    (NPERR_BASE + 0)
#define NPERR_GENERIC_ERROR               (NPERR_BASE + 1)
#define NPERR_INVALID_INSTANCE_ERROR      (NPERR_BASE + 2)
#define NPERR_INVALID_FUNCTABLE_ERROR     (NPERR_BASE + 3)
#define NPERR_MODULE_LOAD_FAILED_ERROR    (NPERR_BASE + 4)
#define NPERR_OUT_OF_MEMORY_ERROR         (NPERR_BASE + 5)
#define NPERR_INVALID_PLUGIN_ERROR        (NPERR_BASE + 6)
#define NPERR_INVALID_PLUGIN_DIR_ERROR    (NPERR_BASE + 7)
#define NPERR_INCOMPATIBLE_VERSION_ERROR  (NPERR_BASE + 8)
#define NPERR_INVALID_PARAM               (NPERR_BASE + 9)
#define NPERR_INVALID_URL                 (NPERR_BASE + 10)
#define NPERR_FILE_NOT_FOUND              (NPERR_BASE + 11)
#define NPERR_NO_DATA                     (NPERR_BASE + 12)
#define NPERR_STREAM_NOT_SEEKABLE         (NPERR_BASE + 13)

/* Parts of npfunctions.h */

typedef NPError (CDECL *NPP_NewProcPtr)(NPMIMEType,NPP,UINT16,INT16,char**,char**,NPSavedData*);
typedef NPError (CDECL *NPP_DestroyProcPtr)(NPP,NPSavedData**);
typedef NPError (CDECL *NPP_SetWindowProcPtr)(NPP,NPWindow*);
typedef NPError (CDECL *NPP_NewStreamProcPtr)(NPP,NPMIMEType,NPStream*,NPBool,UINT16*);
typedef NPError (CDECL *NPP_DestroyStreamProcPtr)(NPP,NPStream*,NPReason);
typedef INT32 (CDECL *NPP_WriteReadyProcPtr)(NPP,NPStream*);
typedef INT32 (CDECL *NPP_WriteProcPtr)(NPP,NPStream*,INT32,INT32,void*);
typedef void (CDECL *NPP_StreamAsFileProcPtr)(NPP,NPStream*,const char*);
typedef void (CDECL *NPP_PrintProcPtr)(NPP,NPPrint*);
typedef INT16 (CDECL *NPP_HandleEventProcPtr)(NPP,void*);
typedef void (CDECL *NPP_URLNotifyProcPtr)(NPP,const char*,NPReason,void*);
typedef NPError (CDECL *NPP_GetValueProcPtr)(NPP,NPPVariable,void*);
typedef NPError (CDECL *NPP_SetValueProcPtr)(NPP,NPNVariable,void*);
typedef NPBool (CDECL *NPP_GotFocusPtr)(NPP,NPFocusDirection);
typedef void (CDECL *NPP_LostFocusPtr)(NPP);

typedef struct _NPPluginFuncs {
    UINT16 size;
    UINT16 version;
    NPP_NewProcPtr newp;
    NPP_DestroyProcPtr destroy;
    NPP_SetWindowProcPtr setwindow;
    NPP_NewStreamProcPtr newstream;
    NPP_DestroyStreamProcPtr destroystream;
    NPP_StreamAsFileProcPtr asfile;
    NPP_WriteReadyProcPtr writeready;
    NPP_WriteProcPtr write;
    NPP_PrintProcPtr print;
    NPP_HandleEventProcPtr event;
    NPP_URLNotifyProcPtr urlnotify;
    void *javaClass;
    NPP_GetValueProcPtr getvalue;
    NPP_SetValueProcPtr setvalue;
    NPP_GotFocusPtr gotfocus;
    NPP_LostFocusPtr lostfocus;
} NPPluginFuncs;

static nsIDOMElement *get_dom_element(NPP instance)
{
    nsISupports *instance_unk = (nsISupports*)instance->ndata;
    nsIPluginInstance *plugin_instance;
    nsIDOMElement *elem;
    nsresult nsres;

    nsres = nsISupports_QueryInterface(instance_unk, &IID_nsIPluginInstance, (void**)&plugin_instance);
    if(NS_FAILED(nsres)) {
        ERR("Could not get nsIPluginInstance interface: %08x\n", nsres);
        return NULL;
    }

    nsres = nsIPluginInstance_GetDOMElement(plugin_instance, &elem);
    nsIPluginInstance_Release(plugin_instance);
    if(NS_FAILED(nsres)) {
        ERR("GetDOMElement failed: %08x\n", nsres);
        return NULL;
    }

    return elem;
}

static HTMLInnerWindow *get_elem_window(nsIDOMElement *elem)
{
    nsIDOMWindow *nswindow;
    nsIDOMDocument *nsdoc;
    HTMLOuterWindow *window;
    nsresult nsres;

    nsres = nsIDOMElement_GetOwnerDocument(elem, &nsdoc);
    if(NS_FAILED(nsres))
        return NULL;

    nsres = nsIDOMDocument_GetDefaultView(nsdoc, &nswindow);
    nsIDOMDocument_Release(nsdoc);
    if(NS_FAILED(nsres) || !nswindow)
        return NULL;

    window = nswindow_to_window(nswindow);
    nsIDOMWindow_Release(nswindow);

    return window->base.inner_window;
}

static BOOL parse_classid(const PRUnichar *classid, CLSID *clsid)
{
    const WCHAR *ptr;
    unsigned len;
    HRESULT hres;

    static const PRUnichar clsidW[] = {'c','l','s','i','d',':'};

    if(strncmpiW(classid, clsidW, sizeof(clsidW)/sizeof(WCHAR)))
        return FALSE;

    ptr = classid + sizeof(clsidW)/sizeof(WCHAR);
    len = strlenW(ptr);

    if(len == 38) {
        hres = CLSIDFromString(ptr, clsid);
    }else if(len == 36) {
        WCHAR buf[39];

        buf[0] = '{';
        memcpy(buf+1, ptr, len*sizeof(WCHAR));
        buf[37] = '}';
        buf[38] = 0;
        hres = CLSIDFromString(buf, clsid);
    }else {
        return FALSE;
    }

    return SUCCEEDED(hres);
}

static BOOL get_elem_clsid(nsIDOMElement *elem, CLSID *clsid)
{
    nsAString attr_str, val_str;
    nsresult nsres;
    BOOL ret = FALSE;

    static const PRUnichar classidW[] = {'c','l','a','s','s','i','d',0};

    nsAString_InitDepend(&attr_str, classidW);
    nsAString_Init(&val_str, NULL);
    nsres = nsIDOMElement_GetAttribute(elem, &attr_str, &val_str);
    nsAString_Finish(&attr_str);
    if(NS_SUCCEEDED(nsres)) {
        const PRUnichar *val;

        nsAString_GetData(&val_str, &val);
        if(*val)
            ret = parse_classid(val, clsid);
    }else {
        ERR("GetAttribute failed: %08x\n", nsres);
    }

    nsAString_Finish(&attr_str);
    return ret;
}

typedef struct {
    IBindStatusCallback IBindStatusCallback_iface;
    IWindowForBindingUI IWindowForBindingUI_iface;
    LONG ref;
} InstallCallback;

static inline InstallCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
{
    return CONTAINING_RECORD(iface, InstallCallback, IBindStatusCallback_iface);
}

static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface,
        REFIID riid, void **ppv)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = &This->IBindStatusCallback_iface;
    }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
        TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv);
        *ppv = &This->IBindStatusCallback_iface;
    }else if(IsEqualGUID(&IID_IWindowForBindingUI, riid)) {
        TRACE("(%p)->(IID_IWindowForBindingUI %p)\n", This, ppv);
        *ppv = &This->IWindowForBindingUI_iface;
    }else {
        TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if(!ref)
        heap_free(This);

    return ref;
}

static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface,
        DWORD dwReserved, IBinding *pib)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    TRACE("(%p)->(%x %p)\n", This, dwReserved, pib);
    return S_OK;
}

static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    TRACE("(%p)->(%p)\n", This, pnPriority);
    return E_NOTIMPL;
}

static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface, DWORD dwReserved)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    TRACE("(%p)->(%x)\n", This, dwReserved);
    return S_OK;
}

static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
        ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    TRACE("(%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, debugstr_w(szStatusText));
    return S_OK;
}

static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface,
        HRESULT hresult, LPCWSTR szError)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
    return S_OK;
}

static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface,
        DWORD* grfBINDF, BINDINFO* pbindinfo)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);

    TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);

    *grfBINDF = BINDF_ASYNCHRONOUS;
    return S_OK;
}

static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
        DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    ERR("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface,
        REFIID riid, IUnknown* punk)
{
    InstallCallback *This = impl_from_IBindStatusCallback(iface);
    ERR("(%p)\n", This);
    return E_NOTIMPL;
}

static IBindStatusCallbackVtbl InstallCallbackVtbl = {
    InstallCallback_QueryInterface,
    InstallCallback_AddRef,
    InstallCallback_Release,
    InstallCallback_OnStartBinding,
    InstallCallback_GetPriority,
    InstallCallback_OnLowResource,
    InstallCallback_OnProgress,
    InstallCallback_OnStopBinding,
    InstallCallback_GetBindInfo,
    InstallCallback_OnDataAvailable,
    InstallCallback_OnObjectAvailable
};

static inline InstallCallback *impl_from_IWindowForBindingUI(IWindowForBindingUI *iface)
{
    return CONTAINING_RECORD(iface, InstallCallback, IWindowForBindingUI_iface);
}

static HRESULT WINAPI WindowForBindingUI_QueryInterface(IWindowForBindingUI *iface, REFIID riid, void **ppv)
{
    InstallCallback *This = impl_from_IWindowForBindingUI(iface);
    return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv);
}

static ULONG WINAPI WindowForBindingUI_AddRef(IWindowForBindingUI *iface)
{
    InstallCallback *This = impl_from_IWindowForBindingUI(iface);
    return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
}

static ULONG WINAPI WindowForBindingUI_Release(IWindowForBindingUI *iface)
{
    InstallCallback *This = impl_from_IWindowForBindingUI(iface);
    return IBindStatusCallback_Release(&This->IBindStatusCallback_iface);
}

static HRESULT WINAPI WindowForBindingUI_GetWindow(IWindowForBindingUI *iface, REFGUID rguidReason, HWND *phwnd)
{
    InstallCallback *This = impl_from_IWindowForBindingUI(iface);
    FIXME("(%p)->(%s %p)\n", This, debugstr_guid(rguidReason), phwnd);
    *phwnd = NULL;
    return S_OK;
}

static const IWindowForBindingUIVtbl WindowForBindingUIVtbl = {
    WindowForBindingUI_QueryInterface,
    WindowForBindingUI_AddRef,
    WindowForBindingUI_Release,
    WindowForBindingUI_GetWindow
};

typedef struct {
    struct list entry;
    IUri *uri;
} install_entry_t;

static struct list install_list = LIST_INIT(install_list);

static CRITICAL_SECTION cs_install_list;
static CRITICAL_SECTION_DEBUG cs_install_list_dbg =
{
    0, 0, &cs_install_list,
    { &cs_install_list_dbg.ProcessLocksList, &cs_install_list_dbg.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": install_list") }
};
static CRITICAL_SECTION cs_install_list = { &cs_install_list_dbg, -1, 0, 0, 0, 0 };

static void install_codebase(const WCHAR *url)
{
    InstallCallback *callback;
    IBindCtx *bctx;
    HRESULT hres;

    callback = heap_alloc(sizeof(*callback));
    if(!callback)
        return;

    callback->IBindStatusCallback_iface.lpVtbl = &InstallCallbackVtbl;
    callback->IWindowForBindingUI_iface.lpVtbl = &WindowForBindingUIVtbl;
    callback->ref = 1;

    hres = CreateAsyncBindCtx(0, &callback->IBindStatusCallback_iface, NULL, &bctx);
    IBindStatusCallback_Release(&callback->IBindStatusCallback_iface);
    if(FAILED(hres))
        return;

    hres = AsyncInstallDistributionUnit(NULL, NULL, NULL, 0, 0, url, bctx, NULL, 0);
    IBindCtx_Release(bctx);
    if(FAILED(hres))
        WARN("FAILED: %08x\n", hres);
}

static void check_codebase(HTMLInnerWindow *window, nsIDOMElement *nselem)
{
    nsAString attr_str, val_str;
    BOOL is_on_list = FALSE;
    install_entry_t *iter;
    IUri *uri = NULL;
    nsresult nsres;
    HRESULT hres;

    static const PRUnichar codebaseW[] = {'c','o','d','e','b','a','s','e',0};

    nsAString_InitDepend(&attr_str, codebaseW);
    nsAString_Init(&val_str, NULL);
    nsres = nsIDOMElement_GetAttribute(nselem, &attr_str, &val_str);
    nsAString_Finish(&attr_str);
    if(NS_SUCCEEDED(nsres)) {
        const PRUnichar *val;

        nsAString_GetData(&val_str, &val);
        if(*val) {
            hres = CoInternetCombineUrlEx(window->base.outer_window->uri, val, 0, &uri, 0);
            if(FAILED(hres))
                uri = NULL;
        }
    }else {
        ERR("GetAttribute failed: %08x\n", nsres);
    }

    nsAString_Finish(&attr_str);
    if(!uri)
        return;

    EnterCriticalSection(&cs_install_list);

    LIST_FOR_EACH_ENTRY(iter, &install_list, install_entry_t, entry) {
        BOOL eq;

        hres = IUri_IsEqual(uri, iter->uri, &eq);
        if(SUCCEEDED(hres) && eq) {
            TRACE("already proceeded\n");
            is_on_list = TRUE;
            break;
        }
    }

    if(!is_on_list) {
        iter = heap_alloc(sizeof(*iter));
        if(iter) {
            IUri_AddRef(uri);
            iter->uri = uri;

            list_add_tail(&install_list, &iter->entry);
        }
    }

    LeaveCriticalSection(&cs_install_list);

    if(!is_on_list) {
        BSTR display_uri;

        hres = IUri_GetDisplayUri(uri, &display_uri);
        if(SUCCEEDED(hres)) {
            install_codebase(display_uri);
            SysFreeString(display_uri);
        }
    }

    IUri_Release(uri);
}

static IUnknown *create_activex_object(HTMLInnerWindow *window, nsIDOMElement *nselem, CLSID *clsid)
{
    IClassFactoryEx *cfex;
    IClassFactory *cf;
    IUnknown *obj;
    DWORD policy;
    HRESULT hres;

    if(!get_elem_clsid(nselem, clsid)) {
        WARN("Could not determine element CLSID\n");
        return NULL;
    }

    TRACE("clsid %s\n", debugstr_guid(clsid));

    policy = 0;
    hres = IInternetHostSecurityManager_ProcessUrlAction(&window->doc->IInternetHostSecurityManager_iface,
            URLACTION_ACTIVEX_RUN, (BYTE*)&policy, sizeof(policy), (BYTE*)clsid, sizeof(GUID), 0, 0);
    if(FAILED(hres) || policy != URLPOLICY_ALLOW) {
        WARN("ProcessUrlAction returned %08x %x\n", hres, policy);
        return NULL;
    }

    hres = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER, NULL, &IID_IClassFactory, (void**)&cf);
    if(hres == REGDB_E_CLASSNOTREG)
        check_codebase(window, nselem);
    if(FAILED(hres))
        return NULL;

    hres = IClassFactory_QueryInterface(cf, &IID_IClassFactoryEx, (void**)&cfex);
    if(SUCCEEDED(hres)) {
        FIXME("Use IClassFactoryEx\n");
        IClassFactoryEx_Release(cfex);
    }

    hres = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void**)&obj);
    if(FAILED(hres))
        return NULL;

    return obj;
}

static NPError CDECL NPP_New(NPMIMEType pluginType, NPP instance, UINT16 mode, INT16 argc, char **argn,
        char **argv, NPSavedData *saved)
{
    nsIDOMElement *nselem;
    HTMLInnerWindow *window;
    IUnknown *obj;
    CLSID clsid;
    NPError err = NPERR_NO_ERROR;

    TRACE("(%s %p %x %d %p %p %p)\n", debugstr_a(pluginType), instance, mode, argc, argn, argv, saved);

    nselem = get_dom_element(instance);
    if(!nselem) {
        ERR("Could not get DOM element\n");
        return NPERR_GENERIC_ERROR;
    }

    window = get_elem_window(nselem);
    if(!window) {
        ERR("Could not get element's window object\n");
        nsIDOMElement_Release(nselem);
        return NPERR_GENERIC_ERROR;
    }

    obj = create_activex_object(window, nselem, &clsid);
    if(obj) {
        PluginHost *host;
        HRESULT hres;

        hres = create_plugin_host(window->doc, nselem, obj, &clsid, &host);
        nsIDOMElement_Release(nselem);
        IUnknown_Release(obj);
        if(SUCCEEDED(hres))
            instance->pdata = host;
        else
            err = NPERR_GENERIC_ERROR;
    }else {
        err = NPERR_GENERIC_ERROR;
    }

    nsIDOMElement_Release(nselem);
    return err;
}

static NPError CDECL NPP_Destroy(NPP instance, NPSavedData **save)
{
    PluginHost *host = instance->pdata;

    TRACE("(%p %p)\n", instance, save);

    if(!host)
        return NPERR_GENERIC_ERROR;

    detach_plugin_host(host);
    IOleClientSite_Release(&host->IOleClientSite_iface);
    instance->pdata = NULL;
    return NPERR_NO_ERROR;
}

static NPError CDECL NPP_SetWindow(NPP instance, NPWindow *window)
{
    PluginHost *host = instance->pdata;
    RECT pos_rect = {0, 0, window->width, window->height};

    TRACE("(%p %p)\n", instance, window);

    if(!host)
        return NPERR_GENERIC_ERROR;

    update_plugin_window(host, window->window, &pos_rect);
    return NPERR_NO_ERROR;
}

static NPError CDECL NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable, UINT16 *stype)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static NPError CDECL NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static INT32 CDECL NPP_WriteReady(NPP instance, NPStream *stream)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static INT32 CDECL NPP_Write(NPP instance, NPStream *stream, INT32 offset, INT32 len, void *buffer)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static void CDECL NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname)
{
    TRACE("\n");
}

static void CDECL NPP_Print(NPP instance, NPPrint *platformPrint)
{
    FIXME("\n");
}

static INT16 CDECL NPP_HandleEvent(NPP instance, void *event)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static void CDECL NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData)
{
    TRACE("\n");
}

static NPError CDECL NPP_GetValue(NPP instance, NPPVariable variable, void *ret_value)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static NPError CDECL NPP_SetValue(NPP instance, NPNVariable variable, void *value)
{
    TRACE("\n");
    return NPERR_GENERIC_ERROR;
}

static NPBool CDECL NPP_GotFocus(NPP instance, NPFocusDirection direction)
{
    FIXME("\n");
    return NPERR_GENERIC_ERROR;
}

static void CDECL NPP_LostFocus(NPP instance)
{
    FIXME("\n");
}

/***********************************************************************
 *          NP_GetEntryPoints (mshtml.@)
 */
NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* funcs)
{
    TRACE("(%p)\n", funcs);

    funcs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
    funcs->newp = NPP_New;
    funcs->destroy = NPP_Destroy;
    funcs->setwindow = NPP_SetWindow;
    funcs->newstream = NPP_NewStream;
    funcs->destroystream = NPP_DestroyStream;
    funcs->asfile = NPP_StreamAsFile;
    funcs->writeready = NPP_WriteReady;
    funcs->write = NPP_Write;
    funcs->print = NPP_Print;
    funcs->event = NPP_HandleEvent;
    funcs->urlnotify = NPP_URLNotify;
    funcs->javaClass = NULL;
    funcs->getvalue = NPP_GetValue;
    funcs->setvalue = NPP_SetValue;
    funcs->gotfocus = NPP_GotFocus;
    funcs->lostfocus = NPP_LostFocus;

    return NPERR_NO_ERROR;
}
