Your plug-in's window is created by the Netscape browser. To receive window events in the Microsoft Windows environment, you must subclass the plug-in window using the Windows API SetWindowLong method or the Microsoft Foundation Class Library SubclassWindow method. The Macintosh does not provide for window subclassing and must use the plug-in method NPP_HandleEvent to get window events.
Printing is handled jointly by your plug-in and the browser in the case of an embedded plug-in. For full-page plug-in types, printing is solely up to the plug-in.
The following APIs are covered in this chapter:
NPP_SetWindow
NPP_Print
NPP_HandleEvent
If your plug-in is of type NP_EMBED or NP_FULL, the Navigator creates a window on its behalf. The size of the window depends on the type. An embedded window's size is specified in the HTML code, as in the following example:
<embed SRC=sound.wav WIDTH=400 HEIGHT=60>
This code causes the browser to create a window of width 400 and height 60. A NP_FULL type plug-in refers to a full Navigator page. Full page plug-ins fill the whole page display area of the browser. Regardless of the size, it is the browser that creates the window, not your plug-in. This creates some special challenges to the plug-in developer.
Because the plug-in does not create the window, it needs a way to receive events from any user actions relating to the window. In the world of Microsoft Windows, this is accomplished through subclassing the window. Macintosh plug-ins receive events through a Macintosh-specific API called NPP_HandleEvent.
In Microsoft Windows, every window has a class associated with it. This class is defined by a WNDCLASS structure and a call to RegisterClass. Within this structure is an address of the window procedure that is called for windows of this class. This window procedure is called whenever a message for the window needs to be processed.
A plug-in is contained in a Windows DLL. This DLL is loaded and called by the Netscape Navigator and becomes part of the Navigator's process. Being part of this process allows the plug-in and browser to easily share system resources such as memory and files. In addition, being in the same process space allows a plug-in to subclass a window procedure that is in the Navigator's code. Through a series of APIs, the plug-in can instruct Windows to call a window procedure within the plug-in's code before it calls the window procedure in the browser's code. The new window procedure is hooked in as shown in Figure 15.1.
Figure 15.1 : A plug-in's window procedure subclassing the Navigator created plug-in window.
Immediately after Netscape Navigator creates a window for your plug-in, it calls the NPP_SetWindow API. This API passes a window handle for the newly created window. If you are not using Microsoft's Foundation Class Library (MFC), you must subclass Netscape's window with the SetWindowLong API. This function replaces the window procedure with one inside your plug-in DLL. You can call it like this:
instance->lpfnOldWndProc = (FARPROC)SetWindowLong (hWnd,
GWL_WNDPROC,
(DWORD)SubclassFunc);
In this example, SetWindowLong replaces the window procedure for the window specified by hWnd with the new procedure SubclassFunc. Note the saving of the old window procedure within lpfnOldWndProc, which is returned by SetWindowLong. The new procedure located within your DLL's code would be structured something like this:
LONG NP_LOADDS WINAPI SubClassFunc (HWND hWnd,
WORD Message,
WORD wParam,
LONG lParam)
{
PluginInstance* instance = GetInstance(hWnd);
switch (Message)
{
case:
//
// Any message specific code would go in this switch statement.
//
break;
default:
break;
}
return CallWindowProc (instance->lpfnOldWndProc,
hWnd,
Message,
wParam,
lParam);
}
At the end of SubClassFunc is a call to the Windows API CallWindowProc. As you might have guessed, this API calls the old window procedure, located in the Navigator, with the saved procedure pointer instance->lpfnOldWndProc.
When your plug-in's instance is being destroyed, you should remove the subclass from the window. This is done with the same API, SetWindowLong, and is called as follows:
SetWindowLong (instance->hWnd, GWL_WNDPROC, instance->lpfnOldWndProc);
Here the saved old window procedure is replacing the new one installed previously.
One obstacle with using a subclassed window procedure without MFC is getting your plug-in's instance data. Most Netscape methods provide your plug-in with instance data with a passed parameter of instance. This is a pointer to a structure containing references to both the plug-in's and Netscape's instance data. However, the subclassed window procedure is not called directly from the Navigator. It is called by Windows itself. Windows does not pass a pointer to your instance data.
The most obvious solution is to provide a global reference to your instance data in the DLL's global data. Unfortunately, this won't do the trick, because plug-ins support multiple window instances. A single pointer to an instance is overwritten when the next window is created.
Another potential way is to use extra window bytes allocated with the RegisterClass API. You could have stored a pointer to your instance data within these extra bytes if Netscape had allocated any extra window bytes for a plug-in's use-which it didn't.
Netscape provides a technique for retrieving your instance data using a linked list to store pointers to your instance for each plug-in window. The non-MFC example of NPSHELL.CPP (provided on this book's CD-ROM) gives an example of this technique with two routines: AssociateInstance and GetInstance. AssociateInstance is called during the NPP_SetWindow method for any new windows. It associates an instance pointer to a window handle. GetInstance, called during the new window procedure, scans through the linked list until the same window handle is found, and then it returns an instance pointer. You can see a call to GetInstance in the previous example of SubClassFunc.
If Netscape's linked list approach causes your head to spin, another way to store window-specific data is by storing it in the window's property list. The SetProp API attaches properties to a window by calling it with a window handle, property string, and pointer to your data. When you need to get your instance data again, simply call GetProp with the window handle and property string. Your instance pointer is returned. Don't forget to call RemoveProp to remove your data when the window is destroyed.
| Note |
SetProp, GetProp, and RemoveProp are well-documented in the Windows API documentation. Another source of information is called Safe Subclassing in Win32 by Kyle Marsh. This article can be found on Microsoft's Development Library CD-ROM. |
Microsoft's Foundation Class Library makes subclassing a window much easier. Problems with instance data are gone, because MFC enables you to create a window object with an associated message map. This window object is defined in your code as a child of CWnd. You can store any data you need, including a pointer to the plug-in's instance data within this object. MFC shields you from the complexities of associating data with a particular window.
To use this technique, first create a new window class derived from CWnd in your header file. Here is a very simple example:
class CPluginWindow : public CWnd
{
protected:
MyInstance* instance;
public:
CPluginWindow ();
~CPluginWindow ();
void StoreData (MyData *);
//{{AFX_MSG( CMainWindow )
afx_msg void OnPlay();
afx_msg void OnStop();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Notice the pointer to your instance data: MyInstance* instance. This pointer is set with a call to the method StoreData. A message map is declared with methods OnPlay and OnStop.
When the plug-in method NPP_SetWindow is called for the first time, construct your object CPluginWindow and attach a pointer to your instance data. Remember, constructing a CWnd derived class does not actually create a window. This is important because CPluginWindow will be subclassed to an already existing window:
instance->pWindow = new CPluginWindow ();
Then call the inherited CWnd method SubclassWindow, and give it a handle to the plug-in window create by the Navigator:
instance->pWindow->SubclassWindow ((HWND)window->window);
Finally, save a pointer to your instance data in your new CPluginWindow with a call to StoreData or your equivalent method:
instance->pWindow->StoreData (instance);
MFC handles the rest by calling your appropriate method defined in the message map and, upon completion, returning control to the original Navigator window procedure.
Remember to unsubclass the window during destruction of the window's instance in NPP_Destroy with a call to CWnd's UnsubclassWindow:
instance->pWindow->UnsubclassWindow ();
| Warning |
Netscape's current documentation instructs you to unsubclass your window during a call to NPP_SetWindow when the Navigator window handle (window->window) is set to NULL. Unfortunately, for Navigator 2.x and early betas of 3.x, a call is made to NPP_SetWindow during printing with a NULL window handle without another call to NPP_SetWindow to allow plug-ins to re-subclass. The workaround is to unsubclass only during an NPP_Destory. |
When a user requests a print action and your plug-in is a visible type and is loaded, a call is made to your plug-in's NPP_Print method. To receive this call, your plug-in must be of type NP_FULL or NP_EMBED.
For plug-ins of type NP_FULL, printing is handled totally by the plug-in code. A NULL pointer is passed to the NPP_Print method for the printInfo parameter. The plug-in is in charge of all printing-related tasks such as displaying the printer dialog, getting the printer, creating the printer device context, scaling data, and so on.
Plug-ins of type NP_EMBED print jointly with the Navigator. An embedded plug-in is, by definition, embedded in another document. The Navigator must print its portion of this document and, in turn, the plug-in prints the embedded portion. A plug-in might opt to print nothing, which simply leaves a space in the printout.
When NPP_Print is called for an embedded plug-in, the API parameter printInfo points to the structure NPPrint. This structure contains the plug-in mode and a union of structures NPFullPrint and NPEmbedPrint:
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;
| Warning |
At the time of this writing, NPPrint is broken. NPPrint only references a plug-in type of NP_EMBED, because a type NP_FULL is passed a NULL pointer for the structure NPPrint. This means that the mode is invalid and the union only references the structure NPEmbedPrint. |
The structure NPEmbedPrint is currently defined as follows:
typedef struct _NPEmbedPrint
{
NPWindow window;
void* platformPrint; /* Platform-specific printing info */
} NPEmbedPrint;
In this case, the window structure member is a structure of type NPWindow and the platformPrint structure member is the printer's device context (DC). NPWindow is currently defined as follows:
typedef struct _NPWindow
{
void* window; /* platform specific window handle */
uint32 x; /* position of top left corner relative
to a netscape page */
uint32 y;
uint32 width; /* maximum window size */
uint32 height;
NPRect clipRect; /* clipping rectangle in port coordinates */
} NPWindow;
NPWindow provides a window handle, x/y coordinates for the top left corner of the plug-in window relative to a browser page, window width and height, and a clipping rectangle.
In Microsoft Windows, all window coordinates are in TWIPs. A TWIP is one twentieth of a printer's point. A point is 1/72 of an inch, so a TWIP is 1/1440 of an inch. You must use the appropriate conversions to get back to logical coordinates.
Because of the Macintosh windowing architecture, it is not possible to receive events using subclassing. To provide window events to a Macintosh plug-in, Netscape has included the Mac-specific plug-in method NPP_HandleEvent.
NPP_HandleEvent is passed a pointer to a Macintosh EventRecord structure. The EventRecord.what field can be any of the normal Macintosh event types or a Netscape-specific event. Currently, the following types are used:
getFocusEvent
loseFocusEvent
adjustCursorEvent
getFocusEvent informs your plug-in that it will get key events. If you don't want to get key events, just return a FALSE when passed a getFocusEvent.
The event loseFocusEvent lets a plug-in know that it is no longer in focus.
The adjustCursorEvent event is passed to your plug-in as the mouse moves over your window. You can hide the cursor by returning FALSE.
The following sections give in-depth information on the APIs used in this chapter.
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX. |
| API Type | Plug-in method called from the Navigator. |
| Purpose | Informs a plug-in that a new window was created, the window was moved or sized, or the window was destroyed. |
| Syntax | NPError NPP_SetWindow (NPP instance, NPWindow* window)
NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPWindow* window: A pointer to structure NPWindow that contains a platform-specific window handle, top left corner, window width, height, and a clipping rectangle. |
| Includes | #include <npapi.h> |
| Description | This method is called to inform you that a window was created for your plug-in. A handle is provided to allow you to subclass this window. Normally, this window is a subwindow of the Navigator
window hierarchy.
NPP_SetWindow is only called for plug-in types of NP_FULL (indicating a Navigator full page) or NP_EMBED (indicating an embedded window). NP_BACKGROUND type plug-ins do not have an associated window. If the window is moved or resized, NPP_SetWindow is called with the new window position and size. Your window can be destroyed. This is indicated by a call to NPP_SetWindow with the platform-specific window handle set to NULL. At times, NPP_SetWindow might be called without any new information. Your plug-in should maintain window state information to handle such cases. |
| Returns | NPERR_NO_ERROR: No error.
NPERR_GENERIC_ERROR: An unknown error occurred. NPERR_OUT_OF_MEMORY_ERROR: Out of memory. NPERR_INVALID_INSTANCE_DATA: Invalid instance data. |
| See Also | NPP_HandleEvent (for Macintosh developers) |
| Example | //
// NPP_SetWindow // NPError NP_LOADDS NPP_SetWindow (NPP instance, NPWindow* window) { if (!window) return NPERR_GENERIC_ERROR; if (!instance) return NPERR_INVALID_INSTANCE_ERROR; // Retrieve instance data MyInstance* myinstance = (MyInstance *)instance->pdata; if (!myinstance) return NPERR_GENERIC_ERROR; if (!window->window && !myinstance->pWindow) // spurious Âentry return NPERR_NO_ERROR; if (!window->window && myinstance->pWindow) { // The window was destroyed. Don't do anything. return NPERR_NO_ERROR; } if (!myinstance->pWindow && window->window) { // The first call to NPP_SetWindow. We need to create Âour window // and subclass it to Navigator's window. myinstance->pWindow = new CPluginWindow (); myinstance->pWindow->SubclassWindow ((HWND)window- Â>window); myinstance->pWindow->InitWindow (); // Save a pointer to our instance in our new window Âclass's data myinstance->pWindow->StoreData (myinstance); } // Resize or move the window. In some cases the window may // not have changed. data->pWindow->InvalidateRect (NULL); data->pWindow->UpdateWindow (); return NPERR_NO_ERROR; } |
| Compatibility | Windows 3.1/95/NT, Macintosh, UNIX. |
| API Type | Plug-in method called from the Navigator. |
| Purpose | Called when the user requests a print action while your plug-in is loaded. The plug-in can be of type NP_EMBED, in which the Navigator prints jointly with the plug-in, or type NP_FULL, in which the plug-in must handle all printing related functions. |
| Syntax | void NP_LOADDS NPP_Print (NPP instance, NPPrint* platformPrint)
NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. NPPrint* platformPrint: A pointer to structure NPPrint that contains the plug-in's current mode and references to either structures NPFullPrint or NPEmbedPrint, depending on this mode. This parameter can be NULL for plug-in type NP_FULL. |
| Includes | #include <npapi.h> |
| Description | A call to NPP_Print results from the user requesting a print action on a Web page where your plug-in is loaded. The plug-in must be either type NP_FULL or NP_EMBED.
NP_FULL plug-ins are expected to handle all printing-related tasks. These include displaying the printer dialog, getting the correct printer, creating a printer device context (DC), scaling data, and so on. In most cases, a print request for NP_FULL is passed a NULL pointer for the parameter printInfo. NP_EMBED type plug-ins print in conjunction with the Navigator. In addition to printing its part of the page, Netscape handles other print functions including displaying the print dialog, getting the printer, creating the DC, and so on. Your plug-in is passed a reference to structure NPEmbedPrint that contains a printer DC in the platformPrint member and your plug-in's window to be printed in the window member. In Microsoft Windows, all window coordinates are in TWIPs. A TWIP is one twentieth of a printer's point. Because a point is 1/72 of an inch, a TWIP is 1/1440 of an inch. You must use the appropriate conversions to get back to logical coordinates. |
| Returns | None. |
| See Also | Platform-specific printing documentation. |
| Example | //
// NPP_Print // void NP_LOADDS NPP_Print (NPP instance, NPPrint* printInfo) { // Make sure we have an instance if(!instance) return; // Retrieve instance data MyInstance* myinstance = (MyInstance *)instance->pdata; if (!printInfo) { // A NULL value for printInfo indicates an NP_FULL type Âprint. // Verify we are in the NP_FULL mode. if (myinstance->mode != NP_FULL) return; // // // Here would go your printing code for NP_FULL. Ânavigator // provides no assistance in this case. // // } else { // Must be NP_EMBED. printInfo contains the printer DC Âand // print window. NPWindow* printWindow = &(printInfo- >print.embedPrint.window); void* platformPrint = printInfo- >print.embedPrint.platformPrint; // Create a pen LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbHatch = 0; HPEN hPen = ExtCreatePen (PS_COSMETIC | PS_SOLID, 1, Â&lb, 0, NULL); // Get the printer DC from platformPrint. HDC hDC = (HDC)(DWORD)platformPrint; HPEN hPenOld = (HPEN)SelectObject (hDC, hPen); // Draw a rectangle BOOL result = Rectangle (hDC, (int)(printWindow->x), (int)(printWindow->y), (int)(printWindow->x + printWindow->width), (int)(printWindow->y + printWindow->height)); // Print some simple text char buf[] = "Hello From My Plug-in"; TextOut (hDC, printWindow->x + 180, printWindow->y + 180, buf, strlen(buf)); // Cleanup SelectObject(hDC, hPenOld); DeleteObject(hPen); } } |
| Compatibility | Macintosh only. |
| API Type | Plug-in method called from the Navigator. |
| Purpose | Provides a Macintosh plug-in with window events. |
| Syntax | int16 NPP_HandleEvent (NPP instance, void* event)
NPP instance: A pointer to structure NPP that references both the plug-in's and Netscape's private instance data. void* event: A pointer to a Macintosh EventRecord structure. |
| Includes | #include <npapi.h> |
| Description | In Windows, the window handle alone is sufficient for event processing through the technique of window subclassing. On the Macintosh, the native window is shared between the plug-in and the
Netscape client. Subclassing is not possible, so the Navigator provides events to the plug-in with calls to the NPP_HandleEvent API.
The parameter event is a pointer to a Macintosh EventRecord structure. The EventRecord.what field can be any of the normal Macintosh event types or a Navigator-specific type such as getFocusEvent, lostFocusEvent, and adjustCursorEvent. The getFocusEvent event informs your plug-in that it will get key events. Conversely, lostFocusEvent means that you are no longer in focus and will not get key events. If you don't want to receive key events, return FALSE when passed a getFocusEvent. The adjustCursorEvent is passed to your plug-in as the mouse moves over your plug-in's window. To hide the cursor, return FALSE; otherwise, return TRUE. The return value for all standard Macintosh events is ignored except for key events. With key events, return TRUE only if your plug-in has handled that event. |
| Returns | TRUE: The event was handled.
FALSE: The event was not handled. |
| See Also | Macintosh-specific documentation. |
| Example | //
// Handle a Macintosh Event // int16 NPP_HandleEvent (NPP instance, void* event) { NPBool eventHandled = FALSE; // Make sure we have an instance if(!instance) return eventHandled; // Retreive instance data
MyInstance* myinstance = (MyInstance *)instance->pdata; |
The Macintosh receives window events through calls to the Macintosh-specific plug-in API NPP_HandleEvent. Windows plug-ins get their events by subclassing the plug-in window given a window handle in the plug-in method NPP_SetWindow.
During window event processing, plug-in instance data is easy to retrieve using Microsoft's Foundation Class Library. Without MFC, instance data is more difficult to access and must be done through a linked list or a window's property list.
Printing can be performed solely by the plug-in (which is the case with types of NP_FULL) or in conjunction with the browser (which is the case with an embedded plug-in).
That about wraps up the plug-in API documentation. The next few chapters introduce you to sample plug-in code that uses the plug-in API. All of the samples are included on the book's CD-ROM.
The next chapter explains how the sample code is structured and what samples are provided.