This document describes how an embedder or other Gecko/Necko-using application can implement download resuming.
The interfaces in question exist in the form they are described here since Gecko 1.8a4 (Firefox 1.5, SeaMonkey 1.0, XULRunner 1.8.0.1).
Introduction
Various protocols support getting partial files. This means that if a download was interrupted, it can be resumed from that point on, rather than regetting the whole file. The Necko implementations of HTTP 1.1 (RFC 2616) as well as FTP support this feature.
Not only is the ability to specify a start position important, but it's also important to have some assurance that the file did not change since the initial download attempt. That would lead to a situation where the first part of the file corresponds to the initial version, while the latter part belongs to a different version, leading to an unusable result.
The interface
Resuming is done using the nsIResumableChannel
interface. The expected way to use it is this:
- For all downloads that happen, get the
entityID
from the channel, and store it for later use. The entity ID is what ensures that the file remains unchanged. This can also be used to check whether the download is resumable: if it is not (e.g. the server is not using HTTP 1.1), then accessing this attribute will throw anNS_ERROR_NOT_RESUMABLE
exception. - If the download gets interrupted, Necko will call the stream listener's
onStopRequest
method with a failure status. (TODO: document what webbrowserpersist/exthandler do). The front-end code can then notify the user of this, and offer to resume the download.
Resuming a download
In order to resume a download, you should create a channel as usual (using nsIIOService
). Then, you can check if it implements nsIResumableChannel
. If it does not, the protocol does not support resuming. Otherwise, call resumeAt
with the desired start position, and the previously stored entity ID. (It is possible to pass an empty string as the ID; however, this means that you have no assurance that the file remained unchanged)
Now, you can open the channel as usual (using nsIChannel.asyncOpen()
in the common case) and write to the file in the onDataAvailable
notifications. You may want to use nsISimpleStreamListener
to simplify this task; to get progress notifications, you can implement nsIProgressEventSink
and set an interface requester as the notificationCallbacks
of the channel that gives out such an event sink (this needs to be done before calling asyncOpen
).
nsIWebBrowserPersist
cannot append to existing files (as opposed to overwriting them), and is therefore not usable for this task (see bugĀ 129921).Detecting when a file changed
If the file changed (that is, the entity ID does not match), then Necko will notify the stream listener with an NS_ERROR_ENTITY_CHANGED
error code. The front-end may want to offer the user to get the new file from scratch in such a case.
Resuming not supported
In some cases, it's not possible to detect whether the server supports resuming until the user actually tries to resume. In such a case, the stream listener will get an NS_ERROR_NOT_RESUMABLE
status. Here, too, the front-end may want to offer downloading the entire file from scratch.