Chapter 8

NPP Methods: Other Tasks


CONTENTS

Chapter 6 "NPP Methods: Starting the Plug-In," shows how Navigator loads the plug-in and starts an instance. Chapter 7, "NPP Methods: Handling Interaction," describes additional tasks your plug-in may have to do to interact with the user and with the data stream itself.

Most plug-ins are also called on to print themselves, either as embedded content in a Navigator window or as a full document. All plug-in instances are eventually destroyed, and any associated window the instance has must be closed.

This chapter deals with these "additional duties" of the plug-in: printing, closing its windows, and destroying itself on request.

Understanding NPP_Print()

Recall that you, as the programmer, write all NPP functions. These NPP functions are called by Navigator. NPP_Print() is unique in that, under the right circumstances, Navigator calls it twice. NPP_Print() is Navigator's request that your plug-in print itself.

Embedded Mode

An HTML author can include a plug-in using an <EMBED> statement. Alternatively, the HTML author can put a link in his or her Web page that points to a whole new document.

If your plug-in is called through <EMBED>, it is an embedded plug-in. If your plug-in is invoked on a document, it is a full-page plug-in.

If your plug-in is embedded, Netscape calls the plug-in's NPP_Print() function once. If your plug-in is full-page, Netscape calls your plug-in's NPP_Print() once to find out if the plug-in handles the whole printing process. If it does not, Netscape calls your plug-in again, also through NPP_Print(), to have it handle the printing of the content.

Confused? NPP_Print() is a busy place, particularly for a full-page plug-in. This section shows an example of how to handle NPP_Print() in an embedded plug-in. NPP_Print() is defined by the following line:


void NPP_Print(NPP instance, NPPrint *platformPrint);

Here, instance is the usual instance that is passed from Netscape to nearly all NPP functions and platformPrint is a structure whose mode member can take on the NP_EMBED and NP_FULL values. When the user elects to print a page that has an embedded plug-in, Netscape calls NPP_Print() for the plug-in with platformPrint->mode set to NP_EMBED.

The full declaration of platformPrint is as follows:


typedef struct _NPPrint

{

    uint16      mode;           /* NP_FULL or NP_EMBED */

    union

    {

        NPFullPrint fullPrint;  /* if mode is NP_FULL */

        NPEmbedPrint embedPrint;/* if mode is NP_EMBED */

    } print;

} NPPrint;

Note that the union statement says that the two data members fullPrint and embedPrint occupy the same space-only one member can be present in an instance at a time. If mode is set to NP_EMBED, the union print has embedPrint.

The structure of embedPrint is as follows:


typedef struct _NPEmbedPrint

{

    NPWindow    window;

    void*       platformPrint;  /* Platform-specific printing info */

} NPEmbedPrint;

The NPWindow in this structure is the window the plug-in should draw into for printing. Recall from Chapter 6 "NPP Methods: Starting the Plug-In," that an NPWindow is defined as shown in the following lines:


typedef struct _NPWindow 

{

    void*       window;    /* Platform specific window handle */

    uint32      x;         /* Position of top left corner relative */ 

    uint32      y;         /*   to a netscape page. */

    uint32      width;     /* Maximum window size */

    uint32      height;

    NPRect      clipRect;  /* Clipping rectangle in port coordi

                           /* Used by Mac only. */

#ifdef XP_UNIX

    void *      ws_info;   /* Platform-dependent additional data */

#endif /* XP_UNIX */

} NPWindow;

Your plug-in should draw into the window member to print its contents.

Caution
On a Windows machine, the coordinates of the window rectangle are in twips. In the MM_TWIPS mapping mode the logical unit, twip, is 1/20 of a point or 1/1440 of an inch, and the base unit is in physical inches. This design is never used with a display (because displays come in all sizes and the physical inch is meaningless), but it is perfectly appropriate when drawing to a printer.
Make sure that you call DPtoLP() to convert these points when you print text into the window. DPtoLP() converts device coordinates to logical coordinates using the current mapping mode.

Special Features on the Macintosh  The second field of NPEmbedPrint, platformPrint, says that it has Platform-specific printing info. Specifically, on the Macintosh, platformPrint has a THPrint, a handle to a TPrint. The TPrint, in turn, is an Apple-defined structure that includes the following:

Caution
The TPrint structure is strictly read-only. Make sure that your code wraps reads of the structure in access methods-Apple changes the details of this structure from time to time to keep up with advances in printing technology. If you use access methods, you can take advantage of Apple's changes.

By the time Navigator is ready for your plug-in to print its contents, it has already given the user a Print… dialog. As a Macintosh user, you will recognize many of these parameters from the Page Setup or Print… dialogs. Navigator also fills in information it gets from the printer driver.

Tip
The TPrint has a rich array of detailed information about the printing job-more than most plug-ins will ever use. Unless your plug-in has special needs, don't access the TPrint record-simply draw the contents of your plug-in into the window provided, and let Navigator and the printer driver handle the rest.
This approach leaves your code more portable to other platforms and makes it more likely that your plug-in will work correctly with all printers (including future versions of the printer driver).

Full-Page Mode

When Navigator is ready to print your full-page plug-in, it first gives you an opportunity to take control of the printing process. Some documents benefit from this opportunity. For example, Figures 8.1 and 8.2 contrast the standard Print… dialog with the Print dialog provided by Microsoft Word.

Figure 8.1 : Unless you take control of the printing process, Navigator provides a standard Print… dialog for printing your full-page plug-in.

Figure 8.2 : Microsoft added several items to the Print… dialog to supplement the standard Print…dialog.

To allow you to take over the printing process, Navigator calls NPP_Print() with platformPrint->mode set to NP_FULL and union member print filled with an NPFullPrint. Navigator defines an NPFullPrint as follows:


typedef struct _NPFullPrint

{

    NPBool   pluginPrinted; /* Set TRUE if plugin handled fullscreen 

                            /*      printing */

    NPBool   printOne;      /* TRUE if plugin should print one copy  

                            /*      to default printer */

    void*    platformPrint; /* Platform-specific printing info */

} NPFullPrint;

The platformPrint member has the same information it has in NPEmbedded. Using the NPBool printOne is self-explanatory and tells the plug-in that the user just wants a quick, no-dialog print.

The NPBool pluginPrinted is the member that distinguishes full-page printing from embedded plug-in printing. If you commandeer the printing process, providing your own dialog boxes and driving the printer yourself, set pluginPrinted to true. If Navigator sees this flag set to true, it bypasses its own Print dialog.

Tip
Although Netscape allows your full-page plug-in to take over the printing process, you need to use this option only if your plug-in has special needs. Most full-page plug-ins can set pluginPrinted to false, allowing Navigator to handle the Print dialog.

If you set pluginPrinted to false, Navigator initiates the printing process. When it is ready for your content, Navigator calls your plug-in again-through NPP_Print()-with platformPrint->mode set to NP_EMBED.

Tip
Your Macintosh plug-in may need access to information from the printer driver or the Print… dialog. But if you don't need to add items to the standard dialog or otherwise control the print process, simplify your design by returning false in pluginPrinted. Then, when Navigator calls NPP_Print() the second time, read platformPrint to get the information you need.
Alas, Netscape doesn't provide similar information for Windows or UNIX plug-ins in Navigator version 3.0. On these platforms you must take control of the printing process if you want detailed information about the printer or the print job.

If you save data with your full-page plug-in instance (using NPSavedData, described in a following section of this chapter, "Saving Instance Data"), consider including a copy of the TPrint. Then, if you are later asked to print again, you can reuse the TPrint to handle a printOne request.

Call PrValidate() to ensure that the user hasn't changed the active printer or printer driver since the last print request. If the TPrint changed-indicated by a TRUE return from PrValidate()-or if the user makes a printOne request before he or she has completed a Print… dialog, just call PrintDefault() to preset the fields of the TPrint.

Printing in Windows  Printing is supported by the Microsoft Foundations Classes (MFCs) CView class. If your full-page plug-in handles multipage documents, override CView's OnPrint() member in your own derived class.

In your view's version of OnPrint(), look up the size of the printer's page and use this information to adjust the clipping region of the device context associated with the print window. After the "page" is defined, call the view's OnDraw() member to output the content.

Even today, not all printers accept bitmaps. You can find out if the printer associated with your print window properly renders a bitmap by including the following code (in which hDC is the device context associated with the window):


if (!(GetDeviceCaps(hDC, RASTERCAPS)

     & RC_BITBLT))

{

  // printer cannot display bitmaps

.

.

.

}

When you are ready to print, you will want to put up a Cancel dialog, in case the user decides to stop the print job before it completes. Putting up this kind of dialog box is a four-step process:

  1. Set a Boolean flag that the abort procedure and the plug-in can share. The abort procedure reads this flag to see if it should allow the printing to continue.
  2. Register an AbortProc function with the operating system.
  3. Display the modeless Cancel dialog.
  4. Disable the application window.

Here's the code for these four steps:


BOOL bContinuePrinting = TRUE;

SetAbortProc(hDC, AbortProc);

hdlgCancel = CreateDialog(hinst, 

  (LPTSTR) "AbortDlg", hWnd, (DLGPROC) AbortPrintJob);

EnableWindow(hWnd, FALSE);

The abort procedure can be very simple, as follows:


BOOL CALLBACK AbortProc(HDC hDC, int nCode)

{

  MSG msg;

  while (PeekMessage((LPMSG) &msg, (HWND) NULL, 0, 0, PM_REMOVE))

  {

    // if the message isn't for us, send it on

    if (!IsDialogMessage(hdlgCancel, (LPMSG) &arg))

    {

      TranslateMessage((LPMSG) &msg);

      DispatchMessage((LPMSG) &msg);

    }

  }

  return bContinuePrinting; // set to FALSE by Cancel button

}

Use your development environment to construct a suitable Cancel dialog. Write a dialog procedure that includes the following code:


switch (message)

  .

  .

  .

  case WM_COMMAND: // the Cancel button is the only control

  {

    bContinuePrinting = FALSE;

    return TRUE:

    break;

   }

  .

  .

  .

}

Now when Navigator tells you to print, you can put up the common Print dialog (possibly with customizations). Verify that the printer you selected can print your document. Put the Cancel dialog box on-screen.

If the user clicks the Cancel button, the WM_COMMAND message is sent to the AbortPrintJob function, which sets bContinuePrinting to FALSE. When the plug-in sees bContinuePrinting go FALSE, it stops drawing pages and cleans up from printing.

After the Cancel dialog box is up, the plug-in calls StartDoc(), and then StartPage(). These Windows functions alert Windows that the print data is coming.

Then, based on the data returned from the Print dialog, you find the page(s) the user wants to print and draw their contents to the print window. Call EndPage() after each page is printed.

When all the data was sent (or bContinuePrinting becomes false), call EndDoc(). Finally, call EnableWindow() to return control to the plug-in window, call DestroyWindow() on the cancel dialog, and call DeleteDC() on the device context allocated by StartDoc().

For most plug-ins, this is all that's needed. But if your plug-in draws text, you may want to think about fonts and text metrics. Microsoft Windows comes with various fonts for the screen-Courier, Helvetica, and Times Roman, to name a few.

Most printers include most of these common fonts. But not all printers use all the standard fonts, and users can add fonts to their system. Therefore, a user may have text on the screen in a font that is unavailable in the printer.

Note
Windows supports many font technologies. A Postscript printer uses, of course, a PostScript font. This font may be downloaded from the computer or stored in the printer's read-only memory (ROM).
Hewlett-Packard's PCL printers use HP's Printer Control Language (PCL). Both Microsoft and HP provide drivers for these printers, which offer good font quality.
On the PC screen, Windows can use bit-mapped fonts (known as raster fonts). If a user prints a document that is being viewed with raster fonts, Windows must try to match a printer font (such as PostScript or PCL) to the raster font.
TrueType technology is a compromise between raster fonts and printer fonts. TrueType fonts are built from mathematical models (like PostScript fonts) but can be displayed both on-screen and on the printed page.
For more information on Windows fonts see Chapter 8 "Working With Fonts," in Platinum Edition Using Windows 95 (Que, 1996).

Windows includes an elaborate penalty weighting algorithm based on ten font characteristics, including character sets, pitch, family, face name, height, width, and various styles. Windows uses this algorithm to choose a printer font that should be a close match to the screen font.

You, the programmer, should control which font the plug-in uses and match it with the available printer font. Otherwise, the printing may be done in a font that is quite unlike the font used on-screen.

Tip
If your plug-in chooses the screen font, choose a TrueType font whenever possible. One big advantage of TrueType is that the printer version and the screen version match exactly.

For better control over the printed page, Windows programmers can call GetTextMetrics() to fill a TextMetric data structure with information about the currently selected font. The TextMetric structure includes information such as the following:

For pinpoint control of key pieces of text (such as titles), use GetTextExtentPoint32(). This function computes the width and height of a specified string in the current font. Use this information to center, scale, or align text with other elements.

Customizing the Print Dialog in Windows  The most common reason for taking control of the printing process is to add items to the Print dialog. Microsoft provides a set of common dialog boxes, three of which apply to printing.

These three dialog boxes (Print Setup, Page Setup, and Print) get some information from the common dialog DLL (COMDLG32.DLL) and some information from the printer driver. Figure 8.3 shows the common Print dialog.

Figure 8.3 : Microsoft makes it easy to display the common Print dialog.

To customize the Print dialog, use MFC to write your plug-in. Then follow this procedure:

  1. Copy the PRINTDLGORD dialog template from commdlg.rc to your plug-in's .rc file. If you have Microsoft Visual C++, commdlg.rc is located at \msvc\mfc\samples\apstudio\commdlg.rc.
  2. Use the resource editor to add controls to your plug-in's copy of the PRINTDLGORD dialog template.
  3. In your plug-in, derive a new C++ class from CDialog. For this example, call the new class CPluginPrintDialog. Use PRINTDLGORD as the dialog ID. If you use Visual C++, use the ClassWizard to build the class template. Where ClassWizard uses CDialog in the header and body files, substitute CPrintDialog.
  4. For example, your class declaration begins as follows:
    class CPluginPrintDialog : public CPrintDialog
  5. Modify the class constructor. The CPrintDialog constructor is specified as follows:
    CPrintDialog(BOOL bPrintSetupOnly,
                 DWORD dwFlags = PD_ALLPAGES |
                                 PD_USEDEVMODECOPIES | PD_NOPAGENUMS |
                                 PD_HIDEPRINTTOFILE | PD_NOSELECTION,
                                 CWnd* pParentWnd = NULL);
  6. Your constructor should set the second parameter, as shown here:
    CPluginPrintDialog(BOOL bPrintSetupOnly,
                  DWORD dwFlags = PD_ALLPAGES |
                     PD_USEDEVMODECOPIES | PD_HIDEPRINTTOFILE,
                     CWnd* pParentWnd = NULL);
  7. Make sure that you include the macro DECLARE_MESSAGE_MAP() at the end of your constructor's declaration.
  8. If you follow the usual MFC design, your plug-in has a view (derived from the MFCs class CView), which displays your document. For this example, call this view CPluginView. In your view implementation file, include the header file for your custom dialog. Overwrite CView's OnPreparePrinting() member as follows:

BOOL CPluginView:OnPreparePrinting(CPrintInfo* pInfo)

{

  // replace the default CPrintDialog with our own

  delete pInfo->m_pPD; 

  pInfo->m_pPD = new CPluginPrintDialog(FALSE); // 

       FALSE for Print Dialog



  // set up some nice defaults

  pInfo->m_pPD->m_pd.nMinPage = 1;

  pInfo->m_pPD->m_pd.NMaxPage = 0xffff;



  // point the view to our version of the print dialog

  pInfo->m_pPD->m_pd.hInstance = AfxGetInstanceHandle();

  pInfo->m_pPD->m_pd.lpPrintTemplateName =   

                    MAKEINTRESOURCE(PRINTDLGORD);



  // and turn on the template

  pInfo->m_pPD->m_pd.Flags |= PD_ENABLEPRINTTEMPLATE;



  // finally, call the parent's method to complete the task

  return DoPreparePrinting(pInfo);

}

Tip
If you don't want some of the controls in the common Print dialog, disable them. Don't delete them-CPrintDialog expects to find them when it calls DoDataExchange(). You can hide them, but users may be confused if you make too many changes to the standard dialog. You can communicate your interface design more clearly by leaving the control present but disabled.

Customizing the Print… Dialog on a Macintosh  If your plug-in is designed for the Macintosh, you can add items to the standard Print… dialog. Delete no items from the standard dialog or change their position. (Not only can these kinds of changes confuse the software, they also can confuse the user.)

Caution
Don't use more than half the screen for your custom controls. Apple warns that future versions of their standard Print… dialog may use as much as half the screen, leaving only the bottom half for your customization. Remember, too, that many users still have older Macs with 9-inch screens.
As a practical matter, if your plug-in needs so many controls that it takes half the screen (even a 9-inch screen), it's probably too busy to be usable. Consider moving some of these controls to a separate dialog, and hooking this dialog to a pop-up menu or another control in your plug-in window.

If you want to understand how to change the Print… dialog, review how the standard dialog works. Your full-page plug-in puts up a Print… dialog by calling PrJobDialog(). This function calls the printer driver of the currently active printer. The printer driver, in turn, calls PrDlgMain(). PrDlgMain() has the following specification:


Boolean PrDlgMain(THPrint hPrtRec, ProcPtr pDlgInit)

The ProcPtr has the address of the dialog initialization procedure-for the Print… dialog. This procedure defaults to PrJobInit().

PrDlgMain() calls the pDlgInit, which sets up the dialog, dialog hook, and dialog event filter. Then PrDlgMain() calls ShowWindow() and ModalDialog() to make the dialog available to the user. As the user interacts with the controls, events are passed to the dialog event filter.

Figure 8.4 illustrates this calling sequence.

Figure 8.4 : The Macintosh operating system provides a hook, allowing applications to add controls to the Print… dialog.

To add controls to the standard Print… dialog, use a resource editor such as ResEdit to make a new Dialog Item List (DITL) with the controls you want to add. Write your own dialog initialization routine, which appends the items from your DITL to the standard dialog and initializes those items. For example, you might write the following:


const short kPluginDITL=256;

.

.

.

pascal TPPrDlg PluginJobDialog( THPrint thehPrint)

{

  // Append the items in kPluginDITL to the DialogPtr PrtJobDialog

  .

  .

  .

  // save the old procedure; we'll need it if the user hits any of 

  // the standard controls.

  prPItemProc = (long)PrtJobDialog->pItemProc;

  PrtJobDialog->pItemProc = (ProcPtr)PluginJobItems;

  return PrtJobDialog;

}

pascal void PluginJobItems(TPPrDlg theDialog, short theItemNo)

{

  // handle hits on our items

  // use a switch on the item number

  // If the item hit was not ours, call the standard handler

  CallPascal(theDialog, theItemNumber, prPItemProc);

}

Tip
Symantec offers an electronic reference to the Mac toolbox functions, known as THINK Reference. This product includes a nice function named AppendDITL() which can be called to "Append the items in kPluginDITL to the DialogPtr PrtJobDialog" as required in the preceding code.

On the Web
For more information on THINK Reference, visit Symantec on-line at http://www.symantec.com/.

Now, when you call PrDlgMain(), pass the address of your dialog initialization function. PrDlgMain()calls your function, which appends items from your DITL to the standard Print… dialog.

When any item in the dialog gets a hit, your PluginJobItems() handles the event. If the hit isn't on one of your plug-in's controls, you call the default handler.

When the user finally activates the OK button, ModalDialog returns, and your plug-in can read the value the user set in the custom controls. If the user clicks Cancel, your implementation of NPP_Print() should just clean up and exit.

Using NPP_Destroy()

It's said that "All good things must come to an end…". So it is with plug-ins. When the user leaves the page on which your plug-in content is displayed, Navigator calls your plug-in's NPP_Destroy() function, and then deletes the plug-in instance.

Freeing Dynamic Memory

In C++, every class has one or more constructors and one destructor. The constructors get called in response to instantiations. The destructor gets called when a stack-based instance goes out of scope or a dynamically allocated instance is deleted.

If a C++ class allocates memory in its constructor, it should deallocate that memory in the destructor. If you forget to deallocate the memory, the pointer to the allocated memory is lost, but the memory remains unavailable for use by other applications.

If the plug-in runs long enough, it eventually uses up all memory and crashes the Netscape client. Depending upon the operating system, the user may lose work in other open applications as well. The phenomenon of allocating memory and then failing to deallocate it is known as a memory leak, which can lead to subtle defects in your software.

Figure 8.5 illustrates a memory leak.

Figure 8.5 : If you forget to free memory that was allocated in the constructor, Navigator eventually runs out of memory and crashes.

Recall that you should allocate dynamic memory for a plug-in by calling NPN_MemAlloc(). The corresponding routine to deallocate that memory is NPN_MemFree(). In C++, implement new and delete using NPN_MemAlloc() and NPN_MemFree() to ensure proper behavior. The section, "The Run-Time Model," in Chapter 5 "Design Issues," shows how to use NPN_MemAlloc() and NPN_MemFree() to implement custom versions of new and delete.

Besides deallocating dynamic memory allocated in constructors, you should make sure that you deallocate all memory you have reserved in the structure pointed to by instance->pdata. The rule of thumb to remember is "For every new, there must be a delete."

Saving Instance Data

Often, you want to associate some record of the user's activity with the URL they visited. For example, the user may have opened a video clip and played the first 30 frames. If the user leaves this URL and comes back later, perhaps play should resume at frame 31. Perhaps the user has printed the current document, specifying parameters such as page range and orientation. If he or she returns to this URL and prints the document again, you may want your plug-in to remember the parameters the user previously specified and use these parameters as the default settings.

You can store data in Navigator's memory and associate it with the current URL by saving this data in the save parameter of NPP_Destroy().

NPP_Destroy() is specified as follows:


NPError NPP_Destroy(NPP instance, NPSavedData **save);

Here, NPSavedData is as follows:


typedef struct _NPSavedData

{

    int32       len;

    void*       buf;

} NPSavedData;

When to Use NPP_Destroy()  If you allocate saved data in NPP_Destroy(), Navigator keeps this data in case the user returns to the same URL. You might want to save data if the following instances are true:

How to Use NPP_Destroy()  To use saved data, follow these five steps in NPP_Destroy():

  1. Design and use a C++ class or structure that has all the information you want to store. For this example, the class is named TStoredData.
  2. Allocate a new instance of TStoredData (here named theStoredData) and fill its fields.
  3. Allocate a new NPSavedData, with a line such as the following:
    *save = (NPSavedData*) NPN_MemAlloc(sizeof(NPSavedData));
  4. Assign sizeof(theStoredData) to (*save)->len.
  5. Assign the address of theStoredData to (*save)->buf.

Now complete NPP_Destroy(). Navigator deletes your plug-in instance. Later, if the user visits the same URL as the earlier instance, Navigator passes theStoredData in the saved parameter of NPP_New().

Tip
Make sure that whatever structure you put into (*save)->buf (in this example, TStoredData) is flat-that is, none of its members are themselves dynamically allocated. If Navigator runs short on memory, it begins deallocating saved data to free space.
Because it doesn't know the internal structure of your storage class, it cannot deallocate internal dynamic memory. Figures 8.6 and 8.7 illustrate the wrong and right ways to design TStoredData.

Figure 8.6 : If any members of your storage class are dynamically allocated, that memory is lost when Navigator deletes the saved data, resulting in a memory leak.

Figure 8.7 : Flatten your stored data before saving it, so that Navigator can get every field with a single call to delete.

Caution
Remember that Navigator deletes saved data if it needs to free up memory. Remember also that all saved data is stored in RAM and is lost if the user exits Navigator. If your plug-in produces critical data that you don't want to lose, either save it on the user's hard disk, or use NPN_PostURL() or NPN_PostURLNotify() to send the data back to the server.
Note that some users are uncomfortable with the idea that a program they download from the Net may write to their hard disk. Be courteous-ask the user at runtime if it's okay, or at least put a conspicuous notice in your documentation that the file will be written to the user's hard drive.

Destroying the Window

If you are an experienced programmer, you are used to explicitly closing and deallocating all windows when the program exits. You don't need to do this in a plug-in. By the time NPP_Destroy() is called, the window is gone.

Caution
Do not do any graphics operations in NPP_Destroy(). Your window is no longer valid-it is gone!

Using NPP_Shutdown()

Recall from Chapter 6 "NPP Methods: Starting the Plug-In," that Navigator calls NPP_Initialize() when the plug-in is first loaded, before calling NPP_New() to make the first instance. NPP_Initialize() is the place to allocate any data that is used by all instances of your plug-in.

NPP_Destroy() corresponds to NPP_New() in that NPP_Destroy() is called whenever the instance is deleted. Similarly, NPP_Shutdown() corresponds to NPP_Initialize().

When the last instance of a plug-in is deleted, Navigator calls NPP_Shutdown() so that it can delete any dynamic data allocated by NPP_Initialize(). Just as in NPP_Destroy(), failure to free all the memory allocated in NPP_Initialize() leads to a memory leak.

From Here…

During the life of your plug-in, the user may want to print the contents of the plug-in. If the plug-in is embedded, Navigator calls the plug-in and requests that it draw its contents into the window provided.

If the plug-in is full-page, Navigator calls your plug-in and invites it to take over the printing process. You may choose to do so to customize the Print dialog or to access details of the printer driver.

If you choose not to run the printing process from the plug-in, Navigator calls your plug-in back when it is time to draw its content.

When the plug-in's life is over, Navigator calls NPP_Destroy(). In NPP_Destroy() you should deallocate all dynamic memory, including the memory allocated in the data structure pointed to by instance->pdata. To keep any data for use by the plug-in the next time it is called for this URL, you can save this data during NPP_Destroy().

When the last instance of the plug-in is deleted, Navigator calls NPP_Shutdown(), giving your plug-in a chance to free any resources allocated during NPP_Initialize().