[Contents] [Previous] [Next] [Index]

Chapter 5
Streams

This chapter describes using Plug-in API functions to receive and send streams.

Streams are objects that represent URLs and the data they contain, or data sent by a plug-in without an associated URL. Although a single stream is associated with one specific instance of a plug-in, a plug-in can have more than one stream object per instance. Streams can be produced by Netscape Communicator and consumed by a plug-in instance, or produced by an instance and consumed by Communicator. Each stream has an associated MIME type identifying the format of the data in the stream.

Streams produced by Communicator can be automatically sent to or requested by the plug-in instance. Communicator calls the Plug-in methods NPP_NewStream, NPP_WriteReady, NPP_Write, and NPP_DestroyStream to, respectively, create a stream, find out how much data the plug-in can handle, push data into the stream, and delete it.

The plug-in instance selects a transmission mode for streams produced by Communicator. Stream data can be pushed by Communicator, pulled by the plug-in, or saved to a local file and passed to the plug-in.

Streams sent by the plug-in to Communicator are like normal-mode streams produced by Communicator, but in reverse. In normal-mode streams, Communicator calls the plug-in to tell it when a stream is created and to push more data. In contrast, for streams produced by the plug-in, the plug-in calls the Plug-in API methods NPP_NewStream, NPP_Write, and NPP_DestroyStream to create a stream, push data into it, and delete it.

For a detailed description of the methods discussed in this chapter, see "Stream Methods" in the API reference.

[Top]


Receiving a Stream

When Communicator sends a data stream to the plug-in, it has several tasks to perform:

[Top] [Receiving a Stream]


Telling the Plug-in When a Stream Is Created

To tell the plug-in instance when a new stream is created, Communicator calls the NPP_NewStream method. This method also determine which mode it should use to send data to the plug-in. Communicator can create a stream for several different types of data:

The NPP_NewStream method has the following syntax:

NPError NPP_NewStream(NPP instance, NPMIMEType type,
NPStream *stream, NPBool seekable, uint16* stype);
The instance parameter refers to the plug-in instance receiving the stream; the type parameter represents the stream's MIME type.

The stream parameter is a pointer to the new stream, which is valid until the stream is destroyed.

The seekable parameter specifies whether the stream is seekable (true) or not (false). Seekable streams support random access (for example, local files or HTTP servers that support byte-range requests).

The plug-in can set the output parameter stype to one of these transmission modes:

Once all data in the stream has been written to the plug-in, the stream is destroyed. To do this, either Communicator can call NPP_DestroyStream or the plug-in can call NPN_DestroyStream. This applies to all plug-in modes except NP_SEEK.

NOTE: A plug-in can also use the NPN_GetURL method to request a stream for an arbitrary URL. §
[Top] [Receiving a Stream]


Telling the Plug-in When a Stream Is Deleted

Communicator calls the NPP_DestroyStream method when it completes the stream sent to the plug-in, either successfully or abnormally. Once the plug-in returns from this method, Communicator deletes the NPStream object. The plug-in can terminate the stream itself by calling NPN_DestroyStream.

You should delete any private data allocated in the plug-in's stream->pdata field when you destroy a stream. The plug-in can store private data associated with the stream in stream->pdata. Communicator stores private data in stream->ndata; this value should not be changed by the plug-in.

NPError NPP_DestroyStream(NPP instance, 
NPStream *stream, NPError reason);
The instance parameter is the current plug-in instance; the stream parameter specifies the stream to be deleted.

The reason parameter specifies why the stream was destroyed. It can have one of these values:

[Top] [Receiving a Stream]


Finding Out How Much Data the Plug-in Can Accept

After a call to NPP_NewStream and before writing data to the plug-in, Communicator calls NPP_WriteReady to determine the maximum number of bytes that the plug-in can consume. This function allows Communicator to send only as much data to the instance as it can handle at one time, and it helps both Communicator and the plug-in to use their resources efficiently.

After a call to NPP_NewStream, in which the plug-in requested a normal-mode stream, Communicator delivers the data in the stream progressively in a series of calls to NPP_WriteReady and NPP_Write. Communicator calls NPP_WriteReady before each call to NPP_Write.

The value returned by NPP_WriteReady indicates how many bytes the plug-in instance can accept for this stream. If the plug-in allocates memory for the entire stream at once, it can return a large number. This number tells Communicator that it can pass as much data to the instance as possible in a single call to NPP_Write. Communicator can write a smaller amount of data if desired or necessary (for example, if only 8K of data is available in a network buffer).

For instance, suppose the plug-in allocates, in NPP_NewStream, an 8K buffer to hold the data written from that stream. In the first call, NPP_WriteReady could return 8192, resulting in a call to NPP_Write with a buffer of up to 8K bytes. After this data is copied from Communicator's buffer to the plug-in's buffer, the plug-in begins to process the data asynchronously. At the next NPP_WriteReady call, only half of the data has been processed. To avoid allocating additional buffers, the plug-in could return 4096, resulting in a call to NPP_Write with a buffer of up to 4K bytes.

The buffer passed to NPP_Write may accommodate more bytes than the maximum number returned from NPP_WriteReady. This maximum is only a promise to consume a certain amount of data from the buffer, not an upper limit on the buffer size. In the example above, suppose that the plug-in allocates an 8K buffer and returns 8192 from NPP_WriteReady. If the plug-in gets 10000 bytes from Communicator in a subsequent call to NPP_Write, the plug-in should copy the first 8192 bytes from Communicator's buffer into its own buffer and return 8192 (the number of bytes actually consumed) from NPP_Write.

int32 NPP_WriteReady(NPP instance, NPStream *stream);
The instance parameter is the current plug-in instance; the stream parameter specifies the current stream.

[Top] [Receiving a Stream]


Writing the Stream to the Plug-in

The next step is to write the data to a plug-in from a stream. After a call to NPP_NewStream, in which the plug-in requested a normal-mode stream, Communicator delivers the data in the stream progressively in a series of calls to NPP_WriteReady and NPP_Write.

The NPP_Write function should return the number of bytes consumed by the instance. If this is a negative number, Communicator calls NPP_DestroyStream to destroy the stream. If the number returned is smaller than the size of the buffer, Communicator sends the remaining data in the buffer to the plug-in through repeated calls to NPP_WriteReady and NPP_Write.

int32 NPP_Write(NPP instance, NPStream *stream,
int32 offset, int32 len, void *buf);
The instance parameter is the current plug-in instance; the stream parameter specifies the current stream. The offset parameter specifies the offset, in bytes, of buf from the beginning of the data in the stream. The len parameter specifies the length, in bytes, of buf, the buffer of data (delivered by the stream). The buffer allocated by Communicator is deleted after returning from the function, so the plug-in must make a copy of the data it needs to keep.

As an example, suppose that a plug-in (and the HTTP server) supports byte-range requests, and that Communicator is in the process of pushing data to the plug-in. If the user now requests a specific page of the document, the plug-in calls NPN_RequestRead with a list of byte ranges. The open stream is converted from normal mode to seek mode in an effort to pass the plug-in the data that was already on the way, rather than just discarding it. All NPP_Write calls for streaming data eventually stop, and NPP_Write calls will be completed only for data requested with NPN_RequestRead.

Communicator does not create a new stream for each byte range it requests. Instead, additional NPP_WriteReady and NPP_Write calls occur on the same stream. An individual call to NPN_RequestRead can request discontiguous ranges, and you can have many outstanding NPN_RequestRead calls. There is no guarantee that NPP_Write will receive requests for ranges in the same order as you requested (although this typically is the case; the server controls the order). So, you'll need to pay attention to the offsets as data is being written.

The stream processes all byte-range requests, and then is placed in seek mode (either explicitly in NPP_NewStream, or implicitly by a call to NPN_RequestRead). It remains open until the plug-in closes it by calling NPN_DestroyStream, or until the instance is destroyed.

NOTE: If you want to be sure that the NPN_*Stream functions are called in the order you want and behave the way you expect, combine NPN_NewStream, NPN_Write, and NPN_Destroy_Stream in the same callback. §
[Top] [Receiving a Stream]


Sending the Stream in Random-Access Mode

In random-access mode, the plug-in "pulls" stream data by calling the NPN_RequestRead method. Communicator must download the entire stream to a temporary file before it can be used, unless the stream comes from a local file or an HTTP server that supports the proposed byte-range extension to HTTP. This mode consumes more resources than the others.

Random-access mode is determined in NPP_NewStream by setting the mode NP_SEEK. This mode gives the plug-in instance random access to stream data as needed, through calls to NPN_RequestRead. If the stream is not seekable, these requests are fulfilled only when all the data has been read and stored in the cache.

The NPN_RequestRead method requests a range of bytes from a seekable stream. Typically, the only streams that are seekable are from data that is in memory or on the disk, or from HTTP servers that support byte-range requests.

The NPN_RequestRead method has the following syntax:

NPError NPN_RequestRead(NPStream *stream, NPByteRange *rangeList);
The stream parameter is the stream from which to read bytes; the rangeList parameter specifies the range of bytes in the form of a linked list of NPByteRange objects, which the plug-in must allocate. Because these objects are copied by Communicator, and so the plug-in can delete them as soon as the call to NPN_RequestRead returns.

The plug-in can request multiple ranges, either through a list of NPByteRange objects in a single call to NPN_RequestRead or through multiple calls to NPN_RequestRead. In this case, Communicator can write individual ranges in any order, with any number of NPP_WriteReady and NPP_Write calls.

[Top] [Receiving a Stream]


Sending the Stream in File Mode

If the stream is sent in file mode, Communicator saves the entire stream to a local file and passes the full file path to the plug-in instance through the NPP_StreamAsFile method. Use this feature only as a last resort; plug-ins should implement an incremental stream-based interface whenever possible.

File mode is determined in NPP_NewStream by setting the mode NP_ASFILEONLY. This mode gives the plug-in full random access to the data with platform-specific file operations. Communicator saves stream data to a local file, and, when the stream is complete, delivers the path of the file through a call to NPP_StreamAsFile.

NOTE: Most plug-ins that need the stream saved to a file should use NP_ASFILEONLY mode rather than the older NP_ASFILE; this mode is less efficient because it uses successive calls to NPP_Write. §
NPP_StreamAsFile provides the plug-in with a full path to a local file for the stream. It is a good idea to check that the file exists in the directory at the start of this method. If an error occurs during data retrieval or writing to the file, Communicator passes null for the filename. If the file is created from a stream from the network, the file is locked in the Communicator disk cache until the stream or its instance is destroyed.

void NPP_StreamAsFile(NPP instance, NPStream *stream, 
const char* fname);
The instance parameter is the current plug-in; the stream parameter specifies the current stream. The fname parameter specifies the full path to a local file (or null if an error occurs during data retrieval or writing to the file).

[Top] [Receiving a Stream]


Sending a Stream

When a plug-in sends a data stream to Communicator, it performs several tasks. The plug-in calls the methods NPN_NewStream, NPN_Write, and NPN_DestroyStream to create a stream, push data into it, and delete it. Streams produced by a plug-in have a specific MIME type and can be sent to a particular Communicator window or frame for display.

For an example that demonstrates these processes, see "Example of Sending a Stream."

[Top] [Sending a Stream]


Creating a Stream

The plug-in calls NPN_NewStream to send a new data stream to Communicator. Communicator creates a new NPStream object and returns it to the plug-in as an output parameter.

The plug-in can use this stream object in subsequent NPN_Write calls to Communicator. When all the plug-in data is written into the stream, the plug-in must terminate the stream and deallocate the NPStream object by calling the NPN_DestroyStream function.

NPError NPN_NewStream(NPP instance,
                      NPMIMEType type,
                      const char* target,
                      NPStream** stream);
The instance parameter is the plug-in instance that is creating the stream; the type specifies the MIME type of the stream.

The target parameter specifies the window or frame. For the possible values for named targets, see the reference entry for NPN_NewStream. The target should not be the same window.

The stream parameter represents the stream that Communicator creates.

For an example that demonstrates using this function with NPN_Write and NPN_DestroyStream, see "Example of Sending a Stream."

[Top] [Sending a Stream]


Pushing Data into the Stream

After creating a stream with NPN_NewStream, the plug-in can call NPN_Write to deliver a buffer of data from the plug-in to Communicator. This function returns the number of bytes written or a negative integer in case of an error during processing. NPN_Write should send as much data as is available. Unlike NPP_Write, NPN_Write has no corresponding NPN_WriteReady function.

int32 NPN_Write(NPP instance, NPStream *stream,
int32 len, void *buf);
The plug-in should terminate the stream by calling NPN_DestroyStream, when all data has been written to the stream, or in the event of an error.

The instance parameter is the current plug-in; the stream parameter is a pointer to the stream being written to. The len parameter specifies the length, in bytes, of data written to the stream. The buf parameter is a pointer to the buffer holding the data to write to the stream.

For an example that demonstrates using this function with NPN_NewStream and NPN_DestroyStream, see "Example of Sending a Stream."

[Top] [Sending a Stream]


Deleting the Stream

When the stream is complete, the plug-in calls NPN_DestroyStream to close and delete it. This applies to streams the plug-in creates with NPN_NewStream or streams created by Communicator with NPP_NewStream.

NPError NPN_DestroyStream(NPP instance, NPStream* stream, 
NPError reason);
The instance parameter is the current plug-in; the stream parameter specifies the stream, created by either Communicator or the plug-in. The reason parameter represents the reason the stream was stopped, as follows:

For the complete list of codes, see "Result Codes."

For an example that demonstrates using this function with NPN_NewStream and NPN_Write, see "Example of Sending a Stream."

[Top] [Sending a Stream]


Example of Sending a Stream

The following code creates a new stream of HTML text displayed by the Communicator in a new window, writes it, and destroys the stream. Error handling has been omitted for simplicity.

NPStream* stream;
char* myData = "<HTML><B>This is a message from my plug-in!</B></HTML>";
int32 myLength = strlen(myData) + 1;
/* Create the stream. */
err = NPN_NewStream(instance, "text/html", "_blank", &stream);
/* Push data into the stream. */
err = NPN_Write(instance, stream, myLength, myData);
/* Delete the stream. */
err = NPN_DestroyStream(instance, stream, NPRES_DONE);
Your plug-in can create another instance of itself by specifying its own MIME type and a new target name in a call to NPN_NewStream.

[Top] [Sending a Stream]


[Contents] [Previous] [Next] [Index]

Last Updated: 01/15/97 16:36:52


Copyright 1997 Netscape Communications Corporation