dom/plugins/base/nsNPAPIPluginStreamListener.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial