nsresult nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamPeer, const char* fileName) { if (!mInst || !mInst->CanFireNotifications()) return NS_ERROR_FAILURE; PluginDestructionGuard guard(mInst); nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); if (!pluginFunctions->asfile) return NS_ERROR_FAILURE; NPP npp; mInst->GetNPP(&npp); NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n", this, npp, mNPStreamWrapper->mNPStream.url, fileName)); return NS_OK; }
void nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason) { if (!mCallNotify || !mInst || !mInst->CanFireNotifications()) return; PluginDestructionGuard guard(mInst); mCallNotify = false; // only do this ONCE and prevent recursion nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); if (pluginFunctions->urlnotify) { NPP npp; mInst->GetNPP(&npp); NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n", this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL)); } }
nsresult nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer) { if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp) return NS_ERROR_FAILURE; PluginDestructionGuard guard(mInst); nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); if (!pluginFunctions->newstream) return NS_ERROR_FAILURE; NPP npp; mInst->GetNPP(&npp); bool seekable; char* contentType; uint16_t streamType = NP_NORMAL; NPError error; streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url); streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end)); streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified)); streamPeer->IsSeekable(&seekable); streamPeer->GetContentType(&contentType); if (!mResponseHeaders.IsEmpty()) { mResponseHeaderBuf = PL_strdup(mResponseHeaders.get()); mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf; } mStreamListenerPeer = streamPeer; NPPAutoPusher nppPusher(npp); NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n", this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url)); if (error != NPERR_NO_ERROR) return NS_ERROR_FAILURE; if (streamType == nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN) { SuspendRequest(); } else if (!SetStreamType(streamType, false)) { return NS_ERROR_FAILURE; } return NS_OK; }
// This method is called when there's more data available off the // network, but it's also called from our data pump when we're feeding // the plugin data that we already got off the network, but the plugin // was unable to consume it at the point it arrived. In the case when // the plugin pump calls this method, the input argument will be null, // and the length will be the number of bytes available in our // internal buffer. nsresult nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamPeer, nsIInputStream* input, uint32_t length) { if (!length || !mInst || !mInst->CanFireNotifications()) return NS_ERROR_FAILURE; PluginDestructionGuard guard(mInst); // Just in case the caller switches plugin info on us. mStreamListenerPeer = streamPeer; nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); // check out if plugin implements NPP_Write call if (!pluginFunctions->write) return NS_ERROR_FAILURE; // it'll cancel necko transaction if (!mStreamBuffer) { // To optimize the mem usage & performance we have to allocate // mStreamBuffer here in first ODA when length of data available // in input stream is known. mStreamBuffer will be freed in DTOR. // we also have to remember the size of that buff to make safe // consecutive Read() calls form input stream into our buff. uint32_t contentLength; streamPeer->GetLength(&contentLength); mStreamBufferSize = NS_MAX(length, contentLength); // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER // (16k). This buffer will grow if needed, as in the case where // we're getting data faster than the plugin can process it. mStreamBufferSize = NS_MIN(mStreamBufferSize, uint32_t(MAX_PLUGIN_NECKO_BUFFER)); mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize); if (!mStreamBuffer) return NS_ERROR_OUT_OF_MEMORY; } // prepare NPP_ calls params NPP npp; mInst->GetNPP(&npp); int32_t streamPosition; streamPeer->GetStreamOffset(&streamPosition); int32_t streamOffset = streamPosition; if (input) { streamOffset += length; // Set new stream offset for the next ODA call regardless of how // following NPP_Write call will behave we pretend to consume all // data from the input stream. It's possible that current steam // position will be overwritten from NPP_RangeRequest call made // from NPP_Write, so we cannot call SetStreamOffset after // NPP_Write. // // Note: there is a special case when data flow should be // temporarily stopped if NPP_WriteReady returns 0 (bug #89270) streamPeer->SetStreamOffset(streamOffset); // set new end in case the content is compressed // initial end is less than end of decompressed stream // and some plugins (e.g. acrobat) can fail. if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset) mNPStreamWrapper->mNPStream.end = streamOffset; } nsresult rv = NS_OK; while (NS_SUCCEEDED(rv) && length > 0) { if (input && length) { if (mStreamBufferSize < mStreamBufferByteCount + length && mIsSuspended) { // We're in the ::OnDataAvailable() call that we might get // after suspending a request, or we suspended the request // from within this ::OnDataAvailable() call while there's // still data in the input, and we don't have enough space to // store what we got off the network. Reallocate our internal // buffer. mStreamBufferSize = mStreamBufferByteCount + length; char *buf = (char*)PR_Realloc(mStreamBuffer, mStreamBufferSize); if (!buf) return NS_ERROR_OUT_OF_MEMORY; mStreamBuffer = buf; } uint32_t bytesToRead = NS_MIN(length, mStreamBufferSize - mStreamBufferByteCount); uint32_t amountRead = 0; rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead, &amountRead); NS_ENSURE_SUCCESS(rv, rv); if (amountRead == 0) { NS_NOTREACHED("input->Read() returns no data, it's almost impossible " "to get here"); break; } mStreamBufferByteCount += amountRead; length -= amountRead; } else { // No input, nothing to read. Set length to 0 so that we don't // keep iterating through this outer loop any more. length = 0; } // Temporary pointer to the beginning of the data we're writing as // we loop and feed the plugin data. char *ptrStreamBuffer = mStreamBuffer; // it is possible plugin's NPP_Write() returns 0 byte consumed. We // use zeroBytesWriteCount to count situation like this and break // the loop int32_t zeroBytesWriteCount = 0; // mStreamBufferByteCount tells us how many bytes there are in the // buffer. WriteReady returns to us how many bytes the plugin is // ready to handle. while (mStreamBufferByteCount > 0) { int32_t numtowrite; if (pluginFunctions->writeready) { NPPAutoPusher nppPusher(npp); NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPP WriteReady called: this=%p, npp=%p, " "return(towrite)=%d, url=%s\n", this, npp, numtowrite, mNPStreamWrapper->mNPStream.url)); if (!mStreamStarted) { // The plugin called NPN_DestroyStream() from within // NPP_WriteReady(), kill the stream. return NS_BINDING_ABORTED; } // if WriteReady returned 0, the plugin is not ready to handle // the data, suspend the stream (if it isn't already // suspended). // // Also suspend the stream if the stream we're loading is not // a javascript: URL load that was initiated during plugin // initialization and there currently is such a stream // loading. This is done to work around a Windows Media Player // plugin bug where it can't deal with being fed data for // other streams while it's waiting for data from the // javascript: URL loads it requests during // initialization. See bug 386493 for more details. if (numtowrite <= 0 || (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) { if (!mIsSuspended) { SuspendRequest(); } // Break out of the inner loop, but keep going through the // outer loop in case there's more data to read from the // input stream. break; } numtowrite = NS_MIN(numtowrite, mStreamBufferByteCount); } else { // if WriteReady is not supported by the plugin, just write // the whole buffer numtowrite = mStreamBufferByteCount; } NPPAutoPusher nppPusher(npp); int32_t writeCount = 0; // bytes consumed by plugin instance NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, " "buf=%s, return(written)=%d, url=%s\n", this, npp, streamPosition, numtowrite, ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url)); if (!mStreamStarted) { // The plugin called NPN_DestroyStream() from within // NPP_Write(), kill the stream. return NS_BINDING_ABORTED; } if (writeCount > 0) { NS_ASSERTION(writeCount <= mStreamBufferByteCount, "Plugin read past the end of the available data!"); writeCount = NS_MIN(writeCount, mStreamBufferByteCount); mStreamBufferByteCount -= writeCount; streamPosition += writeCount; zeroBytesWriteCount = 0; if (mStreamBufferByteCount > 0) { // This alignment code is most likely bogus, but we'll leave // it in for now in case it matters for some plugins on some // architectures. Who knows... if (writeCount % sizeof(intptr_t)) { // memmove will take care about alignment memmove(mStreamBuffer, ptrStreamBuffer + writeCount, mStreamBufferByteCount); ptrStreamBuffer = mStreamBuffer; } else { // if aligned we can use ptrStreamBuffer += to eliminate // memmove() ptrStreamBuffer += writeCount; } } } else if (writeCount == 0) { // if NPP_Write() returns writeCount == 0 lets say 3 times in // a row, suspend the request and continue feeding the plugin // the data we got so far. Once that data is consumed, we'll // resume the request. if (mIsSuspended || ++zeroBytesWriteCount == 3) { if (!mIsSuspended) { SuspendRequest(); } // Break out of the for loop, but keep going through the // while loop in case there's more data to read from the // input stream. break; } } else { // Something's really wrong, kill the stream. rv = NS_ERROR_FAILURE; break; } } // end of inner while loop if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) { memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount); } } if (streamPosition != streamOffset) { // The plugin didn't consume all available data, or consumed some // of our cached data while we're pumping cached data. Adjust the // plugin info's stream offset to match reality, except if the // plugin info's stream offset was set by a re-entering // NPN_RequestRead() call. int32_t postWriteStreamPosition; streamPeer->GetStreamOffset(&postWriteStreamPosition); if (postWriteStreamPosition == streamOffset) { streamPeer->SetStreamOffset(streamPosition); } } return rv; }
nsresult nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer) { if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp) return NS_ERROR_FAILURE; PluginDestructionGuard guard(mInst); nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); if (!pluginFunctions->newstream) return NS_ERROR_FAILURE; NPP npp; mInst->GetNPP(&npp); bool seekable; char* contentType; uint16_t streamType = NP_NORMAL; NPError error; streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url); streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end)); streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified)); streamPeer->IsSeekable(&seekable); streamPeer->GetContentType(&contentType); if (!mResponseHeaders.IsEmpty()) { mResponseHeaderBuf = PL_strdup(mResponseHeaders.get()); mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf; } mStreamListenerPeer = streamPeer; NPPAutoPusher nppPusher(npp); NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n", this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url)); if (error != NPERR_NO_ERROR) return NS_ERROR_FAILURE; switch(streamType) { case NP_NORMAL: mStreamType = NP_NORMAL; break; case NP_ASFILEONLY: mStreamType = NP_ASFILEONLY; break; case NP_ASFILE: mStreamType = NP_ASFILE; break; case NP_SEEK: mStreamType = NP_SEEK; // Seekable streams should continue to exist even after OnStopRequest // is fired, so we AddRef ourself an extra time and Release when the // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never // calls NPN_DestroyStream the stream will be destroyed before the plugin // instance is destroyed. NS_ADDREF_THIS(); break; default: return NS_ERROR_FAILURE; } mStreamStarted = true; return NS_OK; }
nsresult nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason) { nsresult rv = NS_ERROR_FAILURE; // Various bits of code in the rest of this method may result in the // deletion of this object. Use a KungFuDeathGrip to keep ourselves // alive during cleanup. nsRefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this); if (mStreamCleanedUp) return NS_OK; mStreamCleanedUp = true; StopDataPump(); // Release any outstanding redirect callback. if (mHTTPRedirectCallback) { mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE); mHTTPRedirectCallback = nullptr; } // Seekable streams have an extra addref when they are created which must // be matched here. if (NP_SEEK == mStreamType && mStreamStarted) NS_RELEASE_THIS(); if (mStreamListenerPeer) { mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED); mStreamListenerPeer = nullptr; } if (!mInst || !mInst->CanFireNotifications()) return rv; PluginDestructionGuard guard(mInst); nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return rv; NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); NPP npp; mInst->GetNPP(&npp); if (mStreamStarted && pluginFunctions->destroystream) { NPPAutoPusher nppPusher(npp); NPError error; NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n", this, npp, reason, error, mNPStreamWrapper->mNPStream.url)); if (error == NPERR_NO_ERROR) rv = NS_OK; } mStreamStarted = false; // fire notification back to plugin, just like before CallURLNotify(reason); return rv; }