NetPositive Plug-in API Specification

Version 1 (July 31, 1999)
Copyright 1999 Be Incorporated

Table of Contents


Introduction

Web browser plug-ins were originally conceived as a way of allowing Web browsers to seamlessly handle types of data that they weren't natively designed for, such as multimedia, interactive animations, and the like. In the early days of the Web, there was an explosion of plug-ins, as companies raced to catch the burgeoning WWW hype by writing all sorts of plug-in applications whose interface could be embedded into an HTML page.

However, the strategy of writing custom plug-ins to do specific tasks soon lost favor, partly because users were unwilling to hunt down and install dozens of plug-ins to do basic web browsing, and partly because the advent of Java, JavaScript, and DHML made "normal" web pages more powerful and custom plug-ins less necessary.

Today, plug-ins fulfill their original role: providing integrated access to a handful of important data types that browsers don't natively support. The important ones are RealAudio/RealVideo, QuickTime, and Shockwave/Flash. The fact that this list is small does not belie the importance of these data types; rather, support for them is critically important for a good web browsing experience.

This API specification makes frequent reference to the Netscape plug-in API, mostly to point out the similarities and differences for those developers who are familiar with plug-ins on other browsers and want to make a quick transition to the NetPositive model. Knowledge of the Netscape API is not essential to understanding NetPositive plug-ins, so detailed Netscape API information is not included here. If you're interested in learning more about Netscape plug-ins, look in the developer section of their website for more information.


NetPositive plug-ins

The primary goal of the NetPositive plug-in API is to provide the functionality that will allow plug-ins similar to other platforms to be brought to the BeOS and NetPositive. However, with the NetPositive plug-in API, Be has a chance to expand the role of plug-ins to encompass other sorts of web-related functions that aren't served by a data viewer paradigm. These funcitons include (parental) content and access controls, content filtering and manipulation, and the ability to customize the browser by bypassing some of its component parts. Most of these new sorts of functions aren't part of the present plug-in API, but are planned for the future.

The plug-in API for NetPositive follows a design philosophy that differs from the one that guided the Netscape API, which is the standard for all browsers on all different platforms. This is both a blessing and a curse. The Netscape API includes many functions designed to shield the plug-in API from the vagaries of the platform it runs on. This makes it easier to write plug-ins with less platform-dependent code, and is necessary on some platforms because their native API's are hostile to the concept of having different regions of a window under control of different programs.

What this means is that the Netscape API has a UI event and messaging model that is more or less common across all platforms that support the API. It also has a set of functions and callbacks that allow for services such as memory management, acquisition of data from the network, and the like. However, it doesn't attempt to eliminate the need for platform-specific code; in particular, there is no abstract drawing model, requiring plug-ins to still use platform-specific code to do a lot of their work.

Since the BeOS API's are much more modern than other platforms, it is possible to design a plug-in API with much less fuss. A NetPositive data-viewing plug-in simply instantiates a self-sufficient BView subclass which can do its own drawing and handle its own events using the same BeOS Kit API's that other applications use. Plug-ins have full access to the system, and do so directly instead of going through a NetPositive abstraction layer. NetPositive will provide services to plug-ins, such as the fetching of data from the network, where it makes sense to do so. These communications all take place through a BMessenger/BMessage API, with no direct C++ function calls.

While this approach makes it slightly more difficult to port plug-ins from other platforms to work with NetPositive under the BeOS, this disadvantage is made up by the fact that it is vastly easier to retrofit other BeOS applications (particularly those that can function as replicants) to work as plug-ins, because the set of new API requirements is kept to a minimum. The fact that all communications between browser and plug-in happen through BMessages and not through direct C++ function calls makes everything work better in a multithreaded, multiprocessor environment, and provides maximum flexibility to expand the API in the future without worrying about breaking existing code.

In particular, the NetPositive plug-in API has few requirements that force the plug-in to even be running in NetPositive's team, aside from the fact that current BeOS limitations require the plug-in's BView to share NetPositive's address space. In the future, plug-ins may live in their own protected memory space, which will protect the browser if a buggy plug-in crashes.

A single plug-in can support multiple plug-in types and multiple data types. The browser queries the plug-in soon after it is initialized to determine what its capabilities are.

This API specification and many of the names of entities within refer to NetPositive. This is not to say that the API is specific to NetPositive and cannot be implemented in other browsers. Rather, Be hopes that the other browser implementors on the platform will adapt as much of the API as possible to give browser plug-ins on the BeOS as wide an audience as possible.

Enough introduction. On to the details. Provide a means for a plug-in to create an HTML stream and feed it to the page and have it display the HTML.

Allow plug-ins to just open their own windows at launch. Allow them to add toolbars to NetPositive's window, too. Make the URLView a plug-in and make it replaceable.


Plug-in file format

NetPositive plug-ins must be in the form of executable image files that export the following two functions:

        extern "C" BMessenger *InitBrowserPlugin(BMessage *browserInfo);
        extern "C" void        TerminateBrowserPlugin(BMessenger *pluginInstance);
Depending on what types of plug-in instances are supported, additional functions may need to be exported to actually instantiate the plug-in instances. Your plug-in's InitBrowserPlugin function should perform any initialization required of the plug-in as a whole that is not related to a specific instance of the plug-in in a page. It can use this call to set up any data structures that are shared by all instances of a plug-in. It should return a persistent BMessenger object that the browser can use to communicate with the plug-in as long as it is loaded to ask it to create instances of the plug-in or perform other tasks.

This BMessenger object is NetPositive's sole point of contact with the plug-in as a whole. It is also enables the plug-in to be able to identify the team that loaded the plug-in. This is not an issue at present, because the plug-in is loaded into the browser's team, but if in the future plug-ins run in their own address spaces, and if a running plug-in can service more than one browser team at once, this BMessenger will be used to determine which team is initializing or terminating plug-in services. This function will be called shortly after NetPositive detects the plug-in, usually during application startup.

NetPositive passes in a BMessage which contains information about itself that may be of interest to the plug-in. The message has the following fields:

Talk about negotiation of API versions. NetPositive will fail if the plug-in gives it an API version it can't support. If the plug-in sends a version number back to NetPositive that is less than its current version, it will revert as much as possible to the old way of doing things. Plug-ins should do the same with NetPositive. Nonetheless, plug-ins should ignore parameters in messages they don't understand.

The BMessenger that NetPositive passes in is important. This is the plug-in's contact with the browser application as a whole. If the browser needs to make a request of the browser, it can do so by sending messages to this messenger. This is different from the BMessenger that a plug-in instance can use to communicate with the window it is loaded in; this BMessenger is not tied to a window, but to the browser's team

The browser will call TerminateBrowserPlugin when it is finished with all instances of a plug-in. It will not be called unless all instances of the plug-in have been freed. This call must free any memory or system resources still in use by the plug-in; it is important that plug-ins not leak anything, or the stability and footprint of the browser will be adversely affected. This function will generally be called before NetPositive shuts down.

Other than being an executable exporting these two functions, there is nothing else that earmarks a file as being a browser plug-in; for example, a particular MIME type is not required. This will allow you to have a BeOS application image that can also be invoked as a browser plug-in.

Plug-ins are installed in the B_USER_SETTINGS_DIRECTORY/NetPositive/Plug-ins/ directory; you can either place the executable image there or a symbolic link to the image located somewhere else. When NetPositive launches, it scans this directory for files meeting its plug-in criteria, loads and initializes them, and queries them to determine their capabilities. NetPositive node monitors the Plug-ins directory to allow plug-ins to be added and removed dynamically while the application is still running.


Plug-in-wide functions

Once a plug-in is initialized via the InitBrowserPlugin function call, all of NetPositive's communications with the plug-in as a whole take the form of BMessages passed to the BMessenger that InitBrowserPlugin returns. Following is a list of the messages that NetPositive can send to the plug-in. The message constants for each of these messages can be found in <NetPositive.h>.


Browser -> plug-in messages

B_NETPOSITIVE_GET_PLUGIN_INFO

  • description: Used by the browser to query the plug-in about its name, version number, and general capabilities. Using this call, the browser can determine what the plug-in can support, and gets enough information to ask the plug-in to create instances. Since the response to this message is rather complex, and since it isn't likely to change dynamically, you might wish to pre-create it and archive it into a resource rather than generate it on-the-fly.
  • what: B_NETPOSITIVE_GET_PLUGIN_INFO
  • fields:
    • none
  • response required?: yes
    • what: B_NETPOSITIVE_GET_PLUGIN_INFO
    • fields:
      • PluginName (string)
      • PluginVersion (string)
      • PluginAPISupported (integer)
      • PluginAboutPage (string. A complete HTML page)
      • DataTypePlugins (array of BMessages)
        • what: B_NETPOSITIVE_DATATYPE_PLUGIN
        • fields:
          • MIME type: Array of string
          • Filename extension: Array of string
          • Type description: Array of string
          • ViewArchive (BMessage). This contains the archived BView subclass that provides the plug-in's visual representation on the page. NetPositive will instantiate the instance's BView from the BMessage using the same mechanism used to instantiate replicants; this will require you to export the proper function in your plug-in executable image. If you plug-in is an application which also supports replicants, you may want to include information in the view archive that will let the view know that it is being instantiated as a browser plug-in as opposed to a replicant archive.
      • HTMLFilterPlugins (array of BMessages)
        • what: B_NETPOSITIVE_HTMLFILTER_PLUGIN
        • fields:
          • none
      • URLApprovalPlugins (array of BMessages)
        • what: B_NETPOSITIVE_URLAPPROVAL_PLUGIN
        • fields:
          • none

B_NETPOSITIVE_GET_PLUGIN_PREFERENCES

B_NETPOSITIVE_SET_PLUGIN_PREFERENCES

If your plugin wishes to have preferences that are accessible from the browser, you can set up an HTML preferences page and let NetPositive know about it via the PluginPrefsPage field in your response to
B_NETPOSITIVE_GET_PLUGIN_INFO message. The user can access the preferences by navigating to the netpositive:plugins page and following the link from that page to the preferences page to your plug-in. This will bring up the HTML page that your plug-in supplies. If the user submits the form, the form data will be packaged up and encoded in the usual way, and sent to your plug-in through this message.

If your plug-in can also function as a standalone application, there may be some question as to whether or not the plug-in preferences are the same as the application prefs, or are a super- or sub-set, or are separate. You'll have to answer this question yourself in the way that best makes sense for your particular plug-in.


Plug-in -> browser messages

B_NETPOSITIVE_TERMINATE_PLUGIN

  • description: Used by the plug-in to ask the browser to immediately dispose of all active instances of the plug-in, and to terminate and unload it. The browser is free to ignore this request.
  • what: B_NETPOSITIVE_TERMINATE_PLUGIN
  • fields:
    • none
  • response required?: no

DataType plug-ins

DataType plug-ins are the NetPositive equivalent of "normal" browser plug-ins. They are instantiated via an <EMBED> tag in a page and are called upon to display a specific kind of data specified by MIME type in the tag. They do their work in a rectangular portion of the page (through a BView) and last as long as the page lasts.

Since a DataType plug-in is instantiated as a BView subclass, that means that your plug-in is pretty self-sufficient in terms of receiving UI events and drawing its contents. Also, your plug-in has full access to the BeOS kits for any sort of system access necessary to do its work.

Your plug-in should not make any assumptions about what its parent view or operating environment is. In particular, don't assume that your plug-in is instantiated inside a browser window; it's possible that the HTML page is being viewed inside a NetPositive replicant which is itself instantiated in some other application's window.

NetPositive will provide your browser with a BMessenger object and a suite of messages for communication with the browser; use this messenger for these communication tasks, rather than trying to send BMessages to your view's parent window or to the browser application itself.

The BMessenger that the browser gives your plug-in instance is not the same BMessenger that is given to your plug-in as a whole; do not send instance messages to the browser messenger and vice-versa. The messenger given to your instance represents the page that the instance is loaded in; if you have multiple instances loaded, each will have its own messenger (which may be different across instances depending on whether the instances are in different pages or not). The messenger given to your plug-in as a whole represents the entire browser and is not tied to a specific window.

Since your DataType plug-in exists as a BView inside NetPositive's browser window, there are some good programming practices that you should follow to keep the browser as fast and responsive as possible. These hints apply not only to browser plug-ins, but to BeOS programs in general.

If you're familiar with the Netscape plugin API, you'll know that Netscape differentiates between "windowed" and "windowless" plug-ins. A windowed plug-in is one that owns its own operating-system-dependent equivalent of a BeOS BView, which receives its own events and is largely self-sufficent. A windowless plug-in shares a view with the browser window, and is less self-sufficient -- Netscape's API includes functionality to handle the passing of operating system events to windowless plug-ins. The main advantage of a windowless plug-in is to allow plug-ins to be "transparent", meaning that they can draw only those parts of their content areas that they want to, leaving the browser to fill the rest in with the page background. This allows plug-ins to have the appearance of non-rectangluar or non-contiguous boundaries.

NetPositive does not make a distinction between windowed and windowless plug-ins in its API; all plug-ins have their own BView and are self-sufficient when it comes to drawing and processing events, making them akin to windowed plug-ins. To support the background-drawing facilities of windowless Netscape plug-ins, NetPositive will in the future set the B_DRAW_ON_CHILDREN flag of the parent view, which will effectively draw the page background on all parts of plug-in views not drawn by the plug-ins themselves. (This sort of functionality is not possible on other platforms, which is why windowless plug-ins were invented). For this reason, it is important that your plug-in erase its content rectangle to some color if it does not want the page background drawn there; while B_DRAW_ON_CHILDREN is not currently set and the operating system will erase the content rectangle for you before your BView::Draw method is called, this will change in the future.

Your plug-in BView subclass should not depend on ever being attached to a window (which means that AttachedToWindow() and DetachedFromWindow() will never be called, and that Window() will always return NULL). This is because an HTML page is free to make a plug-in hidden (through use of the HIDDEN attribute in the EMBED tag). For hidden plug-ins, NetPositive creates the plug-in's BView but does not attach it to the window.

Your plug-in should not rely on the Pulse() mechanism to receive regular pulse notifications from the app_server. This is because your plug-in isn't really free to set the pulse rate or turn the pulse mechanism on or off only for itself; this mechanism operates on a whole-window basis, and NetPositive already uses it for purposes of its own. If your plug-in needs to receive regular pulse events, you should probably spawn a separate pulse thread to send regular messages to your view.

If your DataTye plug-in understands a data format that might potentially appear in standalone files as well as embedded in Web pages, it would be a good idea if your plug-in also acted as a regular application that could be used as a standalone viewer for files of that type. Netscape and Internet Explorer have the capability to use plug-ins to use plug-ins to show standalone files by instantiating the plug-in full-sized in a separate window; NetPositive does not do this. If you try to open a file in NetPositive that it doesn't understand, it will ask the OS to find the normal handler application for the file type and launch that application instead.

DataType plug-ins are given a mechanism to store small amounts of arbitrary data in the browser's page history. Each instance, before it is destroyed, can give this data to the browser in the form of a BMessage. If the user returns to the page via the back/forward buttons, then the data is given back to the instances when they are created. If there are multiple plug-in instances on a single page, each one gets separate storage, and NetPositive is careful to give the proper data back to the proper instances.

This mechanism is intended to allow plug-ins to have the same persistence of state that other parts of the HTML page enjoy when the user uses the Back button to go back to a previously visited page: the page is scrolled to the position it was last in, form fields are populated with their previous values, etc. If you have a plug-in that displays a movie, for example, you could remember the last frame that the movie was playing when the user left the page, and pick up where you left off when the user returns.

This mechanism is not intended for long-term persistent storage; this data is not stored in NetPositive's cache or anywhere on disk. It only works for going back/forward, the way that scroll position and form data do for normal HTML pages now.

What do you do about pages that specify windowed plug-ins? You can't launch the application, because you can't pass the tag parameters along.

Talk about how the view gets sized and when during creation


Tips for writing good DataType plug-ins

It is important that your plug-in avoid doing computationally-intensive work when its BView's parent BWindow is locked. While the window is locked, it is unresponsive to user input and cannot redraw itself as necessary. The window can become locked if your view explictly calls BView::Window()->Lock(). The window is also locked for you before your MessageReceived(), Draw, MouseUp/MouseDown/MouseMoved, KeyUp/KeyDown, Pulse, FrameMoved/FrameResized, and other callback virtual functions are invoked. If at all possible, try to prevent doing CPU-intensive work in these functions, and move it into a separate thread. It's a little more difficult, but there's a big payoff because the browser will feel much quicker and more responsive, qualities users expect of all BeOS applications.


Embedding DataType plug-ins

Talk about the Embed and Object tags

Talk about the location field of the tag and how to auto-download and install plug-ins

NetPositive communicates with your instance by sending BMessages to the BView, which will be caught in your override of BView::MessageReceived.


Browser -> instance messages

B_NETPOSTIIVE_INIT_INSTANCE

  • description: This message is sent to the plug-in instance shortly after it has been created and attached to the parent BView. It supplies the instance with the parameters provided in the <EMBED> tag that brought it to life, and gives the instance an opportunity to perform whatever initialization is necessary for it to do its work.
  • what: B_NETPOSTIIVE_INIT_INSTANCE
  • fields:
    • Parameters (BMessage)
      • Attribute (Array of string)
      • Value (Array of string)
    • BrowserMessenger (BMessenger) - A BMessenger that represents the context which this plug-in instance is living in. The instance can make instance-specific requests of the browser by sending messages to this BMessenger.
    • InstanceData (BMessage) - The instance-specific data that was passed to NetPositive by the plug-in instance at the same URL on this page the last time it was viewed. This data comes from the page history. If this page isn't loaded from the history, or if your instance did not supply any data to be stored, this field will not be present.
  • response required?: no

    B_NETPOSITIVE_TERMINATE_INSTANCE