diff --git a/mozilla/modules/plugin/base/src/ns4xPlugin.cpp b/mozilla/modules/plugin/base/src/ns4xPlugin.cpp index 4c877a2c92d..5a6ebecf505 100644 --- a/mozilla/modules/plugin/base/src/ns4xPlugin.cpp +++ b/mozilla/modules/plugin/base/src/ns4xPlugin.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -89,10 +89,7 @@ enum eNPPStreamTypeInternal { static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_IID(kPluginManagerCID, NS_PLUGINMANAGER_CID); -static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID); static NS_DEFINE_IID(kMemoryCID, NS_MEMORY_CID); -static NS_DEFINE_IID(kIMemoryIID, NS_IMEMORY_IID); -static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID); PR_BEGIN_EXTERN_C @@ -461,9 +458,9 @@ ns4xPlugin::CreatePlugin(nsIServiceManagerObsolete* aServiceMgr, CheckClassInitialized(); // set up the MemAllocator service now because it might be used by the plugin - if (aServiceMgr != nsnull) { - if (nsnull == gMalloc) - aServiceMgr->GetService(kMemoryCID, kIMemoryIID, (nsISupports**)&gMalloc); + if (aServiceMgr && !gMalloc) { + aServiceMgr->GetService(kMemoryCID, NS_GET_IID(nsIMemory), + (nsISupports**)&gMalloc); } #if defined(XP_UNIX) && !defined(XP_MACOSX) @@ -703,9 +700,9 @@ ns4xPlugin::CreatePlugin(nsIServiceManagerObsolete* aServiceMgr, //---------------- //Creates a ns4xPluginInstance object. -nsresult ns4xPlugin :: CreateInstance(nsISupports *aOuter, - const nsIID &aIID, - void **aResult) +nsresult ns4xPlugin::CreateInstance(nsISupports *aOuter, + const nsIID &aIID, + void **aResult) { if (aResult == NULL) return NS_ERROR_NULL_POINTER; @@ -713,23 +710,18 @@ nsresult ns4xPlugin :: CreateInstance(nsISupports *aOuter, *aResult = NULL; // XXX This is suspicuous! - ns4xPluginInstance *inst = new ns4xPluginInstance(&fCallbacks, fLibrary); + nsRefPtr inst = + new ns4xPluginInstance(&fCallbacks, fLibrary); if (inst == NULL) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(inst); // Stabilize - - nsresult res = inst->QueryInterface(aIID, aResult); - - NS_RELEASE(inst); // Destabilize and avoid leaks. Avoid calling delete - - return res; + return inst->QueryInterface(aIID, aResult); } //////////////////////////////////////////////////////////////////////// -nsresult ns4xPlugin :: LockFactory(PRBool aLock) +nsresult ns4xPlugin::LockFactory(PRBool aLock) { // Not implemented in simplest case. return NS_OK; @@ -737,10 +729,10 @@ nsresult ns4xPlugin :: LockFactory(PRBool aLock) //////////////////////////////////////////////////////////////////////// -NS_METHOD ns4xPlugin :: CreatePluginInstance(nsISupports *aOuter, - REFNSIID aIID, - const char *aPluginMIMEType, - void **aResult) +NS_METHOD ns4xPlugin::CreatePluginInstance(nsISupports *aOuter, + REFNSIID aIID, + const char *aPluginMIMEType, + void **aResult) { return CreateInstance(aOuter, aIID, aResult); } @@ -1031,31 +1023,38 @@ NPError NP_EXPORT _destroystream(NPP npp, NPStream *pstream, NPError reason) { NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, - ("NPN_DestroyStream: npp=%p, url=%s, reason=%d\n", (void*)npp, pstream->url, (int)reason)); + ("NPN_DestroyStream: npp=%p, url=%s, reason=%d\n", (void*)npp, + pstream->url, (int)reason)); - if(!npp) + if (!npp) return NPERR_INVALID_INSTANCE_ERROR; - nsISupports* stream = (nsISupports*) pstream->ndata; - nsIPluginStreamListener* listener; + nsCOMPtr listener = + do_QueryInterface((nsISupports *)pstream->ndata); // DestroyStream can kill two kinds of streams: NPP derived and NPN derived. // check to see if they're trying to kill a NPP stream - if(stream->QueryInterface(kIPluginStreamListenerIID, (void**)&listener) == NS_OK) { - // XXX we should try to kill this listener here somehow - NS_RELEASE(listener); - return NPERR_NO_ERROR; + if (listener) { + // Tell the stream listner that the stream is now gone. + listener->OnStopBinding(nsnull, NS_BINDING_ABORTED); + + // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240131 + // + // Is it ok to leave pstream->ndata set here, and who releases it + // (or is it even properly ref counted)? And who closes the stream + // etc? + } else { + ns4xStreamWrapper* wrapper = (ns4xStreamWrapper *)pstream->ndata; + NS_ASSERTION(wrapper != NULL, "null wrapper"); + + if (wrapper == NULL) + return NPERR_INVALID_PARAM; + + // This will release the wrapped nsIOutputStream. + delete wrapper; + pstream->ndata = nsnull; } - ns4xStreamWrapper* wrapper = (ns4xStreamWrapper*) pstream->ndata; - NS_ASSERTION(wrapper != NULL, "null wrapper"); - - if (wrapper == NULL) - return NPERR_INVALID_PARAM; - - // This will release the wrapped nsIOutputStream. - delete wrapper; - pstream->ndata = nsnull; return NPERR_NO_ERROR; } @@ -1112,12 +1111,9 @@ _reloadplugins(NPBool reloadPages) if(gServiceMgr == nsnull) return; - nsIPluginManager * pm; - gServiceMgr->GetService(kPluginManagerCID, kIPluginManagerIID, (nsISupports**)&pm); + nsCOMPtr pm(do_GetService(kPluginManagerCID)); pm->ReloadPlugins(reloadPages); - - NS_RELEASE(pm); } @@ -1491,14 +1487,11 @@ _useragent(NPP npp) return NULL; char *retstr; - - nsIPluginManager * pm; - gServiceMgr->GetService(kPluginManagerCID, kIPluginManagerIID, (nsISupports**)&pm); - + + nsCOMPtr pm(do_GetService(kPluginManagerCID)); + pm->UserAgent((const char **)&retstr); - - NS_RELEASE(pm); - + return retstr; } diff --git a/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp b/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp index 8e843ffb04a..508105a7908 100644 --- a/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp +++ b/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -78,31 +78,30 @@ static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID); /////////////////////////////////////////////////////////////////////////////// // ns4xPluginStreamListener Methods -NS_IMPL_ISUPPORTS1(ns4xPluginStreamListener, nsIPluginStreamListener) +NS_IMPL_ISUPPORTS2(ns4xPluginStreamListener, nsIPluginStreamListener, + nsITimerCallback) /////////////////////////////////////////////////////////////////////////////// ns4xPluginStreamListener::ns4xPluginStreamListener(nsIPluginInstance* inst, void* notifyData, const char* aURL) - : mNotifyData(notifyData), - mStreamBuffer(nsnull), - mNotifyURL(nsnull), - mStreamStarted(PR_FALSE), - mStreamCleanedUp(PR_FALSE), - mCallNotify(PR_FALSE), - mStreamInfo(nsnull) + : mNotifyData(notifyData), + mStreamBuffer(nsnull), + mNotifyURL(aURL ? PL_strdup(aURL) : nsnull), + mInst((ns4xPluginInstance *)inst), + mStreamBufferSize(0), + mStreamBufferByteCount(0), + mStreamType(nsPluginStreamType_Normal), + mStreamStarted(PR_FALSE), + mStreamCleanedUp(PR_FALSE), + mCallNotify(PR_FALSE), + mIsSuspended(PR_FALSE) { - mInst = (ns4xPluginInstance*) inst; - mPosition = 0; - mStreamBufferSize = 0; // Initialize the 4.x interface structure memset(&mNPStream, 0, sizeof(mNPStream)); NS_IF_ADDREF(mInst); - - if (aURL) - mNotifyURL = PL_strdup(aURL); } @@ -127,9 +126,10 @@ ns4xPluginStreamListener::~ns4xPluginStreamListener(void) } } - // For those cases when NewStream is never called, we still may need to fire a - // notification callback. Return network error as fallback reason because for other - // cases, notify should have already been called for other reasons elsewhere. + // For those cases when NewStream is never called, we still may need + // to fire a notification callback. Return network error as fallback + // reason because for other cases, notify should have already been + // called for other reasons elsewhere. CallURLNotify(NPRES_NETWORK_ERR); // lets get rid of the buffer @@ -139,7 +139,6 @@ ns4xPluginStreamListener::~ns4xPluginStreamListener(void) mStreamBuffer=nsnull; } - NS_IF_RELEASE(inst); if (mNotifyURL) @@ -186,6 +185,8 @@ nsresult ns4xPluginStreamListener::CleanUpStream(NPReason reason) mStreamCleanedUp = PR_TRUE; mStreamStarted = PR_FALSE; + StopDataPump(); + // fire notification back to plugin, just like before CallURLNotify(reason); @@ -261,7 +262,6 @@ ns4xPluginStreamListener::OnStartBinding(nsIPluginStreamInfo* pluginInfo) mStreamInfo = pluginInfo; - NS_TRY_SAFE_CALL_RETURN(error, CallNPP_NewStreamProc(callbacks->newstream, npp, (char *)contentType, @@ -299,172 +299,322 @@ ns4xPluginStreamListener::OnStartBinding(nsIPluginStreamInfo* pluginInfo) return NS_OK; } +nsresult +ns4xPluginStreamListener::SuspendRequest() +{ + NS_ASSERTION(!mIsSuspended, + "Suspending a request that's already suspended!"); + + nsCOMPtr pluginInfo4x = + do_QueryInterface(mStreamInfo); + nsIRequest *request; + + if (!pluginInfo4x || !(request = pluginInfo4x->GetRequest())) { + NS_ERROR("Trying to suspend a non-suspendable stream!"); + + return NS_ERROR_FAILURE; + } + + nsresult rv = StartDataPump(); + NS_ENSURE_SUCCESS(rv, rv); + + mIsSuspended = PR_TRUE; + + return request->Suspend(); +} + +void +ns4xPluginStreamListener::ResumeRequest() +{ + nsCOMPtr pluginInfo4x = + do_QueryInterface(mStreamInfo); + + nsIRequest *request = pluginInfo4x->GetRequest(); + + // request can be null if the network stream is done. + if (request) { + request->Resume(); + } + + mIsSuspended = PR_FALSE; +} + +nsresult +ns4xPluginStreamListener::StartDataPump() +{ + nsresult rv; + mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Start pumping data to the plugin every 100ms until it obeys and + // eats the data. + return mDataPumpTimer->InitWithCallback(this, 100, + nsITimer::TYPE_REPEATING_SLACK); +} + +void +ns4xPluginStreamListener::StopDataPump() +{ + if (mDataPumpTimer) { + mDataPumpTimer->Cancel(); + + mDataPumpTimer = nsnull; + } +} + /////////////////////////////////////////////////////////////////////////////// + +// 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. NS_IMETHODIMP ns4xPluginStreamListener::OnDataAvailable(nsIPluginStreamInfo* pluginInfo, nsIInputStream* input, PRUint32 length) { - nsresult rv = NS_ERROR_FAILURE; if (!mInst || !mInst->IsStarted()) - return rv; + return NS_ERROR_FAILURE; + + // Just in case the caller switches plugin info on us. + mStreamInfo = pluginInfo; const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); // check out if plugin implements NPP_Write call if(!callbacks || !callbacks->write || !length) - return rv; // it'll cancel necko transaction + 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. - if (length >= MAX_PLUGIN_NECKO_BUFFER) { - // ">" is rare case for decoded stream, but lets eat it all - mStreamBufferSize = length; - } else { - PRUint32 contentLength; - pluginInfo->GetLength(&contentLength); - if (contentLength < MAX_PLUGIN_NECKO_BUFFER) { - // this is most common case for contentLength < 16k - mStreamBufferSize = length < contentLength ? contentLength:length; - } else { - mStreamBufferSize = MAX_PLUGIN_NECKO_BUFFER; - } - } + // 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. + + PRUint32 contentLength; + pluginInfo->GetLength(&contentLength); + + mStreamBufferSize = PR_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 = PR_MIN(mStreamBufferSize, MAX_PLUGIN_NECKO_BUFFER); + mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize); if (!mStreamBuffer) return NS_ERROR_OUT_OF_MEMORY; } // prepare NPP_ calls params - NPP npp; + NPP npp; mInst->GetNPP(&npp); - PRInt32 streamOffset; - pluginInfo->GetStreamOffset(&streamOffset); - mPosition = streamOffset; - 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) - pluginInfo->SetStreamOffset(streamOffset); + PRInt32 streamPosition; + pluginInfo->GetStreamOffset(&streamPosition); + PRInt32 streamOffset = streamPosition; - // 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 ((PRInt32)mNPStream.end < streamOffset) - mNPStream.end = streamOffset; + if (input) { + streamOffset += length; - do - { - PRUint32 bytesToRead = PR_MIN(length, mStreamBufferSize); - PRInt32 amountRead = 0; - rv = input->Read(mStreamBuffer, bytesToRead, (PRUint32*)&amountRead); - if (amountRead == 0 || NS_FAILED(rv)) { - NS_WARNING("input->Read() returns no data, it's almost impossible to get here"); - break; + // 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) + pluginInfo->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 ((PRInt32)mNPStream.end < streamOffset) + 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; + } + + PRUint32 bytesToRead = + PR_MIN(length, mStreamBufferSize - mStreamBufferByteCount); + + PRUint32 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; } - // this loop in general case will end on length <= 0, without extra input->Read() call - length -= amountRead; - - char *ptrStreamBuffer = mStreamBuffer; // tmp ptr + // 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 + // it is possible plugin's NPP_Write() returns 0 byte consumed. We + // use zeroBytesWriteCount to count situation like this and break + // the loop PRInt32 zeroBytesWriteCount = 0; - - // amountRead tells us how many bytes were put in the buffer - // WriteReady returns to us how many bytes the plugin is - // ready to handle - we have to keep calling WriteReady and - // Write until amountRead > 0 - for(;;) - { // it breaks on (amountRead <= 0) or on error - PRInt32 numtowrite; - if (callbacks->writeready) - { + + // 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) { + PRInt32 numtowrite; + if (callbacks->writeready) { NS_TRY_SAFE_CALL_RETURN(numtowrite, - CallNPP_WriteReadyProc(callbacks->writeready, npp, &mNPStream), - mInst->fLibrary, mInst); - + CallNPP_WriteReadyProc(callbacks->writeready, + npp, &mNPStream), + mInst->fLibrary, mInst); + NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, - ("NPP WriteReady called: this=%p, npp=%p, return(towrite)=%d, url=%s\n", - this, npp, numtowrite, mNPStream.url)); - - // if WriteReady returned 0, the plugin is not ready to handle - // the data, return FAILURE for now + ("NPP WriteReady called: this=%p, npp=%p, " + "return(towrite)=%d, url=%s\n", + this, npp, numtowrite, mNPStream.url)); + + // if WriteReady returned 0, the plugin is not ready to handle + // the data, suspend the stream (if it isn't already + // suspended). if (numtowrite <= 0) { - NS_ASSERTION(numtowrite,"WriteReady returned Zero"); - rv = NS_ERROR_FAILURE; + if (!mIsSuspended) { + rv = 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; } - if (numtowrite > amountRead) - numtowrite = amountRead; + + numtowrite = PR_MIN(numtowrite, mStreamBufferByteCount); + } else { + // if WriteReady is not supported by the plugin, just write + // the whole buffer + numtowrite = mStreamBufferByteCount; } - else - { - // if WriteReady is not supported by the plugin, - // just write the whole buffer - numtowrite = amountRead; - } - - PRInt32 writeCount = 0; // bytes consumed by plugin instance - + + PRInt32 writeCount = 0; // bytes consumed by plugin instance NS_TRY_SAFE_CALL_RETURN(writeCount, - CallNPP_WriteProc(callbacks->write, npp, &mNPStream, mPosition, numtowrite, (void *)ptrStreamBuffer), - mInst->fLibrary, mInst); - + CallNPP_WriteProc(callbacks->write, npp, + &mNPStream, streamPosition, + numtowrite, + ptrStreamBuffer), + mInst->fLibrary, mInst); + 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, mPosition, numtowrite, (char *)ptrStreamBuffer, writeCount, mNPStream.url)); - - if (writeCount > 0) - { - mPosition += writeCount; - amountRead -= writeCount; - if (amountRead <= 0) - break; // in common case we'll break for(;;) loop here - + ("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, mNPStream.url)); + + if (writeCount > 0) { + NS_ASSERTION(writeCount <= mStreamBufferByteCount, + "Plugin read past the end of the available data!"); + + writeCount = PR_MIN(writeCount, mStreamBufferByteCount); + mStreamBufferByteCount -= writeCount; + + streamPosition += writeCount; + zeroBytesWriteCount = 0; - if (writeCount % sizeof(long)) { - // memmove will take care about alignment - memmove(ptrStreamBuffer,ptrStreamBuffer+writeCount,amountRead); - } else { - // if aligned we can use ptrStreamBuffer += to eliminate memmove() - ptrStreamBuffer += writeCount; + + 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(PRWord)) { + // 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 raw - // lets consider this as end of ODA call (plugin isn't hungry, or broken) without an error. - if (++zeroBytesWriteCount == 3) - { - length = 0; // break do{}while - rv = NS_OK; + } 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) { + rv = 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 - { - length = 0; // break do{}while + } else { + // Something's really wrong, kill the stream. rv = NS_ERROR_FAILURE; + break; } - } // end of for(;;) - } while ((PRInt32)(length) > 0); + } // end of inner while loop - return rv == NS_BASE_STREAM_WOULD_BLOCK ? NS_OK:rv; + 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. + + PRInt32 postWriteStreamPosition; + pluginInfo->GetStreamOffset(&postWriteStreamPosition); + + if (postWriteStreamPosition == streamOffset) { + pluginInfo->SetStreamOffset(streamPosition); + } + } + + return rv; } /////////////////////////////////////////////////////////////////////////////// @@ -504,6 +654,20 @@ NS_IMETHODIMP ns4xPluginStreamListener::OnStopBinding(nsIPluginStreamInfo* pluginInfo, nsresult status) { + StopDataPump(); + + if (mIsSuspended && NS_FAILED(status)) { + // We're suspended, and the stream was destroyed, or died for some + // reason. Make sure we cancel the suspended request. + nsCOMPtr pluginInfo4x = + do_QueryInterface(mStreamInfo); + + nsIRequest *request; + if (pluginInfo4x && (request = pluginInfo4x->GetRequest())) { + request->Cancel(status); + } + } + if(!mInst || !mInst->IsStarted()) return NS_ERROR_FAILURE; @@ -518,7 +682,7 @@ ns4xPluginStreamListener::OnStopBinding(nsIPluginStreamInfo* pluginInfo, rv = CleanUpStream(reason); } - + if(rv != NPERR_NO_ERROR) return NS_ERROR_FAILURE; @@ -532,6 +696,39 @@ ns4xPluginStreamListener::GetStreamType(nsPluginStreamType *result) return NS_OK; } +NS_IMETHODIMP +ns4xPluginStreamListener::Notify(nsITimer *aTimer) +{ + NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?"); + + PRInt32 oldStreamBufferByteCount = mStreamBufferByteCount; + + nsresult rv = OnDataAvailable(mStreamInfo, nsnull, mStreamBufferByteCount); + + if (NS_FAILED(rv)) { + // We ran into an error, no need to keep firing this timer then. + + aTimer->Cancel(); + + return NS_OK; + } + + if (mStreamBufferByteCount != oldStreamBufferByteCount && + ((mStreamStarted && mStreamBufferByteCount < 1024) || + mStreamBufferByteCount == 0)) { + // The plugin read some data and we've got less than 1024 bytes in + // our buffer (or its empty and the stream is already + // done). Resume the request so that we get more data off the + // network. + + ResumeRequest(); + + // Necko will pump data now that we've resumed the request. + StopDataPump(); + } + + return NS_OK; +} /////////////////////////////////////////////////////////////////////////////// nsInstanceStream::nsInstanceStream() @@ -553,7 +750,8 @@ NS_IMPL_ISUPPORTS2(ns4xPluginInstance, nsIPluginInstance, nsIScriptablePlugin) /////////////////////////////////////////////////////////////////////////////// -ns4xPluginInstance :: ns4xPluginInstance(NPPluginFuncs* callbacks, PRLibrary* aLibrary) +ns4xPluginInstance::ns4xPluginInstance(NPPluginFuncs* callbacks, + PRLibrary* aLibrary) : fCallbacks(callbacks) { NS_ASSERTION(fCallbacks != NULL, "null callbacks"); @@ -575,7 +773,7 @@ ns4xPluginInstance :: ns4xPluginInstance(NPPluginFuncs* callbacks, PRLibrary* aL /////////////////////////////////////////////////////////////////////////////// -ns4xPluginInstance :: ~ns4xPluginInstance(void) +ns4xPluginInstance::~ns4xPluginInstance(void) { PLUGIN_LOG(PLUGIN_LOG_BASIC, ("ns4xPluginInstance dtor: this=%p\n",this)); @@ -599,7 +797,7 @@ ns4xPluginInstance :: ~ns4xPluginInstance(void) /////////////////////////////////////////////////////////////////////////////// PRBool -ns4xPluginInstance :: IsStarted(void) +ns4xPluginInstance::IsStarted(void) { return mStarted; } @@ -1220,8 +1418,8 @@ nsresult ns4xPluginInstance::GetValueInternal(NPPVariable variable, void* value) //////////////////////////////////////////////////////////////////////// -NS_IMETHODIMP ns4xPluginInstance :: GetValue(nsPluginInstanceVariable variable, - void *value) +NS_IMETHODIMP ns4xPluginInstance::GetValue(nsPluginInstanceVariable variable, + void *value) { nsresult res = NS_OK; @@ -1275,7 +1473,7 @@ nsresult ns4xPluginInstance::GetCallbacks(const NPPluginFuncs ** aCallbacks) //////////////////////////////////////////////////////////////////////// -nsresult ns4xPluginInstance :: SetWindowless(PRBool aWindowless) +nsresult ns4xPluginInstance::SetWindowless(PRBool aWindowless) { mWindowless = aWindowless; return NS_OK; @@ -1283,7 +1481,7 @@ nsresult ns4xPluginInstance :: SetWindowless(PRBool aWindowless) //////////////////////////////////////////////////////////////////////// -nsresult ns4xPluginInstance :: SetTransparent(PRBool aTransparent) +nsresult ns4xPluginInstance::SetTransparent(PRBool aTransparent) { mTransparent = aTransparent; return NS_OK; @@ -1291,7 +1489,7 @@ nsresult ns4xPluginInstance :: SetTransparent(PRBool aTransparent) //////////////////////////////////////////////////////////////////////// /* readonly attribute nsQIResult scriptablePeer; */ -NS_IMETHODIMP ns4xPluginInstance :: GetScriptablePeer(void * *aScriptablePeer) +NS_IMETHODIMP ns4xPluginInstance::GetScriptablePeer(void * *aScriptablePeer) { if (!aScriptablePeer) return NS_ERROR_NULL_POINTER; @@ -1303,7 +1501,7 @@ NS_IMETHODIMP ns4xPluginInstance :: GetScriptablePeer(void * *aScriptablePeer) //////////////////////////////////////////////////////////////////////// /* readonly attribute nsIIDPtr scriptableInterface; */ -NS_IMETHODIMP ns4xPluginInstance :: GetScriptableInterface(nsIID * *aScriptableInterface) +NS_IMETHODIMP ns4xPluginInstance::GetScriptableInterface(nsIID * *aScriptableInterface) { if (!aScriptableInterface) return NS_ERROR_NULL_POINTER; diff --git a/mozilla/modules/plugin/base/src/ns4xPluginStreamListener.h b/mozilla/modules/plugin/base/src/ns4xPluginStreamListener.h index db6ab78b70e..84b8c882dee 100644 --- a/mozilla/modules/plugin/base/src/ns4xPluginStreamListener.h +++ b/mozilla/modules/plugin/base/src/ns4xPluginStreamListener.h @@ -40,28 +40,47 @@ #include "nsIPluginStreamListener.h" #include "nsIPluginStreamInfo.h" +#include "nsIRequest.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" #define MAX_PLUGIN_NECKO_BUFFER 16384 -class ns4xPluginStreamListener : public nsIPluginStreamListener +class ns4xPluginInstance; +class nsI4xPluginStreamInfo; + +class ns4xPluginStreamListener : public nsIPluginStreamListener, + public nsITimerCallback { public: NS_DECL_ISUPPORTS // from nsIPluginStreamListener: NS_IMETHOD OnStartBinding(nsIPluginStreamInfo* pluginInfo); - NS_IMETHOD OnDataAvailable(nsIPluginStreamInfo* pluginInfo, nsIInputStream* input, PRUint32 length); - NS_IMETHOD OnFileAvailable( nsIPluginStreamInfo* pluginInfo, const char* fileName); + NS_IMETHOD OnDataAvailable(nsIPluginStreamInfo* pluginInfo, + nsIInputStream* input, PRUint32 length); + NS_IMETHOD OnFileAvailable( nsIPluginStreamInfo* pluginInfo, + const char* fileName); NS_IMETHOD OnStopBinding(nsIPluginStreamInfo* pluginInfo, nsresult status); NS_IMETHOD GetStreamType(nsPluginStreamType *result); + NS_DECL_NSITIMERCALLBACK + // ns4xPluginStreamListener specific methods: - ns4xPluginStreamListener(nsIPluginInstance* inst, void* notifyData, const char* aURL); + ns4xPluginStreamListener(nsIPluginInstance* inst, void* notifyData, + const char* aURL); virtual ~ns4xPluginStreamListener(); PRBool IsStarted(); nsresult CleanUpStream(NPReason reason); void CallURLNotify(NPReason reason); - void SetCallNotify(PRBool aCallNotify) { mCallNotify = aCallNotify; } + void SetCallNotify(PRBool aCallNotify) + { + mCallNotify = aCallNotify; + } + nsresult SuspendRequest(); + void ResumeRequest(); + nsresult StartDataPump(); + void StopDataPump(); protected: void* mNotifyData; @@ -69,16 +88,38 @@ protected: char* mNotifyURL; ns4xPluginInstance* mInst; NPStream mNPStream; - PRUint32 mPosition; PRUint32 mStreamBufferSize; + PRInt32 mStreamBufferByteCount; nsPluginStreamType mStreamType; PRPackedBool mStreamStarted; PRPackedBool mStreamCleanedUp; PRPackedBool mCallNotify; + PRPackedBool mIsSuspended; + nsCOMPtr mDataPumpTimer; public: - nsIPluginStreamInfo * mStreamInfo; + nsCOMPtr mStreamInfo; +}; + +// nsI4xPluginStreamInfo is an internal helper interface that exposes +// the underlying necko request to consumers of nsIPluginStreamInfo's. +#define NS_I4XPLUGINSTREAMINFO_IID \ +{ 0x097fdaaa, 0xa2a3, 0x49c2, \ + {0x91, 0xee, 0xeb, 0xc5, 0x7d, 0x6c, 0x9c, 0x97} } + +class nsI4xPluginStreamInfo : public nsIPluginStreamInfo +{ +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_I4XPLUGINSTREAMINFO_IID) + + nsIRequest *GetRequest() + { + return mRequest; + } + +protected: + nsCOMPtr mRequest; }; #endif diff --git a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp index d6e885c9536..75d0b8abbab 100644 --- a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp +++ b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp @@ -45,6 +45,7 @@ #include "prio.h" #include "prmem.h" #include "ns4xPlugin.h" +#include "ns4xPluginStreamListener.h" #include "nsPluginInstancePeer.h" #include "nsIPlugin.h" #ifdef OJI @@ -1093,7 +1094,7 @@ PRBool nsPluginTag::Equals(nsPluginTag *aPluginTag) //////////////////////////////////////////////////////////////////////// class nsPluginStreamListenerPeer; -class nsPluginStreamInfo : public nsIPluginStreamInfo +class nsPluginStreamInfo : public nsI4xPluginStreamInfo { public: nsPluginStreamInfo(); @@ -1159,6 +1160,12 @@ public: void SetStreamComplete(const PRBool complete); + void + SetRequest(nsIRequest *request) + { + mRequest = request; + } + private: char* mContentType; @@ -1216,7 +1223,7 @@ private: nsIPluginInstanceOwner *mOwner; nsIPluginInstance *mInstance; nsIPluginStreamListener *mPStreamListener; - nsPluginStreamInfo *mPluginStreamInfo; + nsRefPtr mPluginStreamInfo; // Set to PR_TRUE if we request failed (like with a HTTP response of 404) PRPackedBool mRequestFailed; @@ -1297,37 +1304,10 @@ nsPluginStreamInfo::~nsPluginStreamInfo() } //////////////////////////////////////////////////////////////////////// -NS_IMPL_ADDREF(nsPluginStreamInfo) -NS_IMPL_RELEASE(nsPluginStreamInfo) +NS_IMPL_ISUPPORTS2(nsPluginStreamInfo, nsIPluginStreamInfo, + nsI4xPluginStreamInfo) //////////////////////////////////////////////////////////////////////// -nsresult nsPluginStreamInfo::QueryInterface(const nsIID& aIID, - void** aInstancePtrResult) -{ - NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer"); - - if (nsnull == aInstancePtrResult) - return NS_ERROR_NULL_POINTER; - - if (aIID.Equals(kIPluginStreamInfoIID)) - { - *aInstancePtrResult = (void *)((nsIPluginStreamInfo *)this); - AddRef(); - return NS_OK; - } - - if (aIID.Equals(kISupportsIID)) - { - *aInstancePtrResult = (void *)((nsISupports *)((nsIStreamListener *)this)); - AddRef(); - return NS_OK; - } - - return NS_NOINTERFACE; -} - - -//////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginStreamInfo::GetContentType(nsMIMEType* result) { @@ -1335,8 +1315,6 @@ nsPluginStreamInfo::GetContentType(nsMIMEType* result) return NS_OK; } - -//////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginStreamInfo::IsSeekable(PRBool* result) { @@ -1344,8 +1322,6 @@ nsPluginStreamInfo::IsSeekable(PRBool* result) return NS_OK; } - -//////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginStreamInfo::GetLength(PRUint32* result) { @@ -1353,8 +1329,6 @@ nsPluginStreamInfo::GetLength(PRUint32* result) return NS_OK; } - -//////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginStreamInfo::GetLastModified(PRUint32* result) { @@ -1362,8 +1336,6 @@ nsPluginStreamInfo::GetLastModified(PRUint32* result) return NS_OK; } - -//////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsPluginStreamInfo::GetURL(const char** result) { @@ -1371,9 +1343,6 @@ nsPluginStreamInfo::GetURL(const char** result) return NS_OK; } - - -//////////////////////////////////////////////////////////////////////// void nsPluginStreamInfo::MakeByteRangeString(nsByteRange* aRangeList, nsACString &rangeRequest, PRInt32 *numRequests) { @@ -1647,7 +1616,6 @@ nsPluginStreamListenerPeer::nsPluginStreamListenerPeer() mOwner = nsnull; mInstance = nsnull; mPStreamListener = nsnull; - mPluginStreamInfo = nsnull; mHost = nsnull; mStreamType = nsPluginStreamType_Normal; mStartBinding = PR_FALSE; @@ -1677,7 +1645,6 @@ nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer() NS_IF_RELEASE(mInstance); NS_IF_RELEASE(mPStreamListener); NS_IF_RELEASE(mHost); - NS_IF_RELEASE(mPluginStreamInfo); // close FD of mFileCacheOutputStream if it's still open // or we won't be able to remove the cache file @@ -1747,7 +1714,6 @@ nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL, if (!mPluginStreamInfo) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mPluginStreamInfo); mPluginStreamInfo->SetPluginInstance(aInstance); mPluginStreamInfo->SetPluginStreamListenerPeer(this); @@ -1802,7 +1768,6 @@ nsresult nsPluginStreamListenerPeer::InitializeEmbeded(nsIURI *aURL, if (!mPluginStreamInfo) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mPluginStreamInfo); mPluginStreamInfo->SetPluginInstance(aInstance); mPluginStreamInfo->SetPluginStreamListenerPeer(this); @@ -1829,7 +1794,6 @@ nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIPluginInstance *aInst if (!mPluginStreamInfo) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mPluginStreamInfo); mPluginStreamInfo->SetPluginInstance(aInstance); mPluginStreamInfo->SetPluginStreamListenerPeer(this); @@ -2023,6 +1987,8 @@ nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request, nsISupports* aCo mPluginStreamInfo->SetLength(length); } + mPluginStreamInfo->SetRequest(request); + nsCAutoString aContentType; rv = channel->GetContentType(aContentType); if (NS_FAILED(rv)) @@ -2176,6 +2142,8 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, if(!mPStreamListener || !mPluginStreamInfo) return NS_ERROR_FAILURE; + mPluginStreamInfo->SetRequest(request); + const char * url = nsnull; mPluginStreamInfo->GetURL(&url); @@ -2190,21 +2158,25 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, // get the absolute offset of the request, if one exists. nsCOMPtr brr = do_QueryInterface(request); PRInt32 absoluteOffset = 0; - PRInt32 amtForwardToPlugin = 0; if (brr) { - brr->GetStartRange(&absoluteOffset); - - // we need to track how much data we have forward on to the plugin. - nsPRUintKey key(absoluteOffset); + if (!mDataForwardToRequest) + return NS_ERROR_FAILURE; - if (!mDataForwardToRequest) - return NS_ERROR_FAILURE; + brr->GetStartRange(&absoluteOffset); - if (mDataForwardToRequest->Exists(&key)) - amtForwardToPlugin = NS_PTR_TO_INT32(mDataForwardToRequest->Remove(&key)); - - mDataForwardToRequest->Put(&key, (void*) (amtForwardToPlugin+aLength)); - mPluginStreamInfo->SetStreamOffset(absoluteOffset + amtForwardToPlugin); + // we need to track how much data we have forwarded to the + // plugin. + + // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130 + // + // Why couldn't this be tracked on the plugin info, and not in a + // *hash table*? + nsPRUintKey key(absoluteOffset); + PRInt32 amtForwardToPlugin = + NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key)); + mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength)); + + mPluginStreamInfo->SetStreamOffset(absoluteOffset + amtForwardToPlugin); } nsCOMPtr stream = aIStream; @@ -2219,9 +2191,9 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, return rv; } - rv = mPStreamListener->OnDataAvailable((nsIPluginStreamInfo*)mPluginStreamInfo, - stream, - aLength); + rv = mPStreamListener->OnDataAvailable(mPluginStreamInfo, + stream, + aLength); // if a plugin returns an error, the peer must kill the stream // else the stream and PluginStreamListener leak @@ -2317,7 +2289,7 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request, if (NS_FAILED(aStatus)) { // on error status cleanup the stream // and return w/o OnFileAvailable() - mPStreamListener->OnStopBinding((nsIPluginStreamInfo*)mPluginStreamInfo, aStatus); + mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus); return NS_OK; } @@ -2345,13 +2317,13 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request, if (mStartBinding) { // On start binding has been called - mPStreamListener->OnStopBinding((nsIPluginStreamInfo*)mPluginStreamInfo, aStatus); + mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus); } else { // OnStartBinding hasn't been called, so complete the action. - mPStreamListener->OnStartBinding((nsIPluginStreamInfo*)mPluginStreamInfo); - mPStreamListener->OnStopBinding((nsIPluginStreamInfo*)mPluginStreamInfo, aStatus); + mPStreamListener->OnStartBinding(mPluginStreamInfo); + mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus); } if (NS_SUCCEEDED(aStatus)) @@ -2449,7 +2421,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request, } } - rv = mPStreamListener->OnStartBinding((nsIPluginStreamInfo*)mPluginStreamInfo); + rv = mPStreamListener->OnStartBinding(mPluginStreamInfo); mStartBinding = PR_TRUE; @@ -2495,7 +2467,7 @@ nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile) return NS_OK; } - rv = mPStreamListener->OnFileAvailable((nsIPluginStreamInfo*)mPluginStreamInfo, path.get()); + rv = mPStreamListener->OnFileAvailable(mPluginStreamInfo, path.get()); return rv; } @@ -6594,7 +6566,7 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request, } mPluginStreamInfo->SetSeekable(0); - mPStreamListener->OnStartBinding((nsIPluginStreamInfo*)mPluginStreamInfo); + mPStreamListener->OnStartBinding(mPluginStreamInfo); mPluginStreamInfo->SetStreamOffset(0); // force the plugin use stream as file @@ -6740,4 +6712,10 @@ void nsPluginStreamInfo::SetStreamComplete(const PRBool complete) { mStreamComplete = complete; + + if (complete) { + // We're done, release the request. + + SetRequest(nsnull); + } }