Search code examples
firefoxnpapi

NPP_NewStream: seekable set to 0 (false) for local file


I am trying to implement an NPAPI plugin with streaming capabilities (NP_SEEK+NPN_RequestRead). No matter what I try the boolean NPBool seekable is always set to 0 (false).

I am starting firefox (iceweasel on Debian) from the command line on a local file:

$ iceweasel test1.html

If I attach gdb to the npapi plugin here is what I see:

(gdb) 
#2  0x00007f7e9da54e14 in mozilla::plugins::BrowserStreamChild::StreamConstructed (this=0x7f7e925cf310, mimeType=..., seekable=<optimized out>, stype=<optimized out>)
    at /tmp/buildd/iceweasel-24.6.0esr/dom/plugins/ipc/BrowserStreamChild.cpp:62
62      &mStream, seekable, stype);
(gdb) 
#3  0x00007f7e9da5688e in mozilla::plugins::PluginInstanceChild::AnswerPBrowserStreamConstructor (this=<optimized out>, aActor=<optimized out>, url=..., 
    length=<optimized out>, lastmodified=<optimized out>, notifyData=<optimized out>, headers=..., mimeType=..., seekable=@0x7fff25ed51df: false, rv=0x7fff25ed51e0, 
    stype=0x7fff25ed51e2) at /tmp/buildd/iceweasel-24.6.0esr/dom/plugins/ipc/PluginInstanceChild.cpp:2285
2285              ->StreamConstructed(mimeType, seekable, stype);
(gdb) p seekable
$1 = (const bool &) @0x7fff25ed51df: false
(gdb) up
#4  0x00007f7e9da8f77f in mozilla::plugins::PPluginInstanceChild::OnCallReceived (this=0x7f7e925f2c00, __msg=..., __reply=@0x7fff25ed5470: 0x0)
    at /tmp/buildd/iceweasel-24.6.0esr/build-xulrunner/ipc/ipdl/PPluginInstanceChild.cpp:2479
warning: Source file is more recent than executable.
2479                if ((!(AnswerPBrowserStreamConstructor(actor, url, length, lastmodified, notifyData, headers, mimeType, seekable, (&(rv)), (&(stype)))))) {
(gdb) list -
2469                if ((!(actor))) {
2470                    return MsgValueError;
2471                }
2472                (actor)->mId = RegisterID(actor, (__handle).mId);
2473                (actor)->mManager = this;
2474                (actor)->mChannel = mChannel;
2475                (mManagedPBrowserStreamChild).InsertElementSorted(actor);
2476                (actor)->mState = mozilla::plugins::PBrowserStream::__Start;
2477    
2478                int32_t __id = mId;
(gdb) list -
2459                    FatalError("Error deserializing 'bool'");
2460                    return MsgValueError;
2461                }
2462                (__msg).EndRead(__iter);
2463                if ((!(PPluginInstance::Transition(mState, Trigger(Trigger::Send, PPluginInstance::Msg_PBrowserStreamConstructor__ID), (&(mState)))))) {
2464                    NS_WARNING("bad state transition!");
2465                }
2466                NPError rv;
2467                uint16_t stype;
2468                actor = AllocPBrowserStream(url, length, lastmodified, notifyData, headers, mimeType, seekable, (&(rv)), (&(stype)));
(gdb) list -
2449                }
2450                if ((!(Read((&(headers)), (&(__msg)), (&(__iter)))))) {
2451                    FatalError("Error deserializing 'nsCString'");
2452                    return MsgValueError;
2453                }
2454                if ((!(Read((&(mimeType)), (&(__msg)), (&(__iter)))))) {
2455                    FatalError("Error deserializing 'nsCString'");
2456                    return MsgValueError;
2457                }
2458                if ((!(Read((&(seekable)), (&(__msg)), (&(__iter)))))) {
(gdb) up
#5  0x00007f7e9da868f0 in mozilla::plugins::PPluginModuleChild::OnCallReceived (this=<optimized out>, __msg=..., __reply=@0x7fff25ed5470: 0x0)
    at /tmp/buildd/iceweasel-24.6.0esr/build-xulrunner/ipc/ipdl/PPluginModuleChild.cpp:1023
warning: Source file is more recent than executable.
1023            return (__routed)->OnCallReceived(__msg, __reply);
(gdb) list -
1013    PPluginModuleChild::OnCallReceived(
1014            const Message& __msg,
1015            Message*& __reply)
1016    {
1017        int32_t __route = (__msg).routing_id();
1018        if ((MSG_ROUTING_CONTROL) != (__route)) {
1019            ChannelListener* __routed = Lookup(__route);
1020            if ((!(__routed))) {
1021                return MsgRouteError;
1022            }
(gdb) bt

If I copy test1.html over to /var/www, and then point to http://localhost/test1.html everything works as expected.

However the documentation mention

seekable
Boolean indicating whether the stream is seekable:
true: Seekable. Stream supports random access through calls to NPN_RequestRead (for example, local files or HTTP servers that support byte-range requests).

Solution

  • The documentation is outright lying.

    • The seekable flag in the call to NPP_NewStream
    • The only time mSeekable is ever set true is when (source)
      1. The stream is http (https, spdy)
      2. The http response has no Content-Encoding
      3. The http response provides a Content-Length.
      4. The http response has Accept-Ranges: bytes (omitting the header is not supported)
    • For all other stream types (incl. file://) and http streams not matching the requirements the seekable flag is hence always false.

    Moreover, NPN_RequestRead is only implemented for http streams, but doesn't actually care about seekable and furthermore does not actually check if the server returns 206.

    Conclusion

    You can only use NP_SEEKstreams with http (https, spdy). This is why stuff works from http://localhost, but not from a local file (file://).