Chapter 32

Creating Netscape Navigator Plug-Ins

by Zan Oliphant


CONTENTS

With the release of Netscape Navigator 2.x in the first quarter of 1996 came support for Netscape Navigator plug-in code modules-otherwise known as plug-ins. Plug-ins are similar in functionality to Java, an interpreted language developed by Sun Microsystems. In contrast to Java, Navigator plug-ins are binary code modules written and compiled for each host operating system or hardware platform. Why write a plug-in instead of a Java applet? The answer is speed. Navigator plug-ins are especially well suited for handling high bandwidth data transfers needed for demanding data types such as audio and video over the Internet.

Most of the material in this chapter first appeared in Programming Netscape Plug-ins, also published by Sams.net, and I refer to it throughout this chapter. If you do decide to write a plug-in for Netscape's Navigator or Microsoft's Internet Explorer, you should consider getting a copy of Programming Netscape Plug-ins; it is an authoritative, Netscape-reviewed, plug-in API reference and contains numerous code examples.

Plug-In Design and Architecture

Netscape Navigator's plug-in architecture is based on dynamically loaded code modules. These modules reside in a subfolder or directory called PLUGINS that Navigator reads during its initialization. Each module has a resource that determines which MIME type it can handle. When Navigator finds this MIME type embedded in a Web page through HTML or as a single file, it loads the appropriate code module.

For embedded plug-ins, the HTML EMBED tag tells Navigator the size of the plug-in's window in a given Web page so that Navigator can create the window for the plug-in. The plug-in is given a handle to this newly created window for drawing graphics and processing events.

When a plug-in is loaded, an instance of the plug-in is created with a call to the NPP_New API. The plug-in can be loaded multiple times, creating multiple instances. Web pages frequently have more than one instance of a plug-in. Therefore, if your plug-in uses a restricted resource such as an audio card, you must share this resource across multiple plug-in instances.

Data streams are a big part of Netscape's plug-in architecture. Most plug-ins have data pushed from the server for processing, but others might choose to pull it down, perhaps in a random-access fashion. Just as a plug-in can have multiple instances, it can also have multiple streams per instance. The plug-in API also provides for stream-instance data.

With the introduction of Netscape Navigator 3.0 comes LiveConnect. LiveConnect extends the plug-in architecture by adding communication between plug-ins, Java applets, and JavaScript. The Java Runtime Interface (JRI) plays an important part in this architecture.

How It All Fits Together

As you can see in Figure 32.1, the Navigator 3.x plug-in architecture consists of a plug-in code module, Navigator, Java Applet and JRI, JavaScript, and HTML. A bare minimum plug-in could go without Java, JavaScript, and HTML.

Figure 32.1 : Netscape's Navigator 3.x plug-in architecture.

The core of the plug-in interface is the plug-in API. These APIs are prefixed by NPN for methods located within Navigator and by NPP for methods in the plug-in. By using the APIs, the plug-in becomes part of Navigator's code.

LiveConnect

Netscape's LiveConnect enables you to integrate Java, JavaScript, and plug-ins. New plug-in methods for LiveConnect are NPP_GetJavaClass, NPN_GetJavaEnv, and NPN_GetJavaPeer. Chapter 14 of Programming Netscape Plug-ins, "LiveConnect," documents these Java-specific APIs for plug-ins and gives more details on the LiveConnect interface. Also, Chapter 23 of that volume includes a LiveConnect example written by Netscape.

For the purposes of this chapter, you should understand what LiveConnect does. With LiveConnect you can perform the following tasks:

As you can see, LiveConnect extends not only JavaScript and Java but also plug-ins. Notice the absence of a 33a direct connection between plug-ins and JavaScript in Figure 32.1. Any plug-in to JavaScript communication must go through Java. To learn more about LiveConnect, make sure to get the latest Software Development Kit (SDK) from Netscape and read the documentation. The Netscape SDK has the most up-to-date LiveConnect information.

Runtime Loading

When Navigator initializes itself, it checks for installed plug-ins in the PLUGINS subdirectory or folder. Navigator does not load any plug-ins at this time; it just parses out the resource information containing the plug-ins' supported MIME types, file extensions, and the name for the file-open dialog. At the time of this writing, Macintosh plug-ins enable a user to configure plug-in/MIME type association, while Windows does not. Navigator release 4.0 for Windows is slated to have this feature, according to Netscape sources.

Navigator beta 4 for Windows contains an updated version of the Help | About Plug-ins menu item. The About Plug-ins feature tells you what plug-in you have installed, the full path of each plug-in and its MIME type, its description, its suffixes, and whether it is enabled. Only after Navigator finds a MIME type to which a plug-in is registered is the plug-in loaded into memory and executed. Figure 32.2 shows the Windows Navigator beta 4 About Plug-ins screen.

Figure 32.2 : Windows Navigator beta 4 About Plug-ins screen.

Plug-In Instances and Instance Data

An important feature of a plug-in is that it can be loaded multiple times, depending on a given Web page. For example, a Web page might contain three audio files of the same type. Navigator then creates three instances of the audio plug-in using the NPP_New API. Each of these instances has its own instance data that is allocated by the plug-in and attached to a Navigator-maintained instance data pointer. Each time Navigator calls one of your plug-in methods, you must dereference the Navigator-provided instance pointer and get access to your instance data. All of the examples found on the CD-ROM included with Programming Netscape Plug-ins show how to do this.

In many cases, your plug-in code can be oblivious to other instances. However, in some cases, a plug-in needs to communicate among its instances. The RealAudio plug-in does so by enabling a Web author to load a console instance to control other instances associated with audio files. You can read more about the RealAudio plug-in in Chapter 5of Programming Netscape Plug-ins, "Using a Plug-In."

Windows and Events

Navigator creates a window for your plug-in and gives you a handle to this window during a call to the NPP_SetWindow method. The HTML EMBED tag attributes WIDTH and HEIGHT determine the size of this window. The HTML code also determines the relative window position. Each operating system (UNIX, Windows, and Macintosh) handles window and event processing a little differently.

In Windows and UNIX, a child window is created in the window hierarchy. This child window automatically sees all window events. In the Windows programming environment, you can subclass the Navigator-created window to process any given window event.

In the Macintosh environment, the plug-in and the Navigator share a Macintosh window. Therefore, the plug-in must draw only in the specified area of this shared window. You must save, set up, and restore the shared drawing environment around any drawing operations. Events are sent to the Macintosh plug-in with the plug-in provided with the NPP_HandleEvent API.

Seamless Web Page Embedding

The big advantage of a Navigator plug-in over a Navigator helper application is that a plug-in can embed itself directly in the Navigator-displayed Web page. This feature is similar to the way that a Java applet provides seamless Web page integration. A plug-in differs from Java in that the plug-in's code is native to the local machine and is installed. A Java applet is downloaded before each use and is platform independent.

A plug-in can run in either embedded, full page, or hidden modes. Of these modes, embedded is used most often because it provides seamless Web page integration. Full page mode enables a plug-in to run by itself, taking the whole Navigator display area. Hidden mode, as the name implies, is for plug-ins that have no visible attributes. An example of a hidden plug-in is the Background Musical Instrument Device Interface (MIDI) Player sample code found in Programming Netscape Plug-ins, Chapter 21.

Data Streams

Netscape Navigator plug-ins are built around the concept of client/server data streaming. A stream is a constant flow of data. One of the first implementations of streams was for the UNIX I/O subsystem, which provided a full-duplex, modular connection to a given device driver. As multimedia becomes more prevalent on the home computer, streaming becomes very important to handle data flow for audio and video.

A simple stream can be implemented with a pool of fixed-length buffers. The producer of data continuously fills empty buffers, buffer order is maintained, and data is delivered to the consumer. Such problems as overruns (no more empty buffers) or underruns (no more full buffers) make the life of the stream more complicated.

The Navigator plug-in API provides many methods for managing data streams both to and from a plug-in. A stream that sends data from Navigator to a plug-in (the most common type of stream) uses NPP_NewStream to create it, NPP_Write to write the data, and NPP_DestroyStream to destroy it. A stream that sends data from a plug-in to Navigator (new with the Navigator 3.0 plug-in API) uses NPN_NewStream for creation, NPN_Write to write data to the stream, and NPN_DestroyStream to destroy it. Additionally, your plug-in can have multiple incoming and outgoing streams running concurrently.

NOTE
For further reading, see Chapter 10, "Stream Creation and Destruction," and Chapter 11, "Reading From and Writing to Streams," in Programming Netscape Plug-ins. Be sure to look at the code samples in Chapter 17, "A Streaming Audio Sample," and Chapter 18, "The Buffer Classes," to see how your plug-in can handle a real-time data stream from Navigator.

Sequential and Seekable Streams

After a plug-in is loaded, for all intents and purposes it's part of the Navigator client. A plug-in is compiled machine code and requires no interpretation, in contrast to platform-independent languages such as Java that do require interpretation. Tight coupling enables a plug-in to maintain a very high-speed bond to the Navigator client and, in turn, to the Web server. Data flows between the plug-in and Navigator in either sequential or seekable streams.

A plug-in can change the stream type to seekable by setting *stype to NP_SEEK. Setting this mode enables a plug-in to pull data from a server with calls to the NPN_RequestRead method. This technique puts the onus on a plug-in to continuously call Navigator for data buffers and is not considered a true continuously flowing stream. A seekable stream is generally slower than a sequential stream, which is driven by Navigator with calls to the plug-in implemented method NPP_Write. Your plug-in should use only seekable streams when the random access benefits outweigh the performance penalty.

When Navigator creates a stream for your plug-in, it is in sequential mode. In most cases, Navigator creates a sequential type stream for each plug-in instance. The NPP_Write API automatically notifies your plug-in as chunks of data come across the net. This data flow can come from the Internet via TCP/IP, a local area network (LAN), a local client file, or Navigator's file cache. A sequential stream, as the name implies, is a continuous sequential stream of data true to the streaming definition. In most cases, a sequential stream is the preferred stream type for a plug-in to use.

NOTE
Navigator's cache plays an important role in your plug-in's performance. Netscape Navigator 2.0 has both a memory-based and a disk-based caching system. The caching scheme is based on whole files. If you abort a file download, it is not saved to cache. Both cache sizes are user configurable.
Navigator 3.0 introduced LiveCache. LiveCache provides progressive caching, which enables you to continue where you left off if a file download is aborted. LiveCache also enables you to preload content from slow devices such as CD-ROMs for fast access and viewing.

Assisted Installation

A common problem with plug-ins is that users don't know where to find them. To view a Web page that needs a specific plug-in, the user must have that plug-in installed on his or her local machine. Navigator's assisted installation feature helps users install new plug-ins. This feature is automatically activated when a user displays an HTML page that requires a plug-in that is not currently installed.

When a user hits a Web page that requires a plug-in not found in that user's current plug-in installation, a dialog box opens and enables the user to select either Plug-in Info or Cancel. If the user clicks the Plug-in Info button, Navigator loads a new URL to get the given plug-in. This URL can be specified in the PLUGINSPACE attribute of the EMBED tag. If the tag does not contain the PLUGINSPACE attribute, Navigator goes to a current plug-in list.

Netscape's SDK

You should download Netscape's Plug-in SDK to use in conjunction with this book. The SDK contains the authoritative and most up-to-date plug-in documentation. This SDK is currently located at the following FTP site:


ftp://ftpXX.netscape.com/pub/navigator/sdk

In this address ftpXX is any one of Netscape's 20 or so FTP servers.

The Plug-in SDK provides the following benefits:

Navigator Plug-In Design Considerations

Before you jump into the wonderful world of Netscape Navigator plug-in development, you need to do some planning. The tools you use, cross-platform compatibility, and the performance will affect your plug-in's success in the quickly growing plug-in market. As you design your Netscape Navigator plug-in, consider the following factors:

The following discussion will help you plan your plug-ins.

Choosing a Development Language

Unlike a Java applet, a plug-in is native machine code and is not interpreted, which means that you can write a plug-in in any language that compiles to native machine code. However, some issues make certain languages easier to use than others.

For example, in Windows a plug-in must be a dynamic link library (DLL). The development language you use must be capable of generating a DLL code module to build a stand-alone plug-in. Many languages such as Microsoft's Visual Basic and Borland's Delphi are quite capable of generating Windows DLLs, but these languages might not be able to generate the special resources required for plug-ins. You might be better off using one of these high-level languages to generate an OLE library for use with a third-party plug-in, such as ActiveX from NCompass or OpenScape from BusinessWeb.

The best documented language for Navigator plug-in development is C++. Programming Netscape Plug-ins, along with Netscape's Plug-in SDK, uses C++ exclusively. In addition, you can use assembler inline or in separate routines. In some cases, such as software decompression, an assembler routine might provide the performance you need. And don't hesitate to consider other possibilities, such as a hybrid solution using C++ to call routines implemented in other languages.

Navigator Version Compatibility

Which versions of Netscape's Navigator should your plug-in support? Navigator 2.x introduced plug-in support, and Navigator 3.x added many new and cool features such as LiveConnect and streaming data from a plug-in to the Navigator. Can you design your plug-in so that it works with both Navigator 2.x and Navigator 3.x? What about Navigator 4.x? Your plug-in can also "turn on" more features such as streaming data from the plug-in to the Navigator, depending on the Navigator version under which it is running.

Be sure to check out the NPN_Version API for determining the current Navigator version. Also keep in mind that the version number returned by NPN_Version corresponds to the plug-in API it supports, not to the Navigator version (such as 2.0, 3.0, and so on). You can read more about version support in Chapter 9 of Programming Netscape Plug-ins.

Planning for Bandwidth Limitations

What about network bandwidth? Is your plug-in geared toward Internet usage for cases in which a typical Internet Service Provider (ISP) is connecting users at 14.4Kbps or 28.8Kbps? Or, perhaps you are developing a plug-in for your company's local intranet. Intranet plug-ins, used mostly on local area networks, need not worry about slow modem connections. Multimedia audio and video formats have huge bandwidth requirements. Most of today's multimedia data throughput minimums were defined by CD-ROM speeds, not by comparatively slow Internet connections. Data compression for audio and video media types has never been more important.

Streaming Audio

Now look at audio and how you can stream it in real time. A common modem speed today is 14,400 bits per second. Low-quality audio is generally sampled at 11,025 samples per second, using 1 byte per sample. A speed of 11,025 samples per second is 11,025 bytes per second, which means 88,200 bits per second (bps):


11,025 bytes/second * 8 bits/byte = 88,200 bits/second

How are you going to fit 88,200bps through a 14,400bps Internet connection? The answer, of course, is data compression.

Using the preceding numbers, a 7 to 1 compression ratio would squeak you by.


88,200bps / 14,400bps ~

Some of today's plug-in vendors are claiming compression ratios as high as 50 to 1! This more than ample compression is obtained by using lossy compression, or removing some data, such as dead space between words. In fact, much of what you see on the Web today uses lossy compression, such as JPEG, AVI video, and audio compression.

Remember that the preceding numbers were taken from a perfect world. You won't get 14,400bps from a 14.4Kbps modem. Other factors such as TCP/IP error correction, the speed of your computer, and server load also play an important part in actual throughput.

Multiplatform Compatibility

Multiplatform compatibility is a big design consideration that determines your plug-in's marketability. Although this chapter is geared toward Windows plug-ins, Netscape's plug-in API was designed for platform independence. You will see how Netscape's design is portable later in this chapter. The Netscape development staff worked hard to keep the API set consistent across the Macintosh, UNIX, and Windows platforms. Rumors that Navigator will support even more platforms (such as OS/2) are now coming across the Internet.

If you are a Windows developer, don't be too quick to rule out a version of your plug-in for the Macintosh and UNIX platforms. Try to develop your plug-in with an eye toward other platforms. Consider using Java in conjunction with your plug-in for a user interface. Tight integration between Java and the plug-in comes with LiveConnect. You can read more about LiveConnect in Chapter 14 of Programming Netscape Plug-ins. You should use native operating system level APIs only when you have no other recourse.

Expanding To Java with LiveConnect

Netscape introduced LiveConnect with the release of Navigator 3.0. LiveConnect enables Navigator plug-ins, Java, and JavaScript to communicate with each other. How can this technology benefit your development efforts? Does it make sense to add a Java extension to your plug-in, or are you doing it strictly for the hype value?

Using Java in conjunction with a plug-in can certainly reduce your development efforts. The most dramatic reduction occurs with a Java/plug-in applet that uses a plug-in for platform-specific duties and Java for an interactive user interface. In that case, your only job is to port the hardware-dependent plug-in; you don't have to touch the user interface written in Java.

Netscape's LiveConnect AVI video player is a simple example of using Java and JavaScript for the user interface and leaving the video implementation to a plug-in. The LiveConnect video player is documented in Chapter 23 of Programming Netscape Plug-ins.

HTML EMBED Tag Attributes

The EMBED tag in the Web page's HTML code starts an embedded plug-in. When the plug-in is loaded, it is displayed as part of the HTML document, rather than within another window. This technique is very similar to how graphics are embedded in a Web page. As you design your plug-in, you should think about which EMBED tag attributes your plug-in will support. Additionally, define new attributes that are specific to your plug-in.

Current Attributes

The next few sections document Netscape's current EMBED tag attributes. The EMBED tag has the following syntax:


<EMBED attributes> ... </EMBED>

HEIGHT="value"

Example:


HEIGHT=50

The HEIGHT attribute defines the vertical size of the plug-in window in units defined by the UNITS attribute. Default UNITS are pixels.

HIDDEN=true or HIDDEN=false

Example:


HIDDEN=true

The HIDDEN attribute determines whether the plug-in is visible. A value of true indicates that the plug-in is hidden and not visible. This value overrides any HEIGHT or WIDTH parameters and makes the plug-in zero in size. An example of a hidden plug-in is a Web page background MIDI player (a sample in Programming Netscape Plug-ins). Be sure to use the HIDDEN attribute to define an invisible plug-in rather than defining HEIGHT and WIDTH to zero. If you use the HIDDEN attribute with no parameters, it defaults to true.

PALETTE=foreground or PALETTE=background

Example:


PALETTE=background

The PALETTE attribute is for the Windows platform. This attribute instructs the plug-in to realize its palette as either a foreground or background palette. The attribute is useful for embedding multiple palette-aware plug-ins in a single page. The default value is background.

PLUGINSPAGE="URL"

Example:


PLUGINSPAGE=http://www.yourcompany.com

The PLUGINSPAGE attribute is used by the assisted installation feature if the plug-in registered for the MIME type of a given EMBED tag is not found. Its argument is a standard URL that usually contains the location of the needed plug-in.

NOTE
The PLUGINSPACE attribute is not implemented in Navigator 2.x.

SRC="URL"

Example:


SRC=sound.wav

The HTML argument of the SRC attribute indicates the location of a plug-in's data file. The MIME type of this data file determines which plug-in is loaded to handle the file, and the file's extension usually determines the MIME type. The EMBED tag must use either the SRC or TYPE attribute.

TYPE="type"

Example:


TYPE=audio/x-wav

The TYPE attribute is used instead of SRC to load a plug-in that does not require a data file for startup. The argument for this attribute is a MIME type that maps to a plug-in. The EMBED tag must use either the SRC or TYPE attribute.

WIDTH="value"

Example:


WIDTH=200

The WIDTH attribute defines the horizontal size of the plug-in window in units defined by the UNITS attribute. Default UNITS are pixels.

UNITS="value"

Example:


UNITS=en

The UNITS attribute defines which measurement units the HEIGHT and WIDTH attributes use. The value can be either pixels or en. Pixels are the default. (An en is half the point size.)

Adding Your Own Attributes

Adding attributes for your plug-in's private use is easy. Just put them in the EMBED command line. Navigator ignores all nonstandard attributes while parsing the HTML EMBED tag. You can retrieve any additional name=value pairs during your plug-in's NPP_New API.

For example, many plug-ins use the AUTOSTART and LOOP attributes, which you can add to the EMBED tag such as the following:


<EMBED SRC="video.avi" WIDTH=320 HEIGHT=200 LOOP=true AUTOSTART=true>

Notice how you can put additional private attributes in the EMBED tag command line just like standard attributes. For an example of extensive use of private attributes, check Chapter 5in Programming Netscape Plug-ins, "Using a Plug-in," which shows how the RealAudio plug-in uses this technique.

Multiple MIME Types

Your plug-in can support more than one MIME type. This feature can be really handy for plug-ins that support more than one file format. Consider Netscape's LiveAudio plug-in, which is included with Navigator 3.x. This plug-in currently supports seven different MIME types! It covers the most popular audio formats for UNIX, Macintosh, and Windows. Table 32.1 shows the MIME types, descriptions, and suffixes that the LiveAudio plug-in supports.

Table 32.1. LiveAudio MIME types.

Mime typeDescription Suffixes
audio/basiclAU au
audio/x-aiffAIFF aiff, aif
audio/aiffAIFF aiff, aif
audio/x-wavWAV wav
audio/wavWAV wav
audio/x-midiMIDI midi, mid
audio/midiMIDI midi, mid

MIME Contention

MIME contention occurs when more than one installed plug-in supports the same MIME type. The Macintosh Navigator has solved this problem by letting the user configure appropriate plug-ins for each MIME type. At the time of this writing, Windows and UNIX do not yet have this plug-in user configuration.

When designing your plug-in, you should be aware of these MIME contention issues. The best defense, until Netscape resolves this problem, is a good installation program and user documentation.

File Base Versus Streaming Plug-Ins

Try to design for a streaming plug-in rather than a file-based plug-in whenever possible. A streaming plug-in processes data on a buffer-by-buffer basis as it is downloaded from the network. The advantages of using this design are twofold. First, your plug-in can operate in real time, such as the RealAudio plug-in. And second, your plug-in can take advantage of extra processing time while the data file is being downloaded.

Despite the popularity of the streaming plug-in design, in many cases using this design does not make sense. Consider a plug-in that plays AVI video files. Because the AVI format was designed for CD-ROM streaming at 150KB per second (not your everyday Internet connection speed), you cannot possibly play this file format in real time over the Internet. As you will see with Netscape's AVI player plug-in example, this type of plug-in has to be file-based.

Streaming to the Navigator

Navigator 3.x enables you to stream data from your plug-in to Navigator by creating a new stream with NPN_NewStream, writing to it with NPN_Write, and destroying it with NPN_DestroyStream. These APIs are fully documented in Programming Netscape Plug-ins, Chap-ter 10.

Why would you ever want to stream data from your plug-in to Navigator? Maybe you could create a plug-in that reads raw data from a Web server, converts it to HTML, and streams that HTML data to the Navigator for display. For example, you could write a plug-in that displays UNIX manual pages in a nice HTML format.

Your plug-in can make Web pages on-the-fly using this technique.

Client Server Design

With the addition of a common gateway interface (CGI) program residing on the Web server, your plug-in implementation can have a true client/server design. Your CGI server back end can do things such as database searches while the browser client handles the display.

You can send data to your CGI program with NPN_GetURL or NPN_PostURL. Navigator 3.x adds NPN_GetURLNotify, NPN_PostURLNotify, and NPP_URLNotify to the mix for better error checking. Be sure to check out the Server CPU Monitor sample in Programming Netscape Plug-ins for an example of using these APIs with a CGI program.

The Plug-In API

In order to maintain similarity across multiple platforms, the Windows plug-in API is not built in the usual Windows fashion. A Netscape Navigator plug-in is implemented in a dynamically linked code module. In Windows, this module is the standard DLL. Microsoft designed the DLL with an eye toward implementations, such as a Netscape plug-in, but Netscape has taken a slightly different approach.

This section explains the difference between a Netscape API and a plug-in API. It includes implementations for both types of APIs and how code is called. Additionally, the section briefly explains all APIs (including platform-specific APIs). A complete reference of these APIs, reviewed by Netscape, is available in Programming Netscape Plug-ins.

Who Is Calling Whom?

If you scan through the plug-in documentation, you might notice two types of APIs. The first type begins with the convention NPP. These routines are implemented by your plug-in and are called from the Navigator. The letters NPP stand for Netscape Plug-in: Plug-in Defined. The second type begins with the convention NPN. These routines are implemented by the Navigator and are called from your plug-in. The letters NPN stand for Netscape Plug-in: Navigator Defined. Figure 32.3 shows the calling direction for NPN and NPP plug-in APIs.

Figure 32.3 : API calling techniques.

Dynamically Loaded

All plug-in types-UNIX, Macintosh, or Windows 3.1/95/NT-are dynamically loaded code modules. This architecture calls code on demand and over the years has proved to be an effective technique to save system resources. As a Windows developer, you are probably familiar with Windows DLLs, and you might have used them in your projects.

Entry points defined during compile time call routines within a DLL. An application might implicitly load a DLL by linking to a stub library during compile time or explicitly load a DLL by using a Windows API, such as LoadLibrary, followed with calls to GetProcAddress to retrieve function addresses.

How Netscape Calls Your DLL

Unfortunately, these linking methods are very specific to Windows and do not fit Netscape's criteria for cross-platform equivalence. If you look at the header of a typical plug-in DLL, you'll notice only three entry points:


NP_GETENTRYPOINTS

NP_SHUTDOWN

NP_INITIALIZE

That design certainly doesn't give Netscape much to work with-or does it?

NPWIN.CPP

Netscape provides a file called NPWIN.CPP for Windows developers in its plug-in developer's kit. This file contains code for Windows DLL entry points. You might want to review this file before proceeding with this chapter.

Because NPWIN.CPP is intended as a layer to hide Windows platform-specific entry points from your plug-in, Netscape asks developers not to touch this file. To ensure that no one changes this code, Netscape has mentioned that it might convert NPWIN.CPP to a binary library in the future.

NP_Initialize

Immediately after the plug-in is loaded, the DLL routine NP_Initialize is called. This routine has one parameter-a pointer to the NPNetscapeFuncs structure:


NPError WINAPI NP_EXPORT NP_Initialize (NPNetscapeFuncs* pFuncs)

The NPNetscapeFuncs structure, which is located in the Netscape-provided header file npupp.h, is defined in the Navigator 3.0 Plug-in SDK as follows:


typedef struct _NPNetscapeFuncs {

    uint16 size;

    uint16 version;

    NPN_GetURLUPP geturl;

    NPN_PostURLUPP posturl;

    NPN_RequestReadUPP requestread;

    NPN_NewStreamUPP newstream;

    NPN_WriteUPP write;

    NPN_DestroyStreamUPP destroystream;

    NPN_StatusUPP status;

    NPN_UserAgentUPP uagent;

    NPN_MemAllocUPP memalloc;

    NPN_MemFreeUPP memfree;

    NPN_MemFlushUPP memflush;

    NPN_ReloadPluginsUPP reloadplugins;

    NPN_GetJavaEnvUPP getJavaEnv;

    NPN_GetJavaPeerUPP getJavaPeer;

    NPN_GetURLNotifyUPP geturlnotify;

    NPN_PostURLNotifyUPP posturlnotify;

#ifdef XP_UNIX

    NPN_GetValueUPP getvalue;

#endif /* XP_UNIX */

} NPNetscapeFuncs;

The first things to notice are the structure members size and version. The size is simply a sizeof NPNetscapeFuncs. During NP_Initialize, the size is checked against your plug-in's internal NPNetscapeFuncs structure to assure compatibility:


if(pFuncs->size < sizeof NPNetscapeFuncs)

     return NPERR_INVALID_FUNCTABLE_ERROR;

The version is checked in a similar fashion:


if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)

     return NPERR_INCOMPATIBLE_VERSION_ERROR;

NOTE
The version numbers indicated by NP_VERSION_MAJOR and NP_VERSION_MINOR can't be directly correlated to the version of the Navigator. These values refer to the version of the API. When the major version number of the API increases, it is a breaking change. Plug-ins written for version 0 of the API (the current major version number) won't work if the API in the Navigator progresses to major version 1. Minor version changes indicate nonbreaking changes in the API. Thus, the major version number returned by Atlas (Navigator 3.x) continues to be 0, while the minor version is incremented with each change to the API. NP_VERSION_MAJOR and NP_VERSION_MINOR are predefined in the header file, so those numbers indicate the version of the API against which the plug-in was compiled

Further down in the structure, notice the 12 function pointer prototypes beginning with NPN. You remember that NPN stands for a routine within the Navigator. This structure holds function pointers to all Navigator entry points that your plug-in calls. In addition, your plug-in maintains a global pointer to this structure for future calls to the Navigator.


g_pNavigatorFuncs = pFuncs; // save it for future reference

The NP_Initialize routine ends with a call to your internal NPP_Initialize method, which is documented in Chapter 9 of Programming Netscape Plug-ins.

Mapping Your API Calls to Netscape

As your plug-in is running, it makes calls to Navigator. For instance, a call to allocate memory from Navigator is NPN_MemAlloc. (See Programming Netscape Plug-ins, Chapter 12, "Memory Management.") NPN_MemAlloc is really a routine within your plug-in. Look again in the file NPWIN.CPP and find the following routine:


void* NPN_MemAlloc(uint32 size)

{

    return g_pNavigatorFuncs->memalloc(size);

}

Notice that this routine simply maps your call to NPN_MemAlloc to the previously mentioned structure of function pointers NPNetscapeFuncs and uses calls the saved pointer g_pNavigatorFuncs to call the routine memalloc.

NP_GetEntryPoints

After NP_Initialize is called and returns successfully, the DLL entry point routine NP_GetEntryPoints is called. Just as NP_Initialize gives you the means to call Navigator routines, NP_GetEntryPoints enables Netscape to call your plug-in's routines without using standard DLL calling conventions. Look at the prototype for this routine:


NPError WINAPI NP_EXPORT NP_GetEntryPoints (NPPluginFuncs* pFuncs)

As with NP_Initialize, this routine passes a pointer to a structure. In this case, the pointer is to the structure NPPluginFuncs, which is currently defined as follows:


typedef struct _NPPluginFuncs {

    uint16 size;

    uint16 version;

    NPP_NewUPP newp;

    NPP_DestroyUPP destroy;

    NPP_SetWindowUPP setwindow;

    NPP_NewStreamUPP newstream;

    NPP_DestroyStreamUPP destroystream;

    NPP_StreamAsFileUPP asfile;

    NPP_WriteReadyUPP writeready;

    NPP_WriteUPP write;

    NPP_PrintUPP print;

    NPP_HandleEventUPP event;

    NPP_URLNotifyUPP urlnotify;

    JRIGlobalRef javaClass;

} NPPluginFuncs;

Again, the first two members size and version are provided for compatibility checking. In this case, your plug-in only checks the structure size because the version refers to the plug-in:


if (pFuncs->size < sizeof NPPluginFuncs)

    return NPERR_INVALID_FUNCTABLE_ERROR;

Later in the structure, notice the function pointers and prototypes to routines such as NPP_NewUPP newp. The prototype for these routines begins with NPP, which identifies them as calls from the Navigator to your plug-in's methods.

The plug-in fills the function pointers in this structure with the appropriate internal routines during NP_GetEntryPoints:


pFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;

pFuncs->newp          = NPP_New;

pFuncs->destroy       = NPP_Destroy;

pFuncs->setwindow     = NPP_SetWindow;

pFuncs->newstream     = NPP_NewStream;

pFuncs->destroystream = NPP_DestroyStream;

pFuncs->asfile        = NPP_StreamAsFile;

pFuncs->writeready    = NPP_WriteReady;

pFuncs->write         = NPP_Write;

pFuncs->print         = NPP_Print;

pFuncs->event         = NULL;       /* reserved */

Versions NP_VERSION_MAJOR and NP_VERSION_MINOR are defined in NPAPI.H and indicate the plug-in API release and point version, respectively.

Netscape Calls Your Code

After NP_GetEntryPoints returns, the Navigator structure NPPluginFuncs is filled with valid function pointers that will be directly called as needed. If you are using a compiler other than Microsoft's, you should make sure that your plug-in APIs are prototyped correctly for your compiler. See Part IV of Programming Netscape Plug-ins, "Plug-in Programming Resources for Windows," for more information.

Because the plug-in is running, you no longer need to use the standard Windows DLL interface. The Navigator doesn't use any other DLL entry points until it unloads your plug-in from memory. At this point, NP_Shutdown is called.

NP_Shutdown

The last of the Windows DLL entry points, NP_Shutdown, is called immediately before the plug-in is unloaded. It simply calls your internal NPP_Shutdown routine (see Chapter 9 in Programming Netscape Plug-ins), in addition to zeroing out the global functions pointer.


NPError WINAPI NP_EXPORT NP_Shutdown()

{

    NPP_Shutdown();



    g_pNavigatorFuncs = NULL;



    return NPERR_NO_ERROR;

}

A Quick Look at the Plug-In API Methods

For a complete reference of Navigator's plug-in API methods, consult either Netscape's Plug-in SDK or Programming Netscape Plug-ins. Table 32.2 shows plug-in-implemented APIs, and Table 32.3 shows Netscape-implemented APIs.

Table 32.2. The plug-in APIs, which are called from Netscape.

API NameDescription
NPP_DestroyDeletes an instance of a plug-in
NPP_DestroyStreamCalled when a data stream is complete
NPP_GetJavaClassReturns the plug-in associated Java class
NPP_HandleEventMacintosh-only event handler
NPP_InitializeGlobal initialization
NPP_NewCreates a new instance of a plug-in
NPP_NewStreamCalled when a new stream has been created
NPP_PrintPrint handler
NPP_SetWindowCalled during plug-in's window activity
NPP_ShutdownGlobal termination
NPP_StreamAsFileGives the filename for the stream
NPP_URLNotifyNotifies the completion of a URL request
NPP_WriteCalled to write data to a plug-in
NPP_WriteReadyDetermines whether a plug-in is ready for data

Table 32.3. The Netscape APIs, which are called from the plug-in.

API NameDescription
NPN_DestroyStreamTerminates a data stream
NPN_GetJavaEnvReturns the Java execution environment
NPN_GetJavaPeerReturns the plug-in associated Java object
NPN_GetURLRequests that a new stream be created
NPN_GetURLNotifyRequests that a new stream be created with notification
NPN_MemAllocAllocates memory
NPN_MemFlushMacintosh-only flush memory
NPN_MemFreeFrees memory
NPN_NewStreamCreates a new stream of data
NPN_PostURLPosts data to a URL
NPN_PostURLNotifyPosts data to a URL and notifies of result
NPN_RequestReadRequests bytes from a stream
NPN_StatusDisplays a status message
NPN_UserAgentGets Navigator's user agent field
NPN_VersionGets Navigator's plug-in version
NPN_WriteWrites to a stream

Summary

Throughout this chapter you learned about Netscape Navigator's plug-in architecture, plug-in design, and plug-in APIs. Plug-ins are dynamically loaded code modules. A plug-in that is loaded more than once is considered to have multiple instances. Navigator creates a child window in UNIX and Windows, but it shares the main window with a Macintosh plug-in.

A Windows DLL is not called in the traditional DLL entry point fashion, but rather through direct function pointers retrieved by Navigator. The Netscape SDK file NPUPP.H contains structures to accomplish this calling mechanism. Calls from your plug-in to Navigator, although more straightforward, use the same technique with function pointers.

This chapter also suggested ways to design plug-ins. Among the many factors to consider when you design and develop plug-ins are Navigator compatibility, development languages, bandwidth requirements, LiveConnect, EMBED attributes, and MIME types. Remember, streaming plug-ins are generally considered much cooler than file-based plug-ins. Also, you can use a CGI program to provide server software as needed. For further reading, Programming Netscape Plug-ins, also published by Sams.net, is an excellent resource for more in-depth information on topics covered in this chapter.