Search code examples
javascripthtmlcpluginsnpapi

NPAPI plugin, removeChild from web page crashes Firefox4 & Chrome


I have a html page that opens a new window and loads a basic NPAPI written plugin that I opens a Wide Save dialog and then calls NPN_GetURL with a java-script function to close the window. Each time Firefox crashes and Chrome displays the plugin crashed. I'm not sure if the problem is HTML & JS or the plugin. I didn't find a whole lot when googling for other similar issues. Below is my page that loads the plugin and the javascript function "removePlugin" is called by the plugin. As for firefox no problems in 3.6 only in version 4

----Start

<html>
 <script language="javascript">
  function removePlugin()
   {
   var plgn = document.getElementById("myplugin1");
   document.getElementById("div1").removeChild(plgn);
   setTimeout('doClose()', 2000);
   }
 function doClose()
  {
  window.close();
  }
 </script>
 <body >
  <div id="div1">
   <embed type="application/x-My-Plugin" id="myplugin1"></embed>
  </div>
 </body>
</html>

----End

----Start of Plugin code

#include "npapi.h"

//Prottype
LRESULT CALLBACK    PluginWindowProc  (HWND,UINT,WPARAM,LPARAM);

//Define
#define MY_MESSAGE      WM_USER + 1000

//Global
const char*     gInstanceLookupString = "instance";
WNDPROC         fDefaultWindowProc;

//Function:    NPP_SetWindow
NPError NPP_SetWindow(NPP       instance,
                      NPWindow *window)
{
    fDefaultWindowProc = (WNDPROC)SetWindowLongPtr((HWND)window->window,
                                                   GWL_WNDPROC,
                                                   (LONG)PluginWindowProc);
    SetProp(window->window,
            gInstanceLookupString,
            (HANDLE)instance);
    SendMessage(window->window,
                MY_MESSAGE,
                0,
                0);
    return NPERR_NO_ERROR;
}

//PluginWindowProc
LRESULT CALLBACK PluginWindowProc(HWND      hWnd,
                                  UINT      Msg,
                                  WPARAM    wParam,
                                  LPARAM    lParam)
{
OPENFILENAMEW       ofn;                                                                    //Fix.3a
WCHAR               szFile[512];                                //File name that appears in the Save Dialog box //Fix.3a
NPP                 instance;

    switch(Msg)
        {
        case MY_MESSAGE:
            instance = (NPP)GetProp(hWnd,
                                    gInstanceLookupString);
            //SAVE----------------------------------------
            memset(&ofn,
                   0x00,
                   sizeof(OPENFILENAMEW));

            memset(szFile,
                   0x00,
                   sizeof(szFile));
            _snwprintf_s(szFile,                                
                        _countof(szFile),
                        _countof(szFile),
                        L"Test.Txt");
            ofn.lStructSize = sizeof(OPENFILENAME);            
            ofn.lpstrFile = szFile;
            ofn.nMaxFile = _countof(szFile);
            ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;

            GetSaveFileNameW(&ofn);


            //CLOSE---------------------------------------
            NPN_GetURL(instance,
                       "javascript:removePlugin(0);",
                       "_self");

            //--------------------------------------------
            break;
        default:
            CallWindowProc(fDefaultWindowProc,
                           hWnd,
                           Msg,
                           wParam,
                           lParam);
            break;
        }
    return 0;
    }

//Function:    NPP_Destroy
NPError NPP_Destroy(NPP instance,NPSavedData **save){return NPERR_NO_ERROR;}

//Function:    NPP_DestroyStream
NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError   reason){return NPERR_NO_ERROR;}

//Function:    NPP_HandleEvent
int16 NPP_HandleEvent(NPP instance,void* event){return 0;}

//Function:    NPP_Initialize
NPError NPP_Initialize(void){return NPERR_NO_ERROR;}

//Function:    NPP_New
NPError NPP_New(NPMIMEType pluginType,NPP instance, uint16 mode, int16 argc,char* argn[], char * argv[],NPSavedData* saved){return (NPERR_NO_ERROR);}

//Function: NPP_NewStream
NPError NPP_NewStream(NPP instance,NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype){ return NPERR_NO_ERROR;}

//Function: NPP_Print
void NPP_Print(NPP instance, NPPrint *printInfo){}

//Function: NPP_Shutdown
void NPP_Shutdown(void){}

//Function: NPP_StreamAsFile
void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname){}

//Function: NPP_URLNotify
void NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData){}

//Function: NPP_Write
int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)        {return 0;}

//Function: NPP_WriteReady
int32 NPP_WriteReady(NPP instance, NPStream *stream){return 0;}

----End of Plugin code


Solution

  • It is really difficult to tell for sure what is going on, but I can tell you that the approach you're taking has several other problems. The first and foremost is that you must not ever block the main browser thread, which is what happens when you open a dialog in the main browser. Now, I realize that you might be okay (and some might even be under the delusion that others would be okay) with dialog locking up (no, not just preventing input, but actually locking up) the browser while your dialog is open, but in the cases of all current browsers that run the plugin out of process and wouldn't completely lock up while the dialog box is open the browser is liable to at the very least ask the user if they want to kill the plugin, if not just kill your plugin outright like Safari 5.1 would.

    What you really need to do if you're going to use a dialog is start another thread and put the dialog call on that, then call back into the browser (on the main thread! use NPN_PluginThreadAsyncCall) when you're done.

    As to the crash log, from what it is saying you've somehow borked the browser's handle to the plugin, because it's crashing when it tries to call SetWindow on your plugin. You might want to consider tossing this barebones attempt at NPAPI and use something like FireBreath, where the tricky NPAPI bits have already been solved for you.