1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,903 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNPAPIPluginStreamListener.h" 1.10 +#include "plstr.h" 1.11 +#include "prmem.h" 1.12 +#include "nsDirectoryServiceDefs.h" 1.13 +#include "nsDirectoryServiceUtils.h" 1.14 +#include "nsIFile.h" 1.15 +#include "nsNetUtil.h" 1.16 +#include "nsPluginHost.h" 1.17 +#include "nsNPAPIPlugin.h" 1.18 +#include "nsPluginLogging.h" 1.19 +#include "nsPluginStreamListenerPeer.h" 1.20 + 1.21 +#include <stdint.h> 1.22 +#include <algorithm> 1.23 + 1.24 +nsNPAPIStreamWrapper::nsNPAPIStreamWrapper(nsIOutputStream *outputStream, 1.25 + nsNPAPIPluginStreamListener *streamListener) 1.26 +{ 1.27 + mOutputStream = outputStream; 1.28 + mStreamListener = streamListener; 1.29 + 1.30 + memset(&mNPStream, 0, sizeof(mNPStream)); 1.31 + mNPStream.ndata = static_cast<void*>(this); 1.32 +} 1.33 + 1.34 +nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper() 1.35 +{ 1.36 + if (mOutputStream) { 1.37 + mOutputStream->Close(); 1.38 + } 1.39 +} 1.40 + 1.41 +NS_IMPL_ISUPPORTS(nsPluginStreamToFile, nsIOutputStream) 1.42 + 1.43 +nsPluginStreamToFile::nsPluginStreamToFile(const char* target, 1.44 + nsIPluginInstanceOwner* owner) 1.45 +: mTarget(PL_strdup(target)), 1.46 +mOwner(owner) 1.47 +{ 1.48 + nsresult rv; 1.49 + nsCOMPtr<nsIFile> pluginTmp; 1.50 + rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pluginTmp)); 1.51 + if (NS_FAILED(rv)) return; 1.52 + 1.53 + mTempFile = do_QueryInterface(pluginTmp, &rv); 1.54 + if (NS_FAILED(rv)) return; 1.55 + 1.56 + // need to create a file with a unique name - use target as the basis 1.57 + rv = mTempFile->AppendNative(nsDependentCString(target)); 1.58 + if (NS_FAILED(rv)) return; 1.59 + 1.60 + // Yes, make it unique. 1.61 + rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700); 1.62 + if (NS_FAILED(rv)) return; 1.63 + 1.64 + // create the file 1.65 + rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600); 1.66 + if (NS_FAILED(rv)) 1.67 + return; 1.68 + 1.69 + // construct the URL we'll use later in calls to GetURL() 1.70 + NS_GetURLSpecFromFile(mTempFile, mFileURL); 1.71 + 1.72 +#ifdef DEBUG 1.73 + printf("File URL = %s\n", mFileURL.get()); 1.74 +#endif 1.75 +} 1.76 + 1.77 +nsPluginStreamToFile::~nsPluginStreamToFile() 1.78 +{ 1.79 + // should we be deleting mTempFile here? 1.80 + if (nullptr != mTarget) 1.81 + PL_strfree(mTarget); 1.82 +} 1.83 + 1.84 +NS_IMETHODIMP 1.85 +nsPluginStreamToFile::Flush() 1.86 +{ 1.87 + return NS_OK; 1.88 +} 1.89 + 1.90 +NS_IMETHODIMP 1.91 +nsPluginStreamToFile::Write(const char* aBuf, uint32_t aCount, 1.92 + uint32_t *aWriteCount) 1.93 +{ 1.94 + mOutputStream->Write(aBuf, aCount, aWriteCount); 1.95 + mOutputStream->Flush(); 1.96 + mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0); 1.97 + 1.98 + return NS_OK; 1.99 +} 1.100 + 1.101 +NS_IMETHODIMP 1.102 +nsPluginStreamToFile::WriteFrom(nsIInputStream *inStr, uint32_t count, 1.103 + uint32_t *_retval) 1.104 +{ 1.105 + NS_NOTREACHED("WriteFrom"); 1.106 + return NS_ERROR_NOT_IMPLEMENTED; 1.107 +} 1.108 + 1.109 +NS_IMETHODIMP 1.110 +nsPluginStreamToFile::WriteSegments(nsReadSegmentFun reader, void * closure, 1.111 + uint32_t count, uint32_t *_retval) 1.112 +{ 1.113 + NS_NOTREACHED("WriteSegments"); 1.114 + return NS_ERROR_NOT_IMPLEMENTED; 1.115 +} 1.116 + 1.117 +NS_IMETHODIMP 1.118 +nsPluginStreamToFile::IsNonBlocking(bool *aNonBlocking) 1.119 +{ 1.120 + *aNonBlocking = false; 1.121 + return NS_OK; 1.122 +} 1.123 + 1.124 +NS_IMETHODIMP 1.125 +nsPluginStreamToFile::Close(void) 1.126 +{ 1.127 + mOutputStream->Close(); 1.128 + mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0); 1.129 + return NS_OK; 1.130 +} 1.131 + 1.132 +// nsNPAPIPluginStreamListener Methods 1.133 + 1.134 +NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener, 1.135 + nsITimerCallback, nsIHTTPHeaderListener) 1.136 + 1.137 +nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, 1.138 + void* notifyData, 1.139 + const char* aURL) 1.140 +: mStreamBuffer(nullptr), 1.141 +mNotifyURL(aURL ? PL_strdup(aURL) : nullptr), 1.142 +mInst(inst), 1.143 +mStreamBufferSize(0), 1.144 +mStreamBufferByteCount(0), 1.145 +mStreamType(NP_NORMAL), 1.146 +mStreamStarted(false), 1.147 +mStreamCleanedUp(false), 1.148 +mCallNotify(notifyData ? true : false), 1.149 +mIsSuspended(false), 1.150 +mIsPluginInitJSStream(mInst->mInPluginInitCall && 1.151 + aURL && strncmp(aURL, "javascript:", 1.152 + sizeof("javascript:") - 1) == 0), 1.153 +mRedirectDenied(false), 1.154 +mResponseHeaderBuf(nullptr) 1.155 +{ 1.156 + mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this); 1.157 + mNPStreamWrapper->mNPStream.notifyData = notifyData; 1.158 +} 1.159 + 1.160 +nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener() 1.161 +{ 1.162 + // remove this from the plugin instance's stream list 1.163 + nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners(); 1.164 + streamListeners->RemoveElement(this); 1.165 + 1.166 + // For those cases when NewStream is never called, we still may need 1.167 + // to fire a notification callback. Return network error as fallback 1.168 + // reason because for other cases, notify should have already been 1.169 + // called for other reasons elsewhere. 1.170 + CallURLNotify(NPRES_NETWORK_ERR); 1.171 + 1.172 + // lets get rid of the buffer 1.173 + if (mStreamBuffer) { 1.174 + PR_Free(mStreamBuffer); 1.175 + mStreamBuffer=nullptr; 1.176 + } 1.177 + 1.178 + if (mNotifyURL) 1.179 + PL_strfree(mNotifyURL); 1.180 + 1.181 + if (mResponseHeaderBuf) 1.182 + PL_strfree(mResponseHeaderBuf); 1.183 + 1.184 + if (mNPStreamWrapper) { 1.185 + delete mNPStreamWrapper; 1.186 + } 1.187 +} 1.188 + 1.189 +nsresult 1.190 +nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason) 1.191 +{ 1.192 + nsresult rv = NS_ERROR_FAILURE; 1.193 + 1.194 + // Various bits of code in the rest of this method may result in the 1.195 + // deletion of this object. Use a KungFuDeathGrip to keep ourselves 1.196 + // alive during cleanup. 1.197 + nsRefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this); 1.198 + 1.199 + if (mStreamCleanedUp) 1.200 + return NS_OK; 1.201 + 1.202 + mStreamCleanedUp = true; 1.203 + 1.204 + StopDataPump(); 1.205 + 1.206 + // Release any outstanding redirect callback. 1.207 + if (mHTTPRedirectCallback) { 1.208 + mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE); 1.209 + mHTTPRedirectCallback = nullptr; 1.210 + } 1.211 + 1.212 + // Seekable streams have an extra addref when they are created which must 1.213 + // be matched here. 1.214 + if (NP_SEEK == mStreamType && mStreamStarted) 1.215 + NS_RELEASE_THIS(); 1.216 + 1.217 + if (mStreamListenerPeer) { 1.218 + mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED); 1.219 + mStreamListenerPeer = nullptr; 1.220 + } 1.221 + 1.222 + if (!mInst || !mInst->CanFireNotifications()) 1.223 + return rv; 1.224 + 1.225 + PluginDestructionGuard guard(mInst); 1.226 + 1.227 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.228 + if (!plugin || !plugin->GetLibrary()) 1.229 + return rv; 1.230 + 1.231 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.232 + 1.233 + NPP npp; 1.234 + mInst->GetNPP(&npp); 1.235 + 1.236 + if (mStreamStarted && pluginFunctions->destroystream) { 1.237 + NPPAutoPusher nppPusher(npp); 1.238 + 1.239 + NPError error; 1.240 + NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst, 1.241 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.242 + 1.243 + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, 1.244 + ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n", 1.245 + this, npp, reason, error, mNPStreamWrapper->mNPStream.url)); 1.246 + 1.247 + if (error == NPERR_NO_ERROR) 1.248 + rv = NS_OK; 1.249 + } 1.250 + 1.251 + mStreamStarted = false; 1.252 + 1.253 + // fire notification back to plugin, just like before 1.254 + CallURLNotify(reason); 1.255 + 1.256 + return rv; 1.257 +} 1.258 + 1.259 +void 1.260 +nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason) 1.261 +{ 1.262 + if (!mCallNotify || !mInst || !mInst->CanFireNotifications()) 1.263 + return; 1.264 + 1.265 + PluginDestructionGuard guard(mInst); 1.266 + 1.267 + mCallNotify = false; // only do this ONCE and prevent recursion 1.268 + 1.269 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.270 + if (!plugin || !plugin->GetLibrary()) 1.271 + return; 1.272 + 1.273 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.274 + 1.275 + if (pluginFunctions->urlnotify) { 1.276 + NPP npp; 1.277 + mInst->GetNPP(&npp); 1.278 + 1.279 + NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst, 1.280 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.281 + 1.282 + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, 1.283 + ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n", 1.284 + this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL)); 1.285 + } 1.286 +} 1.287 + 1.288 +nsresult 1.289 +nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer) 1.290 +{ 1.291 + if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp) 1.292 + return NS_ERROR_FAILURE; 1.293 + 1.294 + PluginDestructionGuard guard(mInst); 1.295 + 1.296 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.297 + if (!plugin || !plugin->GetLibrary()) 1.298 + return NS_ERROR_FAILURE; 1.299 + 1.300 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.301 + 1.302 + if (!pluginFunctions->newstream) 1.303 + return NS_ERROR_FAILURE; 1.304 + 1.305 + NPP npp; 1.306 + mInst->GetNPP(&npp); 1.307 + 1.308 + bool seekable; 1.309 + char* contentType; 1.310 + uint16_t streamType = NP_NORMAL; 1.311 + NPError error; 1.312 + 1.313 + streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url); 1.314 + streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end)); 1.315 + streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified)); 1.316 + streamPeer->IsSeekable(&seekable); 1.317 + streamPeer->GetContentType(&contentType); 1.318 + 1.319 + if (!mResponseHeaders.IsEmpty()) { 1.320 + mResponseHeaderBuf = PL_strdup(mResponseHeaders.get()); 1.321 + mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf; 1.322 + } 1.323 + 1.324 + mStreamListenerPeer = streamPeer; 1.325 + 1.326 + NPPAutoPusher nppPusher(npp); 1.327 + 1.328 + NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst, 1.329 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.330 + 1.331 + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, 1.332 + ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n", 1.333 + this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url)); 1.334 + 1.335 + if (error != NPERR_NO_ERROR) 1.336 + return NS_ERROR_FAILURE; 1.337 + 1.338 + switch(streamType) 1.339 + { 1.340 + case NP_NORMAL: 1.341 + mStreamType = NP_NORMAL; 1.342 + break; 1.343 + case NP_ASFILEONLY: 1.344 + mStreamType = NP_ASFILEONLY; 1.345 + break; 1.346 + case NP_ASFILE: 1.347 + mStreamType = NP_ASFILE; 1.348 + break; 1.349 + case NP_SEEK: 1.350 + mStreamType = NP_SEEK; 1.351 + // Seekable streams should continue to exist even after OnStopRequest 1.352 + // is fired, so we AddRef ourself an extra time and Release when the 1.353 + // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never 1.354 + // calls NPN_DestroyStream the stream will be destroyed before the plugin 1.355 + // instance is destroyed. 1.356 + NS_ADDREF_THIS(); 1.357 + break; 1.358 + default: 1.359 + return NS_ERROR_FAILURE; 1.360 + } 1.361 + 1.362 + mStreamStarted = true; 1.363 + return NS_OK; 1.364 +} 1.365 + 1.366 +void 1.367 +nsNPAPIPluginStreamListener::SuspendRequest() 1.368 +{ 1.369 + NS_ASSERTION(!mIsSuspended, 1.370 + "Suspending a request that's already suspended!"); 1.371 + 1.372 + nsresult rv = StartDataPump(); 1.373 + if (NS_FAILED(rv)) 1.374 + return; 1.375 + 1.376 + mIsSuspended = true; 1.377 + 1.378 + if (mStreamListenerPeer) { 1.379 + mStreamListenerPeer->SuspendRequests(); 1.380 + } 1.381 +} 1.382 + 1.383 +void 1.384 +nsNPAPIPluginStreamListener::ResumeRequest() 1.385 +{ 1.386 + mStreamListenerPeer->ResumeRequests(); 1.387 + mIsSuspended = false; 1.388 +} 1.389 + 1.390 +nsresult 1.391 +nsNPAPIPluginStreamListener::StartDataPump() 1.392 +{ 1.393 + nsresult rv; 1.394 + mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); 1.395 + NS_ENSURE_SUCCESS(rv, rv); 1.396 + 1.397 + // Start pumping data to the plugin every 100ms until it obeys and 1.398 + // eats the data. 1.399 + return mDataPumpTimer->InitWithCallback(this, 100, 1.400 + nsITimer::TYPE_REPEATING_SLACK); 1.401 +} 1.402 + 1.403 +void 1.404 +nsNPAPIPluginStreamListener::StopDataPump() 1.405 +{ 1.406 + if (mDataPumpTimer) { 1.407 + mDataPumpTimer->Cancel(); 1.408 + mDataPumpTimer = nullptr; 1.409 + } 1.410 +} 1.411 + 1.412 +// Return true if a javascript: load that was started while the plugin 1.413 +// was being initialized is still in progress. 1.414 +bool 1.415 +nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress() 1.416 +{ 1.417 + if (!mInst) 1.418 + return false; 1.419 + 1.420 + nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners(); 1.421 + for (unsigned int i = 0; i < streamListeners->Length(); i++) { 1.422 + if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) { 1.423 + return true; 1.424 + } 1.425 + } 1.426 + 1.427 + return false; 1.428 +} 1.429 + 1.430 +// This method is called when there's more data available off the 1.431 +// network, but it's also called from our data pump when we're feeding 1.432 +// the plugin data that we already got off the network, but the plugin 1.433 +// was unable to consume it at the point it arrived. In the case when 1.434 +// the plugin pump calls this method, the input argument will be null, 1.435 +// and the length will be the number of bytes available in our 1.436 +// internal buffer. 1.437 +nsresult 1.438 +nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamPeer, 1.439 + nsIInputStream* input, 1.440 + uint32_t length) 1.441 +{ 1.442 + if (!length || !mInst || !mInst->CanFireNotifications()) 1.443 + return NS_ERROR_FAILURE; 1.444 + 1.445 + PluginDestructionGuard guard(mInst); 1.446 + 1.447 + // Just in case the caller switches plugin info on us. 1.448 + mStreamListenerPeer = streamPeer; 1.449 + 1.450 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.451 + if (!plugin || !plugin->GetLibrary()) 1.452 + return NS_ERROR_FAILURE; 1.453 + 1.454 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.455 + 1.456 + // check out if plugin implements NPP_Write call 1.457 + if (!pluginFunctions->write) 1.458 + return NS_ERROR_FAILURE; // it'll cancel necko transaction 1.459 + 1.460 + if (!mStreamBuffer) { 1.461 + // To optimize the mem usage & performance we have to allocate 1.462 + // mStreamBuffer here in first ODA when length of data available 1.463 + // in input stream is known. mStreamBuffer will be freed in DTOR. 1.464 + // we also have to remember the size of that buff to make safe 1.465 + // consecutive Read() calls form input stream into our buff. 1.466 + 1.467 + uint32_t contentLength; 1.468 + streamPeer->GetLength(&contentLength); 1.469 + 1.470 + mStreamBufferSize = std::max(length, contentLength); 1.471 + 1.472 + // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER 1.473 + // (16k). This buffer will grow if needed, as in the case where 1.474 + // we're getting data faster than the plugin can process it. 1.475 + mStreamBufferSize = std::min(mStreamBufferSize, 1.476 + uint32_t(MAX_PLUGIN_NECKO_BUFFER)); 1.477 + 1.478 + mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize); 1.479 + if (!mStreamBuffer) 1.480 + return NS_ERROR_OUT_OF_MEMORY; 1.481 + } 1.482 + 1.483 + // prepare NPP_ calls params 1.484 + NPP npp; 1.485 + mInst->GetNPP(&npp); 1.486 + 1.487 + int32_t streamPosition; 1.488 + streamPeer->GetStreamOffset(&streamPosition); 1.489 + int32_t streamOffset = streamPosition; 1.490 + 1.491 + if (input) { 1.492 + streamOffset += length; 1.493 + 1.494 + // Set new stream offset for the next ODA call regardless of how 1.495 + // following NPP_Write call will behave we pretend to consume all 1.496 + // data from the input stream. It's possible that current steam 1.497 + // position will be overwritten from NPP_RangeRequest call made 1.498 + // from NPP_Write, so we cannot call SetStreamOffset after 1.499 + // NPP_Write. 1.500 + // 1.501 + // Note: there is a special case when data flow should be 1.502 + // temporarily stopped if NPP_WriteReady returns 0 (bug #89270) 1.503 + streamPeer->SetStreamOffset(streamOffset); 1.504 + 1.505 + // set new end in case the content is compressed 1.506 + // initial end is less than end of decompressed stream 1.507 + // and some plugins (e.g. acrobat) can fail. 1.508 + if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset) 1.509 + mNPStreamWrapper->mNPStream.end = streamOffset; 1.510 + } 1.511 + 1.512 + nsresult rv = NS_OK; 1.513 + while (NS_SUCCEEDED(rv) && length > 0) { 1.514 + if (input && length) { 1.515 + if (mStreamBufferSize < mStreamBufferByteCount + length && mIsSuspended) { 1.516 + // We're in the ::OnDataAvailable() call that we might get 1.517 + // after suspending a request, or we suspended the request 1.518 + // from within this ::OnDataAvailable() call while there's 1.519 + // still data in the input, and we don't have enough space to 1.520 + // store what we got off the network. Reallocate our internal 1.521 + // buffer. 1.522 + mStreamBufferSize = mStreamBufferByteCount + length; 1.523 + char *buf = (char*)PR_Realloc(mStreamBuffer, mStreamBufferSize); 1.524 + if (!buf) 1.525 + return NS_ERROR_OUT_OF_MEMORY; 1.526 + 1.527 + mStreamBuffer = buf; 1.528 + } 1.529 + 1.530 + uint32_t bytesToRead = 1.531 + std::min(length, mStreamBufferSize - mStreamBufferByteCount); 1.532 + 1.533 + uint32_t amountRead = 0; 1.534 + rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead, 1.535 + &amountRead); 1.536 + NS_ENSURE_SUCCESS(rv, rv); 1.537 + 1.538 + if (amountRead == 0) { 1.539 + NS_NOTREACHED("input->Read() returns no data, it's almost impossible " 1.540 + "to get here"); 1.541 + 1.542 + break; 1.543 + } 1.544 + 1.545 + mStreamBufferByteCount += amountRead; 1.546 + length -= amountRead; 1.547 + } else { 1.548 + // No input, nothing to read. Set length to 0 so that we don't 1.549 + // keep iterating through this outer loop any more. 1.550 + 1.551 + length = 0; 1.552 + } 1.553 + 1.554 + // Temporary pointer to the beginning of the data we're writing as 1.555 + // we loop and feed the plugin data. 1.556 + char *ptrStreamBuffer = mStreamBuffer; 1.557 + 1.558 + // it is possible plugin's NPP_Write() returns 0 byte consumed. We 1.559 + // use zeroBytesWriteCount to count situation like this and break 1.560 + // the loop 1.561 + int32_t zeroBytesWriteCount = 0; 1.562 + 1.563 + // mStreamBufferByteCount tells us how many bytes there are in the 1.564 + // buffer. WriteReady returns to us how many bytes the plugin is 1.565 + // ready to handle. 1.566 + while (mStreamBufferByteCount > 0) { 1.567 + int32_t numtowrite; 1.568 + if (pluginFunctions->writeready) { 1.569 + NPPAutoPusher nppPusher(npp); 1.570 + 1.571 + NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst, 1.572 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.573 + NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, 1.574 + ("NPP WriteReady called: this=%p, npp=%p, " 1.575 + "return(towrite)=%d, url=%s\n", 1.576 + this, npp, numtowrite, mNPStreamWrapper->mNPStream.url)); 1.577 + 1.578 + if (!mStreamStarted) { 1.579 + // The plugin called NPN_DestroyStream() from within 1.580 + // NPP_WriteReady(), kill the stream. 1.581 + 1.582 + return NS_BINDING_ABORTED; 1.583 + } 1.584 + 1.585 + // if WriteReady returned 0, the plugin is not ready to handle 1.586 + // the data, suspend the stream (if it isn't already 1.587 + // suspended). 1.588 + // 1.589 + // Also suspend the stream if the stream we're loading is not 1.590 + // a javascript: URL load that was initiated during plugin 1.591 + // initialization and there currently is such a stream 1.592 + // loading. This is done to work around a Windows Media Player 1.593 + // plugin bug where it can't deal with being fed data for 1.594 + // other streams while it's waiting for data from the 1.595 + // javascript: URL loads it requests during 1.596 + // initialization. See bug 386493 for more details. 1.597 + 1.598 + if (numtowrite <= 0 || 1.599 + (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) { 1.600 + if (!mIsSuspended) { 1.601 + SuspendRequest(); 1.602 + } 1.603 + 1.604 + // Break out of the inner loop, but keep going through the 1.605 + // outer loop in case there's more data to read from the 1.606 + // input stream. 1.607 + 1.608 + break; 1.609 + } 1.610 + 1.611 + numtowrite = std::min(numtowrite, mStreamBufferByteCount); 1.612 + } else { 1.613 + // if WriteReady is not supported by the plugin, just write 1.614 + // the whole buffer 1.615 + numtowrite = mStreamBufferByteCount; 1.616 + } 1.617 + 1.618 + NPPAutoPusher nppPusher(npp); 1.619 + 1.620 + int32_t writeCount = 0; // bytes consumed by plugin instance 1.621 + NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst, 1.622 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.623 + 1.624 + NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, 1.625 + ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, " 1.626 + "buf=%s, return(written)=%d, url=%s\n", 1.627 + this, npp, streamPosition, numtowrite, 1.628 + ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url)); 1.629 + 1.630 + if (!mStreamStarted) { 1.631 + // The plugin called NPN_DestroyStream() from within 1.632 + // NPP_Write(), kill the stream. 1.633 + return NS_BINDING_ABORTED; 1.634 + } 1.635 + 1.636 + if (writeCount > 0) { 1.637 + NS_ASSERTION(writeCount <= mStreamBufferByteCount, 1.638 + "Plugin read past the end of the available data!"); 1.639 + 1.640 + writeCount = std::min(writeCount, mStreamBufferByteCount); 1.641 + mStreamBufferByteCount -= writeCount; 1.642 + 1.643 + streamPosition += writeCount; 1.644 + 1.645 + zeroBytesWriteCount = 0; 1.646 + 1.647 + if (mStreamBufferByteCount > 0) { 1.648 + // This alignment code is most likely bogus, but we'll leave 1.649 + // it in for now in case it matters for some plugins on some 1.650 + // architectures. Who knows... 1.651 + if (writeCount % sizeof(intptr_t)) { 1.652 + // memmove will take care about alignment 1.653 + memmove(mStreamBuffer, ptrStreamBuffer + writeCount, 1.654 + mStreamBufferByteCount); 1.655 + ptrStreamBuffer = mStreamBuffer; 1.656 + } else { 1.657 + // if aligned we can use ptrStreamBuffer += to eliminate 1.658 + // memmove() 1.659 + ptrStreamBuffer += writeCount; 1.660 + } 1.661 + } 1.662 + } else if (writeCount == 0) { 1.663 + // if NPP_Write() returns writeCount == 0 lets say 3 times in 1.664 + // a row, suspend the request and continue feeding the plugin 1.665 + // the data we got so far. Once that data is consumed, we'll 1.666 + // resume the request. 1.667 + if (mIsSuspended || ++zeroBytesWriteCount == 3) { 1.668 + if (!mIsSuspended) { 1.669 + SuspendRequest(); 1.670 + } 1.671 + 1.672 + // Break out of the for loop, but keep going through the 1.673 + // while loop in case there's more data to read from the 1.674 + // input stream. 1.675 + 1.676 + break; 1.677 + } 1.678 + } else { 1.679 + // Something's really wrong, kill the stream. 1.680 + rv = NS_ERROR_FAILURE; 1.681 + 1.682 + break; 1.683 + } 1.684 + } // end of inner while loop 1.685 + 1.686 + if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) { 1.687 + memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount); 1.688 + } 1.689 + } 1.690 + 1.691 + if (streamPosition != streamOffset) { 1.692 + // The plugin didn't consume all available data, or consumed some 1.693 + // of our cached data while we're pumping cached data. Adjust the 1.694 + // plugin info's stream offset to match reality, except if the 1.695 + // plugin info's stream offset was set by a re-entering 1.696 + // NPN_RequestRead() call. 1.697 + 1.698 + int32_t postWriteStreamPosition; 1.699 + streamPeer->GetStreamOffset(&postWriteStreamPosition); 1.700 + 1.701 + if (postWriteStreamPosition == streamOffset) { 1.702 + streamPeer->SetStreamOffset(streamPosition); 1.703 + } 1.704 + } 1.705 + 1.706 + return rv; 1.707 +} 1.708 + 1.709 +nsresult 1.710 +nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamPeer, 1.711 + const char* fileName) 1.712 +{ 1.713 + if (!mInst || !mInst->CanFireNotifications()) 1.714 + return NS_ERROR_FAILURE; 1.715 + 1.716 + PluginDestructionGuard guard(mInst); 1.717 + 1.718 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.719 + if (!plugin || !plugin->GetLibrary()) 1.720 + return NS_ERROR_FAILURE; 1.721 + 1.722 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.723 + 1.724 + if (!pluginFunctions->asfile) 1.725 + return NS_ERROR_FAILURE; 1.726 + 1.727 + NPP npp; 1.728 + mInst->GetNPP(&npp); 1.729 + 1.730 + NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst, 1.731 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.732 + 1.733 + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, 1.734 + ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n", 1.735 + this, npp, mNPStreamWrapper->mNPStream.url, fileName)); 1.736 + 1.737 + return NS_OK; 1.738 +} 1.739 + 1.740 +nsresult 1.741 +nsNPAPIPluginStreamListener::OnStopBinding(nsPluginStreamListenerPeer* streamPeer, 1.742 + nsresult status) 1.743 +{ 1.744 + StopDataPump(); 1.745 + 1.746 + if (NS_FAILED(status)) { 1.747 + // The stream was destroyed, or died for some reason. Make sure we 1.748 + // cancel the underlying request. 1.749 + if (mStreamListenerPeer) { 1.750 + mStreamListenerPeer->CancelRequests(status); 1.751 + } 1.752 + } 1.753 + 1.754 + if (!mInst || !mInst->CanFireNotifications()) 1.755 + return NS_ERROR_FAILURE; 1.756 + 1.757 + NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE; 1.758 + if (mRedirectDenied || status == NS_BINDING_ABORTED) { 1.759 + reason = NPRES_USER_BREAK; 1.760 + } 1.761 + 1.762 + // The following code can result in the deletion of 'this'. Don't 1.763 + // assume we are alive after this! 1.764 + // 1.765 + // Delay cleanup if the stream is of type NP_SEEK and status isn't 1.766 + // NS_BINDING_ABORTED (meaning the plugin hasn't called NPN_DestroyStream). 1.767 + // This is because even though we're done delivering data the plugin may 1.768 + // want to seek. Eventually either the plugin will call NPN_DestroyStream 1.769 + // or we'll perform cleanup when the instance goes away. See bug 91140. 1.770 + if (mStreamType != NP_SEEK || 1.771 + (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) { 1.772 + return CleanUpStream(reason); 1.773 + } 1.774 + 1.775 + return NS_OK; 1.776 +} 1.777 + 1.778 +nsresult 1.779 +nsNPAPIPluginStreamListener::GetStreamType(int32_t *result) 1.780 +{ 1.781 + *result = mStreamType; 1.782 + return NS_OK; 1.783 +} 1.784 + 1.785 +NS_IMETHODIMP 1.786 +nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer) 1.787 +{ 1.788 + NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?"); 1.789 + 1.790 + int32_t oldStreamBufferByteCount = mStreamBufferByteCount; 1.791 + 1.792 + nsresult rv = OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount); 1.793 + 1.794 + if (NS_FAILED(rv)) { 1.795 + // We ran into an error, no need to keep firing this timer then. 1.796 + aTimer->Cancel(); 1.797 + return NS_OK; 1.798 + } 1.799 + 1.800 + if (mStreamBufferByteCount != oldStreamBufferByteCount && 1.801 + ((mStreamStarted && mStreamBufferByteCount < 1024) || 1.802 + mStreamBufferByteCount == 0)) { 1.803 + // The plugin read some data and we've got less than 1024 bytes in 1.804 + // our buffer (or its empty and the stream is already 1.805 + // done). Resume the request so that we get more data off the 1.806 + // network. 1.807 + ResumeRequest(); 1.808 + // Necko will pump data now that we've resumed the request. 1.809 + StopDataPump(); 1.810 + } 1.811 + 1.812 + return NS_OK; 1.813 +} 1.814 + 1.815 +NS_IMETHODIMP 1.816 +nsNPAPIPluginStreamListener::StatusLine(const char* line) 1.817 +{ 1.818 + mResponseHeaders.Append(line); 1.819 + mResponseHeaders.Append('\n'); 1.820 + return NS_OK; 1.821 +} 1.822 + 1.823 +NS_IMETHODIMP 1.824 +nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName, 1.825 + const char* headerValue) 1.826 +{ 1.827 + mResponseHeaders.Append(headerName); 1.828 + mResponseHeaders.Append(": "); 1.829 + mResponseHeaders.Append(headerValue); 1.830 + mResponseHeaders.Append('\n'); 1.831 + return NS_OK; 1.832 +} 1.833 + 1.834 +bool 1.835 +nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel, 1.836 + nsIAsyncVerifyRedirectCallback* callback) 1.837 +{ 1.838 + nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel); 1.839 + nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel); 1.840 + if (!oldHttpChannel || !newHttpChannel) { 1.841 + return false; 1.842 + } 1.843 + 1.844 + if (!mInst || !mInst->CanFireNotifications()) { 1.845 + return false; 1.846 + } 1.847 + 1.848 + nsNPAPIPlugin* plugin = mInst->GetPlugin(); 1.849 + if (!plugin || !plugin->GetLibrary()) { 1.850 + return false; 1.851 + } 1.852 + 1.853 + NPPluginFuncs* pluginFunctions = plugin->PluginFuncs(); 1.854 + if (!pluginFunctions->urlredirectnotify) { 1.855 + return false; 1.856 + } 1.857 + 1.858 + // A non-null closure is required for redirect handling support. 1.859 + if (mNPStreamWrapper->mNPStream.notifyData) { 1.860 + uint32_t status; 1.861 + if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) { 1.862 + nsCOMPtr<nsIURI> uri; 1.863 + if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) { 1.864 + nsAutoCString spec; 1.865 + if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) { 1.866 + // At this point the plugin will be responsible for making the callback 1.867 + // so save the callback object. 1.868 + mHTTPRedirectCallback = callback; 1.869 + 1.870 + NPP npp; 1.871 + mInst->GetNPP(&npp); 1.872 +#if defined(XP_WIN) 1.873 + NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst, 1.874 + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); 1.875 +#else 1.876 + MAIN_THREAD_JNI_REF_GUARD; 1.877 + (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData); 1.878 +#endif 1.879 + return true; 1.880 + } 1.881 + } 1.882 + } 1.883 + } 1.884 + 1.885 + callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE); 1.886 + return true; 1.887 +} 1.888 + 1.889 +void 1.890 +nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow) 1.891 +{ 1.892 + if (mHTTPRedirectCallback) { 1.893 + mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK : NS_ERROR_FAILURE); 1.894 + mRedirectDenied = allow ? false : true; 1.895 + mHTTPRedirectCallback = nullptr; 1.896 + } 1.897 +} 1.898 + 1.899 +void* 1.900 +nsNPAPIPluginStreamListener::GetNotifyData() 1.901 +{ 1.902 + if (mNPStreamWrapper) { 1.903 + return mNPStreamWrapper->mNPStream.notifyData; 1.904 + } 1.905 + return nullptr; 1.906 +}