LCOV - code coverage report
Current view: top level - dom/plugins/base - nsNPAPIPluginStreamListener.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 366 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 28 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsNPAPIPluginStreamListener.h"
       7             : #include "plstr.h"
       8             : #include "nsDirectoryServiceDefs.h"
       9             : #include "nsDirectoryServiceUtils.h"
      10             : #include "nsIFile.h"
      11             : #include "nsNetUtil.h"
      12             : #include "nsPluginHost.h"
      13             : #include "nsNPAPIPlugin.h"
      14             : #include "nsPluginLogging.h"
      15             : #include "nsPluginStreamListenerPeer.h"
      16             : 
      17             : #include <stdint.h>
      18             : #include <algorithm>
      19             : 
      20           0 : nsNPAPIStreamWrapper::nsNPAPIStreamWrapper(nsIOutputStream *outputStream,
      21           0 :                                            nsNPAPIPluginStreamListener *streamListener)
      22             : {
      23           0 :   mOutputStream = outputStream;
      24           0 :   mStreamListener = streamListener;
      25             : 
      26           0 :   memset(&mNPStream, 0, sizeof(mNPStream));
      27           0 :   mNPStream.ndata = static_cast<void*>(this);
      28           0 : }
      29             : 
      30           0 : nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper()
      31             : {
      32           0 :   if (mOutputStream) {
      33           0 :     mOutputStream->Close();
      34             :   }
      35           0 : }
      36             : 
      37             : // nsNPAPIPluginStreamListener Methods
      38           0 : NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener,
      39             :                   nsITimerCallback, nsIHTTPHeaderListener)
      40             : 
      41           0 : nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst,
      42             :                                                          void* notifyData,
      43           0 :                                                          const char* aURL)
      44             :   : mStreamBuffer(nullptr)
      45           0 :   , mNotifyURL(aURL ? PL_strdup(aURL) : nullptr)
      46             :   , mInst(inst)
      47             :   , mStreamBufferSize(0)
      48             :   , mStreamBufferByteCount(0)
      49             :   , mStreamType(NP_NORMAL)
      50             :   , mStreamState(eStreamStopped)
      51             :   , mStreamCleanedUp(false)
      52           0 :   , mCallNotify(notifyData ? true : false)
      53             :   , mIsSuspended(false)
      54           0 :   , mIsPluginInitJSStream(mInst->mInPluginInitCall &&
      55           0 :                           aURL && strncmp(aURL, "javascript:",
      56             :                                           sizeof("javascript:") - 1) == 0)
      57             :   , mRedirectDenied(false)
      58             :   , mResponseHeaderBuf(nullptr)
      59             :   , mStreamStopMode(eNormalStop)
      60           0 :   , mPendingStopBindingStatus(NS_OK)
      61             : {
      62           0 :   mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this);
      63           0 :   mNPStreamWrapper->mNPStream.notifyData = notifyData;
      64           0 : }
      65             : 
      66           0 : nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener()
      67             : {
      68             :   // remove this from the plugin instance's stream list
      69           0 :   nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
      70           0 :   streamListeners->RemoveElement(this);
      71             : 
      72             :   // For those cases when NewStream is never called, we still may need
      73             :   // to fire a notification callback. Return network error as fallback
      74             :   // reason because for other cases, notify should have already been
      75             :   // called for other reasons elsewhere.
      76           0 :   CallURLNotify(NPRES_NETWORK_ERR);
      77             : 
      78             :   // lets get rid of the buffer
      79           0 :   if (mStreamBuffer) {
      80           0 :     free(mStreamBuffer);
      81           0 :     mStreamBuffer=nullptr;
      82             :   }
      83             : 
      84           0 :   if (mNotifyURL)
      85           0 :     PL_strfree(mNotifyURL);
      86             : 
      87           0 :   if (mResponseHeaderBuf)
      88           0 :     PL_strfree(mResponseHeaderBuf);
      89             : 
      90           0 :   if (mNPStreamWrapper) {
      91           0 :     delete mNPStreamWrapper;
      92             :   }
      93           0 : }
      94             : 
      95             : nsresult
      96           0 : nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason)
      97             : {
      98           0 :   nsresult rv = NS_ERROR_FAILURE;
      99             : 
     100             :   // Various bits of code in the rest of this method may result in the
     101             :   // deletion of this object. Use a KungFuDeathGrip to keep ourselves
     102             :   // alive during cleanup.
     103           0 :   RefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this);
     104             : 
     105           0 :   if (mStreamCleanedUp)
     106           0 :     return NS_OK;
     107             : 
     108           0 :   mStreamCleanedUp = true;
     109             : 
     110           0 :   StopDataPump();
     111             : 
     112             :   // Release any outstanding redirect callback.
     113           0 :   if (mHTTPRedirectCallback) {
     114           0 :     mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
     115           0 :     mHTTPRedirectCallback = nullptr;
     116             :   }
     117             : 
     118             :   // Seekable streams have an extra addref when they are created which must
     119             :   // be matched here.
     120           0 :   if (NP_SEEK == mStreamType && mStreamState == eStreamTypeSet)
     121           0 :     NS_RELEASE_THIS();
     122             : 
     123           0 :   if (mStreamListenerPeer) {
     124           0 :     mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED);
     125           0 :     mStreamListenerPeer = nullptr;
     126             :   }
     127             : 
     128           0 :   if (!mInst || !mInst->CanFireNotifications())
     129           0 :     return rv;
     130             : 
     131           0 :   PluginDestructionGuard guard(mInst);
     132             : 
     133           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     134           0 :   if (!plugin || !plugin->GetLibrary())
     135           0 :     return rv;
     136             : 
     137           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     138             : 
     139             :   NPP npp;
     140           0 :   mInst->GetNPP(&npp);
     141             : 
     142           0 :   if (mStreamState >= eNewStreamCalled && pluginFunctions->destroystream) {
     143           0 :     NPPAutoPusher nppPusher(npp);
     144             : 
     145             :     NPError error;
     146           0 :     NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst,
     147             :                             NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     148             : 
     149           0 :     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     150             :                    ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
     151             :                     this, npp, reason, error, mNPStreamWrapper->mNPStream.url));
     152             : 
     153           0 :     if (error == NPERR_NO_ERROR)
     154           0 :       rv = NS_OK;
     155             :   }
     156             : 
     157           0 :   mStreamState = eStreamStopped;
     158             : 
     159             :   // fire notification back to plugin, just like before
     160           0 :   CallURLNotify(reason);
     161             : 
     162           0 :   return rv;
     163             : }
     164             : 
     165             : void
     166           0 : nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason)
     167             : {
     168           0 :   if (!mCallNotify || !mInst || !mInst->CanFireNotifications())
     169           0 :     return;
     170             : 
     171           0 :   PluginDestructionGuard guard(mInst);
     172             : 
     173           0 :   mCallNotify = false; // only do this ONCE and prevent recursion
     174             : 
     175           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     176           0 :   if (!plugin || !plugin->GetLibrary())
     177           0 :     return;
     178             : 
     179           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     180             : 
     181           0 :   if (pluginFunctions->urlnotify) {
     182             :     NPP npp;
     183           0 :     mInst->GetNPP(&npp);
     184             : 
     185           0 :     NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst,
     186             :                           NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     187             : 
     188           0 :     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     189             :                    ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
     190             :                     this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL));
     191             :   }
     192             : }
     193             : 
     194             : nsresult
     195           0 : nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer)
     196             : {
     197           0 :   AUTO_PROFILER_LABEL("nsNPAPIPluginStreamListener::OnStartBinding", OTHER);
     198           0 :   if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp)
     199           0 :     return NS_ERROR_FAILURE;
     200             : 
     201           0 :   PluginDestructionGuard guard(mInst);
     202             : 
     203           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     204           0 :   if (!plugin || !plugin->GetLibrary())
     205           0 :     return NS_ERROR_FAILURE;
     206             : 
     207           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     208             : 
     209           0 :   if (!pluginFunctions->newstream)
     210           0 :     return NS_ERROR_FAILURE;
     211             : 
     212             :   NPP npp;
     213           0 :   mInst->GetNPP(&npp);
     214             : 
     215             :   bool seekable;
     216             :   char* contentType;
     217           0 :   uint16_t streamType = NP_NORMAL;
     218             :   NPError error;
     219             : 
     220           0 :   streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url);
     221           0 :   streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end));
     222           0 :   streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified));
     223           0 :   streamPeer->IsSeekable(&seekable);
     224           0 :   streamPeer->GetContentType(&contentType);
     225             : 
     226           0 :   if (!mResponseHeaders.IsEmpty()) {
     227           0 :     mResponseHeaderBuf = PL_strdup(mResponseHeaders.get());
     228           0 :     mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf;
     229             :   }
     230             : 
     231           0 :   mStreamListenerPeer = streamPeer;
     232             : 
     233           0 :   NPPAutoPusher nppPusher(npp);
     234             : 
     235           0 :   NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst,
     236             :                           NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     237             : 
     238           0 :   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     239             :                  ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
     240             :                   this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url));
     241             : 
     242           0 :   if (error != NPERR_NO_ERROR)
     243           0 :     return NS_ERROR_FAILURE;
     244             : 
     245           0 :   mStreamState = eNewStreamCalled;
     246             : 
     247           0 :   if (!SetStreamType(streamType, false)) {
     248           0 :     return NS_ERROR_FAILURE;
     249             :   }
     250             : 
     251           0 :   return NS_OK;
     252             : }
     253             : 
     254             : bool
     255           0 : nsNPAPIPluginStreamListener::SetStreamType(uint16_t aType, bool aNeedsResume)
     256             : {
     257           0 :   switch(aType)
     258             :   {
     259             :     case NP_NORMAL:
     260           0 :       mStreamType = NP_NORMAL;
     261           0 :       break;
     262             :     case NP_ASFILEONLY:
     263           0 :       mStreamType = NP_ASFILEONLY;
     264           0 :       break;
     265             :     case NP_ASFILE:
     266           0 :       mStreamType = NP_ASFILE;
     267           0 :       break;
     268             :     case NP_SEEK:
     269           0 :       mStreamType = NP_SEEK;
     270             :       // Seekable streams should continue to exist even after OnStopRequest
     271             :       // is fired, so we AddRef ourself an extra time and Release when the
     272             :       // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never
     273             :       // calls NPN_DestroyStream the stream will be destroyed before the plugin
     274             :       // instance is destroyed.
     275           0 :       NS_ADDREF_THIS();
     276           0 :       break;
     277             :     case nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN:
     278           0 :       MOZ_ASSERT(!aNeedsResume);
     279           0 :       mStreamType = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
     280           0 :       SuspendRequest();
     281           0 :       mStreamStopMode = eDoDeferredStop;
     282             :       // In this case we do not want to execute anything else in this function.
     283           0 :       return true;
     284             :     default:
     285           0 :       return false;
     286             :   }
     287           0 :   mStreamState = eStreamTypeSet;
     288           0 :   if (aNeedsResume) {
     289           0 :     if (mStreamListenerPeer) {
     290           0 :       mStreamListenerPeer->OnStreamTypeSet(mStreamType);
     291             :     }
     292           0 :     ResumeRequest();
     293             :   }
     294           0 :   return true;
     295             : }
     296             : 
     297             : void
     298           0 : nsNPAPIPluginStreamListener::SuspendRequest()
     299             : {
     300           0 :   NS_ASSERTION(!mIsSuspended,
     301             :                "Suspending a request that's already suspended!");
     302             : 
     303           0 :   nsresult rv = StartDataPump();
     304           0 :   if (NS_FAILED(rv))
     305           0 :     return;
     306             : 
     307           0 :   mIsSuspended = true;
     308             : 
     309           0 :   if (mStreamListenerPeer) {
     310           0 :     mStreamListenerPeer->SuspendRequests();
     311             :   }
     312             : }
     313             : 
     314             : void
     315           0 : nsNPAPIPluginStreamListener::ResumeRequest()
     316             : {
     317           0 :   if (mStreamListenerPeer) {
     318           0 :     mStreamListenerPeer->ResumeRequests();
     319             :   }
     320           0 :   mIsSuspended = false;
     321           0 : }
     322             : 
     323             : nsresult
     324           0 : nsNPAPIPluginStreamListener::StartDataPump()
     325             : {
     326             :   nsresult rv;
     327           0 :   mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     328           0 :   NS_ENSURE_SUCCESS(rv, rv);
     329             : 
     330             :   // Start pumping data to the plugin every 100ms until it obeys and
     331             :   // eats the data.
     332           0 :   return mDataPumpTimer->InitWithCallback(this, 100,
     333           0 :                                           nsITimer::TYPE_REPEATING_SLACK);
     334             : }
     335             : 
     336             : void
     337           0 : nsNPAPIPluginStreamListener::StopDataPump()
     338             : {
     339           0 :   if (mDataPumpTimer) {
     340           0 :     mDataPumpTimer->Cancel();
     341           0 :     mDataPumpTimer = nullptr;
     342             :   }
     343           0 : }
     344             : 
     345             : // Return true if a javascript: load that was started while the plugin
     346             : // was being initialized is still in progress.
     347             : bool
     348           0 : nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress()
     349             : {
     350           0 :   if (!mInst)
     351           0 :     return false;
     352             : 
     353           0 :   nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
     354           0 :   for (unsigned int i = 0; i < streamListeners->Length(); i++) {
     355           0 :     if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) {
     356           0 :       return true;
     357             :     }
     358             :   }
     359             : 
     360           0 :   return false;
     361             : }
     362             : 
     363             : // This method is called when there's more data available off the
     364             : // network, but it's also called from our data pump when we're feeding
     365             : // the plugin data that we already got off the network, but the plugin
     366             : // was unable to consume it at the point it arrived. In the case when
     367             : // the plugin pump calls this method, the input argument will be null,
     368             : // and the length will be the number of bytes available in our
     369             : // internal buffer.
     370             : nsresult
     371           0 : nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
     372             :                                              nsIInputStream* input,
     373             :                                              uint32_t length)
     374             : {
     375           0 :   if (!length || !mInst || !mInst->CanFireNotifications())
     376           0 :     return NS_ERROR_FAILURE;
     377             : 
     378           0 :   PluginDestructionGuard guard(mInst);
     379             : 
     380             :   // Just in case the caller switches plugin info on us.
     381           0 :   mStreamListenerPeer = streamPeer;
     382             : 
     383           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     384           0 :   if (!plugin || !plugin->GetLibrary())
     385           0 :     return NS_ERROR_FAILURE;
     386             : 
     387           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     388             : 
     389             :   // check out if plugin implements NPP_Write call
     390           0 :   if (!pluginFunctions->write)
     391           0 :     return NS_ERROR_FAILURE; // it'll cancel necko transaction
     392             : 
     393           0 :   if (!mStreamBuffer) {
     394             :     // To optimize the mem usage & performance we have to allocate
     395             :     // mStreamBuffer here in first ODA when length of data available
     396             :     // in input stream is known.  mStreamBuffer will be freed in DTOR.
     397             :     // we also have to remember the size of that buff to make safe
     398             :     // consecutive Read() calls form input stream into our buff.
     399             : 
     400             :     uint32_t contentLength;
     401           0 :     streamPeer->GetLength(&contentLength);
     402             : 
     403           0 :     mStreamBufferSize = std::max(length, contentLength);
     404             : 
     405             :     // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER
     406             :     // (16k). This buffer will grow if needed, as in the case where
     407             :     // we're getting data faster than the plugin can process it.
     408           0 :     mStreamBufferSize = std::min(mStreamBufferSize,
     409           0 :                                uint32_t(MAX_PLUGIN_NECKO_BUFFER));
     410             : 
     411           0 :     mStreamBuffer = (char*) malloc(mStreamBufferSize);
     412           0 :     if (!mStreamBuffer)
     413           0 :       return NS_ERROR_OUT_OF_MEMORY;
     414             :   }
     415             : 
     416             :   // prepare NPP_ calls params
     417             :   NPP npp;
     418           0 :   mInst->GetNPP(&npp);
     419             : 
     420             :   int32_t streamPosition;
     421           0 :   streamPeer->GetStreamOffset(&streamPosition);
     422           0 :   int32_t streamOffset = streamPosition;
     423             : 
     424           0 :   if (input) {
     425           0 :     streamOffset += length;
     426             : 
     427             :     // Set new stream offset for the next ODA call regardless of how
     428             :     // following NPP_Write call will behave we pretend to consume all
     429             :     // data from the input stream.  It's possible that current steam
     430             :     // position will be overwritten from NPP_RangeRequest call made
     431             :     // from NPP_Write, so we cannot call SetStreamOffset after
     432             :     // NPP_Write.
     433             :     //
     434             :     // Note: there is a special case when data flow should be
     435             :     // temporarily stopped if NPP_WriteReady returns 0 (bug #89270)
     436           0 :     streamPeer->SetStreamOffset(streamOffset);
     437             : 
     438             :     // set new end in case the content is compressed
     439             :     // initial end is less than end of decompressed stream
     440             :     // and some plugins (e.g. acrobat) can fail.
     441           0 :     if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset)
     442           0 :       mNPStreamWrapper->mNPStream.end = streamOffset;
     443             :   }
     444             : 
     445           0 :   nsresult rv = NS_OK;
     446           0 :   while (NS_SUCCEEDED(rv) && length > 0) {
     447           0 :     if (input && length) {
     448           0 :       if (mStreamBufferSize < mStreamBufferByteCount + length) {
     449             :         // We're in the ::OnDataAvailable() call that we might get
     450             :         // after suspending a request, or we suspended the request
     451             :         // from within this ::OnDataAvailable() call while there's
     452             :         // still data in the input, or we have resumed a previously
     453             :         // suspended request and our buffer is already full, and we
     454             :         // don't have enough space to store what we got off the network.
     455             :         // Reallocate our internal buffer.
     456           0 :         mStreamBufferSize = mStreamBufferByteCount + length;
     457           0 :         char* buf = (char*) realloc(mStreamBuffer, mStreamBufferSize);
     458           0 :         if (!buf)
     459           0 :           return NS_ERROR_OUT_OF_MEMORY;
     460             : 
     461           0 :         mStreamBuffer = buf;
     462             :       }
     463             : 
     464             :       uint32_t bytesToRead =
     465           0 :       std::min(length, mStreamBufferSize - mStreamBufferByteCount);
     466           0 :       MOZ_ASSERT(bytesToRead > 0);
     467             : 
     468           0 :       uint32_t amountRead = 0;
     469           0 :       rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead,
     470           0 :                        &amountRead);
     471           0 :       NS_ENSURE_SUCCESS(rv, rv);
     472             : 
     473           0 :       if (amountRead == 0) {
     474           0 :         NS_NOTREACHED("input->Read() returns no data, it's almost impossible "
     475             :                       "to get here");
     476             : 
     477           0 :         break;
     478             :       }
     479             : 
     480           0 :       mStreamBufferByteCount += amountRead;
     481           0 :       length -= amountRead;
     482             :     } else {
     483             :       // No input, nothing to read. Set length to 0 so that we don't
     484             :       // keep iterating through this outer loop any more.
     485             : 
     486           0 :       length = 0;
     487             :     }
     488             : 
     489             :     // Temporary pointer to the beginning of the data we're writing as
     490             :     // we loop and feed the plugin data.
     491           0 :     char *ptrStreamBuffer = mStreamBuffer;
     492             : 
     493             :     // it is possible plugin's NPP_Write() returns 0 byte consumed. We
     494             :     // use zeroBytesWriteCount to count situation like this and break
     495             :     // the loop
     496           0 :     int32_t zeroBytesWriteCount = 0;
     497             : 
     498             :     // mStreamBufferByteCount tells us how many bytes there are in the
     499             :     // buffer. WriteReady returns to us how many bytes the plugin is
     500             :     // ready to handle.
     501           0 :     while (mStreamBufferByteCount > 0) {
     502             :       int32_t numtowrite;
     503           0 :       if (pluginFunctions->writeready) {
     504           0 :         NPPAutoPusher nppPusher(npp);
     505             : 
     506           0 :         NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst,
     507             :                                 NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     508           0 :         NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
     509             :                        ("NPP WriteReady called: this=%p, npp=%p, "
     510             :                         "return(towrite)=%d, url=%s\n",
     511             :                         this, npp, numtowrite, mNPStreamWrapper->mNPStream.url));
     512             : 
     513           0 :         if (mStreamState == eStreamStopped) {
     514             :           // The plugin called NPN_DestroyStream() from within
     515             :           // NPP_WriteReady(), kill the stream.
     516             : 
     517           0 :           return NS_BINDING_ABORTED;
     518             :         }
     519             : 
     520             :         // if WriteReady returned 0, the plugin is not ready to handle
     521             :         // the data, suspend the stream (if it isn't already
     522             :         // suspended).
     523             :         //
     524             :         // Also suspend the stream if the stream we're loading is not
     525             :         // a javascript: URL load that was initiated during plugin
     526             :         // initialization and there currently is such a stream
     527             :         // loading. This is done to work around a Windows Media Player
     528             :         // plugin bug where it can't deal with being fed data for
     529             :         // other streams while it's waiting for data from the
     530             :         // javascript: URL loads it requests during
     531             :         // initialization. See bug 386493 for more details.
     532             : 
     533           0 :         if (numtowrite <= 0 ||
     534           0 :             (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) {
     535           0 :           if (!mIsSuspended) {
     536           0 :             SuspendRequest();
     537             :           }
     538             : 
     539             :           // Break out of the inner loop, but keep going through the
     540             :           // outer loop in case there's more data to read from the
     541             :           // input stream.
     542             : 
     543           0 :           break;
     544             :         }
     545             : 
     546           0 :         numtowrite = std::min(numtowrite, mStreamBufferByteCount);
     547             :       } else {
     548             :         // if WriteReady is not supported by the plugin, just write
     549             :         // the whole buffer
     550           0 :         numtowrite = mStreamBufferByteCount;
     551             :       }
     552             : 
     553           0 :       NPPAutoPusher nppPusher(npp);
     554             : 
     555           0 :       int32_t writeCount = 0; // bytes consumed by plugin instance
     556           0 :       NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst,
     557             :                               NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     558             : 
     559           0 :       NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
     560             :                      ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
     561             :                       "buf=%.*s, return(written)=%d,  url=%s\n",
     562             :                       this, npp, streamPosition, numtowrite,
     563             :                       numtowrite, ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url));
     564             : 
     565           0 :       if (mStreamState == eStreamStopped) {
     566             :         // The plugin called NPN_DestroyStream() from within
     567             :         // NPP_Write(), kill the stream.
     568           0 :         return NS_BINDING_ABORTED;
     569             :       }
     570             : 
     571           0 :       if (writeCount > 0) {
     572           0 :         NS_ASSERTION(writeCount <= mStreamBufferByteCount,
     573             :                      "Plugin read past the end of the available data!");
     574             : 
     575           0 :         writeCount = std::min(writeCount, mStreamBufferByteCount);
     576           0 :         mStreamBufferByteCount -= writeCount;
     577             : 
     578           0 :         streamPosition += writeCount;
     579             : 
     580           0 :         zeroBytesWriteCount = 0;
     581             : 
     582           0 :         if (mStreamBufferByteCount > 0) {
     583             :           // This alignment code is most likely bogus, but we'll leave
     584             :           // it in for now in case it matters for some plugins on some
     585             :           // architectures. Who knows...
     586           0 :           if (writeCount % sizeof(intptr_t)) {
     587             :             // memmove will take care  about alignment
     588           0 :             memmove(mStreamBuffer, ptrStreamBuffer + writeCount,
     589           0 :                     mStreamBufferByteCount);
     590           0 :             ptrStreamBuffer = mStreamBuffer;
     591             :           } else {
     592             :             // if aligned we can use ptrStreamBuffer += to eliminate
     593             :             // memmove()
     594           0 :             ptrStreamBuffer += writeCount;
     595             :           }
     596             :         }
     597           0 :       } else if (writeCount == 0) {
     598             :         // if NPP_Write() returns writeCount == 0 lets say 3 times in
     599             :         // a row, suspend the request and continue feeding the plugin
     600             :         // the data we got so far. Once that data is consumed, we'll
     601             :         // resume the request.
     602           0 :         if (mIsSuspended || ++zeroBytesWriteCount == 3) {
     603           0 :           if (!mIsSuspended) {
     604           0 :             SuspendRequest();
     605             :           }
     606             : 
     607             :           // Break out of the for loop, but keep going through the
     608             :           // while loop in case there's more data to read from the
     609             :           // input stream.
     610             : 
     611           0 :           break;
     612             :         }
     613             :       } else {
     614             :         // Something's really wrong, kill the stream.
     615           0 :         rv = NS_ERROR_FAILURE;
     616             : 
     617           0 :         break;
     618             :       }
     619             :     } // end of inner while loop
     620             : 
     621           0 :     if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) {
     622           0 :       memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount);
     623             :     }
     624             :   }
     625             : 
     626           0 :   if (streamPosition != streamOffset) {
     627             :     // The plugin didn't consume all available data, or consumed some
     628             :     // of our cached data while we're pumping cached data. Adjust the
     629             :     // plugin info's stream offset to match reality, except if the
     630             :     // plugin info's stream offset was set by a re-entering
     631             :     // NPN_RequestRead() call.
     632             : 
     633             :     int32_t postWriteStreamPosition;
     634           0 :     streamPeer->GetStreamOffset(&postWriteStreamPosition);
     635             : 
     636           0 :     if (postWriteStreamPosition == streamOffset) {
     637           0 :       streamPeer->SetStreamOffset(streamPosition);
     638             :     }
     639             :   }
     640             : 
     641           0 :   return rv;
     642             : }
     643             : 
     644             : nsresult
     645           0 : nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamPeer,
     646             :                                              const char* fileName)
     647             : {
     648           0 :   if (!mInst || !mInst->CanFireNotifications())
     649           0 :     return NS_ERROR_FAILURE;
     650             : 
     651           0 :   PluginDestructionGuard guard(mInst);
     652             : 
     653           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     654           0 :   if (!plugin || !plugin->GetLibrary())
     655           0 :     return NS_ERROR_FAILURE;
     656             : 
     657           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     658             : 
     659           0 :   if (!pluginFunctions->asfile)
     660           0 :     return NS_ERROR_FAILURE;
     661             : 
     662             :   NPP npp;
     663           0 :   mInst->GetNPP(&npp);
     664             : 
     665           0 :   NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst,
     666             :                         NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     667             : 
     668           0 :   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     669             :                  ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
     670             :                   this, npp, mNPStreamWrapper->mNPStream.url, fileName));
     671             : 
     672           0 :   return NS_OK;
     673             : }
     674             : 
     675             : nsresult
     676           0 : nsNPAPIPluginStreamListener::OnStopBinding(nsPluginStreamListenerPeer* streamPeer,
     677             :                                            nsresult status)
     678             : {
     679           0 :   if (NS_FAILED(status)) {
     680             :     // The stream was destroyed, or died for some reason. Make sure we
     681             :     // cancel the underlying request.
     682           0 :     if (mStreamListenerPeer) {
     683           0 :       mStreamListenerPeer->CancelRequests(status);
     684             :     }
     685             :   }
     686             : 
     687           0 :   if (!mInst || !mInst->CanFireNotifications()) {
     688           0 :     StopDataPump();
     689           0 :     return NS_ERROR_FAILURE;
     690             :   }
     691             : 
     692             :   // We need to detect that the stop is due to async stream init completion.
     693           0 :   if (mStreamStopMode == eDoDeferredStop) {
     694             :     // We shouldn't be delivering this until async init is done
     695           0 :     mStreamStopMode = eStopPending;
     696           0 :     mPendingStopBindingStatus = status;
     697           0 :     if (!mDataPumpTimer) {
     698           0 :       StartDataPump();
     699             :     }
     700           0 :     return NS_OK;
     701             :   }
     702             : 
     703           0 :   StopDataPump();
     704             : 
     705           0 :   NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
     706           0 :   if (mRedirectDenied || status == NS_BINDING_ABORTED) {
     707           0 :     reason = NPRES_USER_BREAK;
     708             :   }
     709             : 
     710             :   // The following code can result in the deletion of 'this'. Don't
     711             :   // assume we are alive after this!
     712             :   //
     713             :   // Delay cleanup if the stream is of type NP_SEEK and status isn't
     714             :   // NS_BINDING_ABORTED (meaning the plugin hasn't called NPN_DestroyStream).
     715             :   // This is because even though we're done delivering data the plugin may
     716             :   // want to seek. Eventually either the plugin will call NPN_DestroyStream
     717             :   // or we'll perform cleanup when the instance goes away. See bug 91140.
     718           0 :   if (mStreamType != NP_SEEK ||
     719           0 :       (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) {
     720           0 :     return CleanUpStream(reason);
     721             :   }
     722             : 
     723           0 :   return NS_OK;
     724             : }
     725             : 
     726             : nsresult
     727           0 : nsNPAPIPluginStreamListener::GetStreamType(int32_t *result)
     728             : {
     729           0 :   *result = mStreamType;
     730           0 :   return NS_OK;
     731             : }
     732             : 
     733             : bool
     734           0 : nsNPAPIPluginStreamListener::MaybeRunStopBinding()
     735             : {
     736           0 :   if (mIsSuspended || mStreamStopMode != eStopPending) {
     737           0 :     return false;
     738             :   }
     739           0 :   OnStopBinding(mStreamListenerPeer, mPendingStopBindingStatus);
     740           0 :   mStreamStopMode = eNormalStop;
     741           0 :   return true;
     742             : }
     743             : 
     744             : NS_IMETHODIMP
     745           0 : nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer)
     746             : {
     747           0 :   NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?");
     748             : 
     749           0 :   int32_t oldStreamBufferByteCount = mStreamBufferByteCount;
     750             : 
     751           0 :   nsresult rv = OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount);
     752             : 
     753           0 :   if (NS_FAILED(rv)) {
     754             :     // We ran into an error, no need to keep firing this timer then.
     755           0 :     StopDataPump();
     756           0 :     MaybeRunStopBinding();
     757           0 :     return NS_OK;
     758             :   }
     759             : 
     760           0 :   if (mStreamBufferByteCount != oldStreamBufferByteCount &&
     761           0 :       ((mStreamState == eStreamTypeSet && mStreamBufferByteCount < 1024) ||
     762           0 :        mStreamBufferByteCount == 0)) {
     763             :         // The plugin read some data and we've got less than 1024 bytes in
     764             :         // our buffer (or its empty and the stream is already
     765             :         // done). Resume the request so that we get more data off the
     766             :         // network.
     767           0 :         ResumeRequest();
     768             :         // Necko will pump data now that we've resumed the request.
     769           0 :         StopDataPump();
     770             :       }
     771             : 
     772           0 :   MaybeRunStopBinding();
     773           0 :   return NS_OK;
     774             : }
     775             : 
     776             : NS_IMETHODIMP
     777           0 : nsNPAPIPluginStreamListener::StatusLine(const char* line)
     778             : {
     779           0 :   mResponseHeaders.Append(line);
     780           0 :   mResponseHeaders.Append('\n');
     781           0 :   return NS_OK;
     782             : }
     783             : 
     784             : NS_IMETHODIMP
     785           0 : nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName,
     786             :                                                const char* headerValue)
     787             : {
     788           0 :   mResponseHeaders.Append(headerName);
     789           0 :   mResponseHeaders.AppendLiteral(": ");
     790           0 :   mResponseHeaders.Append(headerValue);
     791           0 :   mResponseHeaders.Append('\n');
     792           0 :   return NS_OK;
     793             : }
     794             : 
     795             : bool
     796           0 : nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel,
     797             :                                                         nsIAsyncVerifyRedirectCallback* callback)
     798             : {
     799           0 :   nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel);
     800           0 :   nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel);
     801           0 :   if (!oldHttpChannel || !newHttpChannel) {
     802           0 :     return false;
     803             :   }
     804             : 
     805           0 :   if (!mInst || !mInst->CanFireNotifications()) {
     806           0 :     return false;
     807             :   }
     808             : 
     809           0 :   nsNPAPIPlugin* plugin = mInst->GetPlugin();
     810           0 :   if (!plugin || !plugin->GetLibrary()) {
     811           0 :     return false;
     812             :   }
     813             : 
     814           0 :   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
     815           0 :   if (!pluginFunctions->urlredirectnotify) {
     816           0 :     return false;
     817             :   }
     818             : 
     819             :   // A non-null closure is required for redirect handling support.
     820           0 :   if (mNPStreamWrapper->mNPStream.notifyData) {
     821             :     uint32_t status;
     822           0 :     if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) {
     823           0 :       nsCOMPtr<nsIURI> uri;
     824           0 :       if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) {
     825           0 :         nsAutoCString spec;
     826           0 :         if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) {
     827             :           // At this point the plugin will be responsible for making the callback
     828             :           // so save the callback object.
     829           0 :           mHTTPRedirectCallback = callback;
     830             : 
     831             :           NPP npp;
     832           0 :           mInst->GetNPP(&npp);
     833             : #if defined(XP_WIN)
     834             :           NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst,
     835             :                                 NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     836             : #else
     837             :           MAIN_THREAD_JNI_REF_GUARD;
     838           0 :           (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData);
     839             : #endif
     840           0 :           return true;
     841             :         }
     842             :       }
     843             :     }
     844             :   }
     845             : 
     846           0 :   callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
     847           0 :   return true;
     848             : }
     849             : 
     850             : void
     851           0 : nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow)
     852             : {
     853           0 :   if (mHTTPRedirectCallback) {
     854           0 :     mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK : NS_ERROR_FAILURE);
     855           0 :     mRedirectDenied = allow ? false : true;
     856           0 :     mHTTPRedirectCallback = nullptr;
     857             :   }
     858           0 : }
     859             : 
     860             : void*
     861           0 : nsNPAPIPluginStreamListener::GetNotifyData()
     862             : {
     863           0 :   if (mNPStreamWrapper) {
     864           0 :     return mNPStreamWrapper->mNPStream.notifyData;
     865             :   }
     866           0 :   return nullptr;
     867             : }

Generated by: LCOV version 1.13