1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1276 @@ 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 "nsPluginStreamListenerPeer.h" 1.10 +#include "nsIStreamConverterService.h" 1.11 +#include "nsIHttpChannel.h" 1.12 +#include "nsIHttpChannelInternal.h" 1.13 +#include "nsIFileChannel.h" 1.14 +#include "nsMimeTypes.h" 1.15 +#include "nsISupportsPrimitives.h" 1.16 +#include "nsNetCID.h" 1.17 +#include "nsPluginLogging.h" 1.18 +#include "nsIURI.h" 1.19 +#include "nsIURL.h" 1.20 +#include "nsPluginHost.h" 1.21 +#include "nsIByteRangeRequest.h" 1.22 +#include "nsIMultiPartChannel.h" 1.23 +#include "nsIInputStreamTee.h" 1.24 +#include "nsPrintfCString.h" 1.25 +#include "nsIScriptGlobalObject.h" 1.26 +#include "nsIDocument.h" 1.27 +#include "nsIWebNavigation.h" 1.28 +#include "nsContentUtils.h" 1.29 +#include "nsNetUtil.h" 1.30 +#include "nsPluginNativeWindow.h" 1.31 +#include "GeckoProfiler.h" 1.32 +#include "nsPluginInstanceOwner.h" 1.33 +#include "nsDataHashtable.h" 1.34 + 1.35 +#define MAGIC_REQUEST_CONTEXT 0x01020304 1.36 + 1.37 +// nsPluginByteRangeStreamListener 1.38 + 1.39 +class nsPluginByteRangeStreamListener 1.40 + : public nsIStreamListener 1.41 + , public nsIInterfaceRequestor 1.42 +{ 1.43 +public: 1.44 + nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr); 1.45 + virtual ~nsPluginByteRangeStreamListener(); 1.46 + 1.47 + NS_DECL_ISUPPORTS 1.48 + NS_DECL_NSIREQUESTOBSERVER 1.49 + NS_DECL_NSISTREAMLISTENER 1.50 + NS_DECL_NSIINTERFACEREQUESTOR 1.51 + 1.52 +private: 1.53 + nsCOMPtr<nsIStreamListener> mStreamConverter; 1.54 + nsWeakPtr mWeakPtrPluginStreamListenerPeer; 1.55 + bool mRemoveMagicNumber; 1.56 +}; 1.57 + 1.58 +NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener, 1.59 + nsIRequestObserver, 1.60 + nsIStreamListener, 1.61 + nsIInterfaceRequestor) 1.62 + 1.63 +nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr) 1.64 +{ 1.65 + mWeakPtrPluginStreamListenerPeer = aWeakPtr; 1.66 + mRemoveMagicNumber = false; 1.67 +} 1.68 + 1.69 +nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener() 1.70 +{ 1.71 + mStreamConverter = 0; 1.72 + mWeakPtrPluginStreamListenerPeer = 0; 1.73 +} 1.74 + 1.75 +/** 1.76 + * Unwrap any byte-range requests so that we can check whether the base channel 1.77 + * is being tracked properly. 1.78 + */ 1.79 +static nsCOMPtr<nsIRequest> 1.80 +GetBaseRequest(nsIRequest* r) 1.81 +{ 1.82 + nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(r); 1.83 + if (!mp) 1.84 + return r; 1.85 + 1.86 + nsCOMPtr<nsIChannel> base; 1.87 + mp->GetBaseChannel(getter_AddRefs(base)); 1.88 + return already_AddRefed<nsIRequest>(base.forget()); 1.89 +} 1.90 + 1.91 +NS_IMETHODIMP 1.92 +nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt) 1.93 +{ 1.94 + nsresult rv; 1.95 + 1.96 + nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); 1.97 + if (!finalStreamListener) 1.98 + return NS_ERROR_FAILURE; 1.99 + 1.100 + nsPluginStreamListenerPeer *pslp = 1.101 + static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get()); 1.102 + 1.103 + NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1, 1.104 + "Untracked byte-range request?"); 1.105 + 1.106 + nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); 1.107 + if (NS_SUCCEEDED(rv)) { 1.108 + rv = serv->AsyncConvertData(MULTIPART_BYTERANGES, 1.109 + "*/*", 1.110 + finalStreamListener, 1.111 + nullptr, 1.112 + getter_AddRefs(mStreamConverter)); 1.113 + if (NS_SUCCEEDED(rv)) { 1.114 + rv = mStreamConverter->OnStartRequest(request, ctxt); 1.115 + if (NS_SUCCEEDED(rv)) 1.116 + return rv; 1.117 + } 1.118 + } 1.119 + mStreamConverter = 0; 1.120 + 1.121 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request)); 1.122 + if (!httpChannel) { 1.123 + return NS_ERROR_FAILURE; 1.124 + } 1.125 + 1.126 + uint32_t responseCode = 0; 1.127 + rv = httpChannel->GetResponseStatus(&responseCode); 1.128 + if (NS_FAILED(rv)) { 1.129 + return NS_ERROR_FAILURE; 1.130 + } 1.131 + 1.132 + if (responseCode != 200) { 1.133 + uint32_t wantsAllNetworkStreams = 0; 1.134 + rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams, 1.135 + &wantsAllNetworkStreams); 1.136 + // If the call returned an error code make sure we still use our default value. 1.137 + if (NS_FAILED(rv)) { 1.138 + wantsAllNetworkStreams = 0; 1.139 + } 1.140 + 1.141 + if (!wantsAllNetworkStreams){ 1.142 + return NS_ERROR_FAILURE; 1.143 + } 1.144 + } 1.145 + 1.146 + // if server cannot continue with byte range (206 status) and sending us whole object (200 status) 1.147 + // reset this seekable stream & try serve it to plugin instance as a file 1.148 + mStreamConverter = finalStreamListener; 1.149 + mRemoveMagicNumber = true; 1.150 + 1.151 + rv = pslp->ServeStreamAsFile(request, ctxt); 1.152 + return rv; 1.153 +} 1.154 + 1.155 +NS_IMETHODIMP 1.156 +nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt, 1.157 + nsresult status) 1.158 +{ 1.159 + if (!mStreamConverter) 1.160 + return NS_ERROR_FAILURE; 1.161 + 1.162 + nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); 1.163 + if (!finalStreamListener) 1.164 + return NS_ERROR_FAILURE; 1.165 + 1.166 + nsPluginStreamListenerPeer *pslp = 1.167 + static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get()); 1.168 + bool found = pslp->mRequests.RemoveObject(request); 1.169 + if (!found) { 1.170 + NS_ERROR("OnStopRequest received for untracked byte-range request!"); 1.171 + } 1.172 + 1.173 + if (mRemoveMagicNumber) { 1.174 + // remove magic number from container 1.175 + nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt); 1.176 + if (container) { 1.177 + uint32_t magicNumber = 0; 1.178 + container->GetData(&magicNumber); 1.179 + if (magicNumber == MAGIC_REQUEST_CONTEXT) { 1.180 + // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest() 1.181 + // set it to something that is not the magic number. 1.182 + container->SetData(0); 1.183 + } 1.184 + } else { 1.185 + NS_WARNING("Bad state of nsPluginByteRangeStreamListener"); 1.186 + } 1.187 + } 1.188 + 1.189 + return mStreamConverter->OnStopRequest(request, ctxt, status); 1.190 +} 1.191 + 1.192 +// CachedFileHolder 1.193 + 1.194 +CachedFileHolder::CachedFileHolder(nsIFile* cacheFile) 1.195 +: mFile(cacheFile) 1.196 +{ 1.197 + NS_ASSERTION(mFile, "Empty CachedFileHolder"); 1.198 +} 1.199 + 1.200 +CachedFileHolder::~CachedFileHolder() 1.201 +{ 1.202 + mFile->Remove(false); 1.203 +} 1.204 + 1.205 +void 1.206 +CachedFileHolder::AddRef() 1.207 +{ 1.208 + ++mRefCnt; 1.209 + NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this)); 1.210 +} 1.211 + 1.212 +void 1.213 +CachedFileHolder::Release() 1.214 +{ 1.215 + --mRefCnt; 1.216 + NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder"); 1.217 + if (0 == mRefCnt) 1.218 + delete this; 1.219 +} 1.220 + 1.221 + 1.222 +NS_IMETHODIMP 1.223 +nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, 1.224 + nsIInputStream *inStr, 1.225 + uint64_t sourceOffset, 1.226 + uint32_t count) 1.227 +{ 1.228 + if (!mStreamConverter) 1.229 + return NS_ERROR_FAILURE; 1.230 + 1.231 + nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); 1.232 + if (!finalStreamListener) 1.233 + return NS_ERROR_FAILURE; 1.234 + 1.235 + return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count); 1.236 +} 1.237 + 1.238 +NS_IMETHODIMP 1.239 +nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result) 1.240 +{ 1.241 + // Forward interface requests to our parent 1.242 + nsCOMPtr<nsIInterfaceRequestor> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer); 1.243 + if (!finalStreamListener) 1.244 + return NS_ERROR_FAILURE; 1.245 + 1.246 + return finalStreamListener->GetInterface(aIID, result); 1.247 +} 1.248 + 1.249 +// nsPluginStreamListenerPeer 1.250 + 1.251 +NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer, 1.252 + nsIStreamListener, 1.253 + nsIRequestObserver, 1.254 + nsIHttpHeaderVisitor, 1.255 + nsISupportsWeakReference, 1.256 + nsIInterfaceRequestor, 1.257 + nsIChannelEventSink) 1.258 + 1.259 +nsPluginStreamListenerPeer::nsPluginStreamListenerPeer() 1.260 +{ 1.261 + mStreamType = NP_NORMAL; 1.262 + mStartBinding = false; 1.263 + mAbort = false; 1.264 + mRequestFailed = false; 1.265 + 1.266 + mPendingRequests = 0; 1.267 + mHaveFiredOnStartRequest = false; 1.268 + mDataForwardToRequest = nullptr; 1.269 + 1.270 + mSeekable = false; 1.271 + mModified = 0; 1.272 + mStreamOffset = 0; 1.273 + mStreamComplete = 0; 1.274 +} 1.275 + 1.276 +nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer() 1.277 +{ 1.278 +#ifdef PLUGIN_LOGGING 1.279 + PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, 1.280 + ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n",this, mURLSpec.get())); 1.281 +#endif 1.282 + 1.283 + if (mPStreamListener) { 1.284 + mPStreamListener->SetStreamListenerPeer(nullptr); 1.285 + } 1.286 + 1.287 + // close FD of mFileCacheOutputStream if it's still open 1.288 + // or we won't be able to remove the cache file 1.289 + if (mFileCacheOutputStream) 1.290 + mFileCacheOutputStream = nullptr; 1.291 + 1.292 + delete mDataForwardToRequest; 1.293 + 1.294 + if (mPluginInstance) 1.295 + mPluginInstance->FileCachedStreamListeners()->RemoveElement(this); 1.296 +} 1.297 + 1.298 +// Called as a result of GetURL and PostURL, or by the host in the case of the 1.299 +// initial plugin stream. 1.300 +nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL, 1.301 + nsNPAPIPluginInstance *aInstance, 1.302 + nsNPAPIPluginStreamListener* aListener) 1.303 +{ 1.304 +#ifdef PLUGIN_LOGGING 1.305 + nsAutoCString urlSpec; 1.306 + if (aURL != nullptr) aURL->GetSpec(urlSpec); 1.307 + 1.308 + PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL, 1.309 + ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get())); 1.310 + 1.311 + PR_LogFlush(); 1.312 +#endif 1.313 + 1.314 + // Not gonna work out 1.315 + if (!aInstance) { 1.316 + return NS_ERROR_FAILURE; 1.317 + } 1.318 + 1.319 + mURL = aURL; 1.320 + 1.321 + NS_ASSERTION(mPluginInstance == nullptr, 1.322 + "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr"); 1.323 + mPluginInstance = aInstance; 1.324 + 1.325 + // If the plugin did not request this stream, e.g. the initial stream, we wont 1.326 + // have a nsNPAPIPluginStreamListener yet - this will be handled by 1.327 + // SetUpStreamListener 1.328 + if (aListener) { 1.329 + mPStreamListener = aListener; 1.330 + mPStreamListener->SetStreamListenerPeer(this); 1.331 + } 1.332 + 1.333 + mPendingRequests = 1; 1.334 + 1.335 + mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>(); 1.336 + 1.337 + return NS_OK; 1.338 +} 1.339 + 1.340 +// SetupPluginCacheFile is called if we have to save the stream to disk. 1.341 +// 1.342 +// These files will be deleted when the host is destroyed. 1.343 +// 1.344 +// TODO? What if we fill up the the dest dir? 1.345 +nsresult 1.346 +nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel) 1.347 +{ 1.348 + nsresult rv = NS_OK; 1.349 + 1.350 + bool useExistingCacheFile = false; 1.351 + nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); 1.352 + 1.353 + // Look for an existing cache file for the URI. 1.354 + nsTArray< nsRefPtr<nsNPAPIPluginInstance> > *instances = pluginHost->InstanceArray(); 1.355 + for (uint32_t i = 0; i < instances->Length(); i++) { 1.356 + // most recent streams are at the end of list 1.357 + nsTArray<nsPluginStreamListenerPeer*> *streamListeners = instances->ElementAt(i)->FileCachedStreamListeners(); 1.358 + for (int32_t i = streamListeners->Length() - 1; i >= 0; --i) { 1.359 + nsPluginStreamListenerPeer *lp = streamListeners->ElementAt(i); 1.360 + if (lp && lp->mLocalCachedFileHolder) { 1.361 + useExistingCacheFile = lp->UseExistingPluginCacheFile(this); 1.362 + if (useExistingCacheFile) { 1.363 + mLocalCachedFileHolder = lp->mLocalCachedFileHolder; 1.364 + break; 1.365 + } 1.366 + } 1.367 + if (useExistingCacheFile) 1.368 + break; 1.369 + } 1.370 + } 1.371 + 1.372 + // Create a new cache file if one could not be found. 1.373 + if (!useExistingCacheFile) { 1.374 + nsCOMPtr<nsIFile> pluginTmp; 1.375 + rv = nsPluginHost::GetPluginTempDir(getter_AddRefs(pluginTmp)); 1.376 + if (NS_FAILED(rv)) { 1.377 + return rv; 1.378 + } 1.379 + 1.380 + // Get the filename from the channel 1.381 + nsCOMPtr<nsIURI> uri; 1.382 + rv = channel->GetURI(getter_AddRefs(uri)); 1.383 + if (NS_FAILED(rv)) return rv; 1.384 + 1.385 + nsCOMPtr<nsIURL> url(do_QueryInterface(uri)); 1.386 + if (!url) 1.387 + return NS_ERROR_FAILURE; 1.388 + 1.389 + nsAutoCString filename; 1.390 + url->GetFileName(filename); 1.391 + if (NS_FAILED(rv)) 1.392 + return rv; 1.393 + 1.394 + // Create a file to save our stream into. Should we scramble the name? 1.395 + filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0); 1.396 + rv = pluginTmp->AppendNative(filename); 1.397 + if (NS_FAILED(rv)) 1.398 + return rv; 1.399 + 1.400 + // Yes, make it unique. 1.401 + rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); 1.402 + if (NS_FAILED(rv)) 1.403 + return rv; 1.404 + 1.405 + // create a file output stream to write to... 1.406 + nsCOMPtr<nsIOutputStream> outstream; 1.407 + rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600); 1.408 + if (NS_FAILED(rv)) 1.409 + return rv; 1.410 + 1.411 + // save the file. 1.412 + mLocalCachedFileHolder = new CachedFileHolder(pluginTmp); 1.413 + } 1.414 + 1.415 + // add this listenerPeer to list of stream peers for this instance 1.416 + mPluginInstance->FileCachedStreamListeners()->AppendElement(this); 1.417 + 1.418 + return rv; 1.419 +} 1.420 + 1.421 +NS_IMETHODIMP 1.422 +nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request, 1.423 + nsISupports* aContext) 1.424 +{ 1.425 + nsresult rv = NS_OK; 1.426 + PROFILER_LABEL("nsPluginStreamListenerPeer", "OnStartRequest"); 1.427 + 1.428 + if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) { 1.429 + NS_ASSERTION(mRequests.Count() == 0, 1.430 + "Only our initial stream should be unknown!"); 1.431 + TrackRequest(request); 1.432 + } 1.433 + 1.434 + if (mHaveFiredOnStartRequest) { 1.435 + return NS_OK; 1.436 + } 1.437 + 1.438 + mHaveFiredOnStartRequest = true; 1.439 + 1.440 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.441 + NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE); 1.442 + 1.443 + // deal with 404 (Not Found) HTTP response, 1.444 + // just return, this causes the request to be ignored. 1.445 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); 1.446 + if (httpChannel) { 1.447 + uint32_t responseCode = 0; 1.448 + rv = httpChannel->GetResponseStatus(&responseCode); 1.449 + if (NS_FAILED(rv)) { 1.450 + // NPP_Notify() will be called from OnStopRequest 1.451 + // in nsNPAPIPluginStreamListener::CleanUpStream 1.452 + // return error will cancel this request 1.453 + // ...and we also need to tell the plugin that 1.454 + mRequestFailed = true; 1.455 + return NS_ERROR_FAILURE; 1.456 + } 1.457 + 1.458 + if (responseCode > 206) { // not normal 1.459 + uint32_t wantsAllNetworkStreams = 0; 1.460 + 1.461 + // We don't always have an instance here already, but if we do, check 1.462 + // to see if it wants all streams. 1.463 + if (mPluginInstance) { 1.464 + rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams, 1.465 + &wantsAllNetworkStreams); 1.466 + // If the call returned an error code make sure we still use our default value. 1.467 + if (NS_FAILED(rv)) { 1.468 + wantsAllNetworkStreams = 0; 1.469 + } 1.470 + } 1.471 + 1.472 + if (!wantsAllNetworkStreams) { 1.473 + mRequestFailed = true; 1.474 + return NS_ERROR_FAILURE; 1.475 + } 1.476 + } 1.477 + } 1.478 + 1.479 + // Get the notification callbacks from the channel and save it as 1.480 + // week ref we'll use it in nsPluginStreamInfo::RequestRead() when 1.481 + // we'll create channel for byte range request. 1.482 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.483 + channel->GetNotificationCallbacks(getter_AddRefs(callbacks)); 1.484 + if (callbacks) 1.485 + mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks); 1.486 + 1.487 + nsCOMPtr<nsILoadGroup> loadGroup; 1.488 + channel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.489 + if (loadGroup) 1.490 + mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup); 1.491 + 1.492 + int64_t length; 1.493 + rv = channel->GetContentLength(&length); 1.494 + 1.495 + // it's possible for the server to not send a Content-Length. 1.496 + // we should still work in this case. 1.497 + if (NS_FAILED(rv) || length < 0 || length > UINT32_MAX) { 1.498 + // check out if this is file channel 1.499 + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel); 1.500 + if (fileChannel) { 1.501 + // file does not exist 1.502 + mRequestFailed = true; 1.503 + return NS_ERROR_FAILURE; 1.504 + } 1.505 + mLength = 0; 1.506 + } 1.507 + else { 1.508 + mLength = uint32_t(length); 1.509 + } 1.510 + 1.511 + nsAutoCString aContentType; // XXX but we already got the type above! 1.512 + rv = channel->GetContentType(aContentType); 1.513 + if (NS_FAILED(rv)) 1.514 + return rv; 1.515 + 1.516 + nsCOMPtr<nsIURI> aURL; 1.517 + rv = channel->GetURI(getter_AddRefs(aURL)); 1.518 + if (NS_FAILED(rv)) 1.519 + return rv; 1.520 + 1.521 + aURL->GetSpec(mURLSpec); 1.522 + 1.523 + if (!aContentType.IsEmpty()) 1.524 + mContentType = aContentType; 1.525 + 1.526 +#ifdef PLUGIN_LOGGING 1.527 + PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY, 1.528 + ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n", 1.529 + this, request, aContentType.get(), mURLSpec.get())); 1.530 + 1.531 + PR_LogFlush(); 1.532 +#endif 1.533 + 1.534 + // Set up the stream listener... 1.535 + rv = SetUpStreamListener(request, aURL); 1.536 + if (NS_FAILED(rv)) return rv; 1.537 + 1.538 + return rv; 1.539 +} 1.540 + 1.541 +NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request, 1.542 + nsISupports* aContext, 1.543 + uint64_t aProgress, 1.544 + uint64_t aProgressMax) 1.545 +{ 1.546 + nsresult rv = NS_OK; 1.547 + return rv; 1.548 +} 1.549 + 1.550 +NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request, 1.551 + nsISupports* aContext, 1.552 + nsresult aStatus, 1.553 + const char16_t* aStatusArg) 1.554 +{ 1.555 + return NS_OK; 1.556 +} 1.557 + 1.558 +nsresult 1.559 +nsPluginStreamListenerPeer::GetContentType(char** result) 1.560 +{ 1.561 + *result = const_cast<char*>(mContentType.get()); 1.562 + return NS_OK; 1.563 +} 1.564 + 1.565 + 1.566 +nsresult 1.567 +nsPluginStreamListenerPeer::IsSeekable(bool* result) 1.568 +{ 1.569 + *result = mSeekable; 1.570 + return NS_OK; 1.571 +} 1.572 + 1.573 +nsresult 1.574 +nsPluginStreamListenerPeer::GetLength(uint32_t* result) 1.575 +{ 1.576 + *result = mLength; 1.577 + return NS_OK; 1.578 +} 1.579 + 1.580 +nsresult 1.581 +nsPluginStreamListenerPeer::GetLastModified(uint32_t* result) 1.582 +{ 1.583 + *result = mModified; 1.584 + return NS_OK; 1.585 +} 1.586 + 1.587 +nsresult 1.588 +nsPluginStreamListenerPeer::GetURL(const char** result) 1.589 +{ 1.590 + *result = mURLSpec.get(); 1.591 + return NS_OK; 1.592 +} 1.593 + 1.594 +void 1.595 +nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest, 1.596 + int32_t *numRequests) 1.597 +{ 1.598 + rangeRequest.Truncate(); 1.599 + *numRequests = 0; 1.600 + //the string should look like this: bytes=500-700,601-999 1.601 + if (!aRangeList) 1.602 + return; 1.603 + 1.604 + int32_t requestCnt = 0; 1.605 + nsAutoCString string("bytes="); 1.606 + 1.607 + for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) { 1.608 + // XXX zero length? 1.609 + if (!range->length) 1.610 + continue; 1.611 + 1.612 + // XXX needs to be fixed for negative offsets 1.613 + string.AppendInt(range->offset); 1.614 + string.Append("-"); 1.615 + string.AppendInt(range->offset + range->length - 1); 1.616 + if (range->next) 1.617 + string += ","; 1.618 + 1.619 + requestCnt++; 1.620 + } 1.621 + 1.622 + // get rid of possible trailing comma 1.623 + string.Trim(",", false); 1.624 + 1.625 + rangeRequest = string; 1.626 + *numRequests = requestCnt; 1.627 + return; 1.628 +} 1.629 + 1.630 +nsresult 1.631 +nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList) 1.632 +{ 1.633 + nsAutoCString rangeString; 1.634 + int32_t numRequests; 1.635 + 1.636 + MakeByteRangeString(rangeList, rangeString, &numRequests); 1.637 + 1.638 + if (numRequests == 0) 1.639 + return NS_ERROR_FAILURE; 1.640 + 1.641 + nsresult rv = NS_OK; 1.642 + 1.643 + nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks); 1.644 + nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup); 1.645 + nsCOMPtr<nsIChannel> channel; 1.646 + rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks); 1.647 + if (NS_FAILED(rv)) 1.648 + return rv; 1.649 + 1.650 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); 1.651 + if (!httpChannel) 1.652 + return NS_ERROR_FAILURE; 1.653 + 1.654 + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false); 1.655 + 1.656 + mAbort = true; // instruct old stream listener to cancel 1.657 + // the request on the next ODA. 1.658 + 1.659 + nsCOMPtr<nsIStreamListener> converter; 1.660 + 1.661 + if (numRequests == 1) { 1.662 + converter = this; 1.663 + // set current stream offset equal to the first offset in the range list 1.664 + // it will work for single byte range request 1.665 + // for multy range we'll reset it in ODA 1.666 + SetStreamOffset(rangeList->offset); 1.667 + } else { 1.668 + nsWeakPtr weakpeer = 1.669 + do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this)); 1.670 + nsPluginByteRangeStreamListener *brrListener = 1.671 + new nsPluginByteRangeStreamListener(weakpeer); 1.672 + if (brrListener) 1.673 + converter = brrListener; 1.674 + else 1.675 + return NS_ERROR_OUT_OF_MEMORY; 1.676 + } 1.677 + 1.678 + mPendingRequests += numRequests; 1.679 + 1.680 + nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv); 1.681 + if (NS_FAILED(rv)) 1.682 + return rv; 1.683 + rv = container->SetData(MAGIC_REQUEST_CONTEXT); 1.684 + if (NS_FAILED(rv)) 1.685 + return rv; 1.686 + 1.687 + rv = channel->AsyncOpen(converter, container); 1.688 + if (NS_SUCCEEDED(rv)) 1.689 + TrackRequest(channel); 1.690 + return rv; 1.691 +} 1.692 + 1.693 +nsresult 1.694 +nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result) 1.695 +{ 1.696 + *result = mStreamOffset; 1.697 + return NS_OK; 1.698 +} 1.699 + 1.700 +nsresult 1.701 +nsPluginStreamListenerPeer::SetStreamOffset(int32_t value) 1.702 +{ 1.703 + mStreamOffset = value; 1.704 + return NS_OK; 1.705 +} 1.706 + 1.707 +nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request, 1.708 + nsISupports* aContext) 1.709 +{ 1.710 + if (!mPluginInstance) 1.711 + return NS_ERROR_FAILURE; 1.712 + 1.713 + // mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up 1.714 + mPluginInstance->Stop(); 1.715 + mPluginInstance->Start(); 1.716 + nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner(); 1.717 + if (owner) { 1.718 + NPWindow* window = nullptr; 1.719 + owner->GetWindow(window); 1.720 +#if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT) 1.721 + // Should call GetPluginPort() here. 1.722 + // This part is copied from nsPluginInstanceOwner::GetPluginPort(). 1.723 + nsCOMPtr<nsIWidget> widget; 1.724 + ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget)); 1.725 + if (widget) { 1.726 + window->window = widget->GetNativeData(NS_NATIVE_PLUGIN_PORT); 1.727 + } 1.728 +#endif 1.729 + owner->CallSetWindow(); 1.730 + } 1.731 + 1.732 + mSeekable = false; 1.733 + mPStreamListener->OnStartBinding(this); 1.734 + mStreamOffset = 0; 1.735 + 1.736 + // force the plugin to use stream as file 1.737 + mStreamType = NP_ASFILE; 1.738 + 1.739 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.740 + if (channel) { 1.741 + SetupPluginCacheFile(channel); 1.742 + } 1.743 + 1.744 + // unset mPendingRequests 1.745 + mPendingRequests = 0; 1.746 + 1.747 + return NS_OK; 1.748 +} 1.749 + 1.750 +bool 1.751 +nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi) 1.752 +{ 1.753 + NS_ENSURE_TRUE(psi, false); 1.754 + 1.755 + if (psi->mLength == mLength && 1.756 + psi->mModified == mModified && 1.757 + mStreamComplete && 1.758 + mURLSpec.Equals(psi->mURLSpec)) 1.759 + { 1.760 + return true; 1.761 + } 1.762 + return false; 1.763 +} 1.764 + 1.765 +NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, 1.766 + nsISupports* aContext, 1.767 + nsIInputStream *aIStream, 1.768 + uint64_t sourceOffset, 1.769 + uint32_t aLength) 1.770 +{ 1.771 + if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) { 1.772 + MOZ_ASSERT(false, "Received OnDataAvailable for untracked request."); 1.773 + return NS_ERROR_UNEXPECTED; 1.774 + } 1.775 + 1.776 + if (mRequestFailed) 1.777 + return NS_ERROR_FAILURE; 1.778 + 1.779 + if (mAbort) { 1.780 + uint32_t magicNumber = 0; // set it to something that is not the magic number. 1.781 + nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); 1.782 + if (container) 1.783 + container->GetData(&magicNumber); 1.784 + 1.785 + if (magicNumber != MAGIC_REQUEST_CONTEXT) { 1.786 + // this is not one of our range requests 1.787 + mAbort = false; 1.788 + return NS_BINDING_ABORTED; 1.789 + } 1.790 + } 1.791 + 1.792 + nsresult rv = NS_OK; 1.793 + 1.794 + if (!mPStreamListener) 1.795 + return NS_ERROR_FAILURE; 1.796 + 1.797 + const char * url = nullptr; 1.798 + GetURL(&url); 1.799 + 1.800 + PLUGIN_LOG(PLUGIN_LOG_NOISY, 1.801 + ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n", 1.802 + this, request, sourceOffset, aLength, url ? url : "no url set")); 1.803 + 1.804 + // if the plugin has requested an AsFileOnly stream, then don't 1.805 + // call OnDataAvailable 1.806 + if (mStreamType != NP_ASFILEONLY) { 1.807 + // get the absolute offset of the request, if one exists. 1.808 + nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); 1.809 + if (brr) { 1.810 + if (!mDataForwardToRequest) 1.811 + return NS_ERROR_FAILURE; 1.812 + 1.813 + int64_t absoluteOffset64 = 0; 1.814 + brr->GetStartRange(&absoluteOffset64); 1.815 + 1.816 + // XXX handle 64-bit for real 1.817 + int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64); 1.818 + 1.819 + // we need to track how much data we have forwarded to the 1.820 + // plugin. 1.821 + 1.822 + // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130 1.823 + // 1.824 + // Why couldn't this be tracked on the plugin info, and not in a 1.825 + // *hash table*? 1.826 + int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset); 1.827 + mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength)); 1.828 + 1.829 + SetStreamOffset(absoluteOffset + amtForwardToPlugin); 1.830 + } 1.831 + 1.832 + nsCOMPtr<nsIInputStream> stream = aIStream; 1.833 + 1.834 + // if we are caching the file ourselves to disk, we want to 'tee' off 1.835 + // the data as the plugin read from the stream. We do this by the magic 1.836 + // of an input stream tee. 1.837 + 1.838 + if (mFileCacheOutputStream) { 1.839 + rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream); 1.840 + if (NS_FAILED(rv)) 1.841 + return rv; 1.842 + } 1.843 + 1.844 + rv = mPStreamListener->OnDataAvailable(this, 1.845 + stream, 1.846 + aLength); 1.847 + 1.848 + // if a plugin returns an error, the peer must kill the stream 1.849 + // else the stream and PluginStreamListener leak 1.850 + if (NS_FAILED(rv)) 1.851 + request->Cancel(rv); 1.852 + } 1.853 + else 1.854 + { 1.855 + // if we don't read from the stream, OnStopRequest will never be called 1.856 + char* buffer = new char[aLength]; 1.857 + uint32_t amountRead, amountWrote = 0; 1.858 + rv = aIStream->Read(buffer, aLength, &amountRead); 1.859 + 1.860 + // if we are caching this to disk ourselves, lets write the bytes out. 1.861 + if (mFileCacheOutputStream) { 1.862 + while (amountWrote < amountRead && NS_SUCCEEDED(rv)) { 1.863 + rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote); 1.864 + } 1.865 + } 1.866 + delete [] buffer; 1.867 + } 1.868 + return rv; 1.869 +} 1.870 + 1.871 +NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request, 1.872 + nsISupports* aContext, 1.873 + nsresult aStatus) 1.874 +{ 1.875 + nsresult rv = NS_OK; 1.876 + 1.877 + nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request); 1.878 + if (!mp) { 1.879 + bool found = mRequests.RemoveObject(request); 1.880 + if (!found) { 1.881 + NS_ERROR("Received OnStopRequest for untracked request."); 1.882 + } 1.883 + } 1.884 + 1.885 + PLUGIN_LOG(PLUGIN_LOG_NOISY, 1.886 + ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n", 1.887 + this, aStatus, request)); 1.888 + 1.889 + // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return. 1.890 + nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); 1.891 + if (brr) { 1.892 + int64_t absoluteOffset64 = 0; 1.893 + brr->GetStartRange(&absoluteOffset64); 1.894 + // XXX support 64-bit offsets 1.895 + int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64); 1.896 + 1.897 + // remove the request from our data forwarding count hash. 1.898 + mDataForwardToRequest->Remove(absoluteOffset); 1.899 + 1.900 + 1.901 + PLUGIN_LOG(PLUGIN_LOG_NOISY, 1.902 + (" ::OnStopRequest for ByteRangeRequest Started=%d\n", 1.903 + absoluteOffset)); 1.904 + } else { 1.905 + // if this is not byte range request and 1.906 + // if we are writting the stream to disk ourselves, 1.907 + // close & tear it down here 1.908 + mFileCacheOutputStream = nullptr; 1.909 + } 1.910 + 1.911 + // if we still have pending stuff to do, lets not close the plugin socket. 1.912 + if (--mPendingRequests > 0) 1.913 + return NS_OK; 1.914 + 1.915 + // we keep our connections around... 1.916 + nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); 1.917 + if (container) { 1.918 + uint32_t magicNumber = 0; // set it to something that is not the magic number. 1.919 + container->GetData(&magicNumber); 1.920 + if (magicNumber == MAGIC_REQUEST_CONTEXT) { 1.921 + // this is one of our range requests 1.922 + return NS_OK; 1.923 + } 1.924 + } 1.925 + 1.926 + if (!mPStreamListener) 1.927 + return NS_ERROR_FAILURE; 1.928 + 1.929 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.930 + if (!channel) 1.931 + return NS_ERROR_FAILURE; 1.932 + // Set the content type to ensure we don't pass null to the plugin 1.933 + nsAutoCString aContentType; 1.934 + rv = channel->GetContentType(aContentType); 1.935 + if (NS_FAILED(rv) && !mRequestFailed) 1.936 + return rv; 1.937 + 1.938 + if (!aContentType.IsEmpty()) 1.939 + mContentType = aContentType; 1.940 + 1.941 + // set error status if stream failed so we notify the plugin 1.942 + if (mRequestFailed) 1.943 + aStatus = NS_ERROR_FAILURE; 1.944 + 1.945 + if (NS_FAILED(aStatus)) { 1.946 + // on error status cleanup the stream 1.947 + // and return w/o OnFileAvailable() 1.948 + mPStreamListener->OnStopBinding(this, aStatus); 1.949 + return NS_OK; 1.950 + } 1.951 + 1.952 + // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly 1.953 + if (mStreamType >= NP_ASFILE) { 1.954 + nsCOMPtr<nsIFile> localFile; 1.955 + if (mLocalCachedFileHolder) 1.956 + localFile = mLocalCachedFileHolder->file(); 1.957 + else { 1.958 + // see if it is a file channel. 1.959 + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request); 1.960 + if (fileChannel) { 1.961 + fileChannel->GetFile(getter_AddRefs(localFile)); 1.962 + } 1.963 + } 1.964 + 1.965 + if (localFile) { 1.966 + OnFileAvailable(localFile); 1.967 + } 1.968 + } 1.969 + 1.970 + if (mStartBinding) { 1.971 + // On start binding has been called 1.972 + mPStreamListener->OnStopBinding(this, aStatus); 1.973 + } else { 1.974 + // OnStartBinding hasn't been called, so complete the action. 1.975 + mPStreamListener->OnStartBinding(this); 1.976 + mPStreamListener->OnStopBinding(this, aStatus); 1.977 + } 1.978 + 1.979 + if (NS_SUCCEEDED(aStatus)) { 1.980 + mStreamComplete = true; 1.981 + } 1.982 + 1.983 + return NS_OK; 1.984 +} 1.985 + 1.986 +nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request, 1.987 + nsIURI* aURL) 1.988 +{ 1.989 + nsresult rv = NS_OK; 1.990 + 1.991 + // If we don't yet have a stream listener, we need to get 1.992 + // one from the plugin. 1.993 + // NOTE: this should only happen when a stream was NOT created 1.994 + // with GetURL or PostURL (i.e. it's the initial stream we 1.995 + // send to the plugin as determined by the SRC or DATA attribute) 1.996 + if (!mPStreamListener) { 1.997 + if (!mPluginInstance) { 1.998 + return NS_ERROR_FAILURE; 1.999 + } 1.1000 + 1.1001 + nsRefPtr<nsNPAPIPluginStreamListener> streamListener; 1.1002 + rv = mPluginInstance->NewStreamListener(nullptr, nullptr, 1.1003 + getter_AddRefs(streamListener)); 1.1004 + if (NS_FAILED(rv) || !streamListener) { 1.1005 + return NS_ERROR_FAILURE; 1.1006 + } 1.1007 + 1.1008 + mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get()); 1.1009 + } 1.1010 + 1.1011 + mPStreamListener->SetStreamListenerPeer(this); 1.1012 + 1.1013 + bool useLocalCache = false; 1.1014 + 1.1015 + // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup 1.1016 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.1017 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); 1.1018 + 1.1019 + /* 1.1020 + * Assumption 1.1021 + * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets 1.1022 + * called, all the headers have been read. 1.1023 + */ 1.1024 + if (httpChannel) { 1.1025 + // Reassemble the HTTP response status line and provide it to our 1.1026 + // listener. Would be nice if we could get the raw status line, 1.1027 + // but nsIHttpChannel doesn't currently provide that. 1.1028 + // Status code: required; the status line isn't useful without it. 1.1029 + uint32_t statusNum; 1.1030 + if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) && 1.1031 + statusNum < 1000) { 1.1032 + // HTTP version: provide if available. Defaults to empty string. 1.1033 + nsCString ver; 1.1034 + nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = 1.1035 + do_QueryInterface(channel); 1.1036 + if (httpChannelInternal) { 1.1037 + uint32_t major, minor; 1.1038 + if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major, 1.1039 + &minor))) { 1.1040 + ver = nsPrintfCString("/%lu.%lu", major, minor); 1.1041 + } 1.1042 + } 1.1043 + 1.1044 + // Status text: provide if available. Defaults to "OK". 1.1045 + nsCString statusText; 1.1046 + if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) { 1.1047 + statusText = "OK"; 1.1048 + } 1.1049 + 1.1050 + // Assemble everything and pass to listener. 1.1051 + nsPrintfCString status("HTTP%s %lu %s", ver.get(), statusNum, 1.1052 + statusText.get()); 1.1053 + static_cast<nsIHTTPHeaderListener*>(mPStreamListener)->StatusLine(status.get()); 1.1054 + } 1.1055 + 1.1056 + // Also provide all HTTP response headers to our listener. 1.1057 + httpChannel->VisitResponseHeaders(this); 1.1058 + 1.1059 + mSeekable = false; 1.1060 + // first we look for a content-encoding header. If we find one, we tell the 1.1061 + // plugin that stream is not seekable, because the plugin always sees 1.1062 + // uncompressed data, so it can't make meaningful range requests on a 1.1063 + // compressed entity. Also, we force the plugin to use 1.1064 + // nsPluginStreamType_AsFile stream type and we have to save decompressed 1.1065 + // file into local plugin cache, because necko cache contains original 1.1066 + // compressed file. 1.1067 + nsAutoCString contentEncoding; 1.1068 + if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), 1.1069 + contentEncoding))) { 1.1070 + useLocalCache = true; 1.1071 + } else { 1.1072 + // set seekability (seekable if the stream has a known length and if the 1.1073 + // http server accepts byte ranges). 1.1074 + uint32_t length; 1.1075 + GetLength(&length); 1.1076 + if (length) { 1.1077 + nsAutoCString range; 1.1078 + if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) && 1.1079 + range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) { 1.1080 + mSeekable = true; 1.1081 + } 1.1082 + } 1.1083 + } 1.1084 + 1.1085 + // we require a content len 1.1086 + // get Last-Modified header for plugin info 1.1087 + nsAutoCString lastModified; 1.1088 + if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) && 1.1089 + !lastModified.IsEmpty()) { 1.1090 + PRTime time64; 1.1091 + PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time 1.1092 + 1.1093 + // Convert PRTime to unix-style time_t, i.e. seconds since the epoch 1.1094 + double fpTime = double(time64); 1.1095 + mModified = (uint32_t)(fpTime * 1e-6 + 0.5); 1.1096 + } 1.1097 + } 1.1098 + 1.1099 + rv = mPStreamListener->OnStartBinding(this); 1.1100 + 1.1101 + mStartBinding = true; 1.1102 + 1.1103 + if (NS_FAILED(rv)) 1.1104 + return rv; 1.1105 + 1.1106 + mPStreamListener->GetStreamType(&mStreamType); 1.1107 + 1.1108 + if (!useLocalCache && mStreamType >= NP_ASFILE) { 1.1109 + // check it out if this is not a file channel. 1.1110 + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request); 1.1111 + if (!fileChannel) { 1.1112 + useLocalCache = true; 1.1113 + } 1.1114 + } 1.1115 + 1.1116 + if (useLocalCache) { 1.1117 + SetupPluginCacheFile(channel); 1.1118 + } 1.1119 + 1.1120 + return NS_OK; 1.1121 +} 1.1122 + 1.1123 +nsresult 1.1124 +nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile) 1.1125 +{ 1.1126 + nsresult rv; 1.1127 + if (!mPStreamListener) 1.1128 + return NS_ERROR_FAILURE; 1.1129 + 1.1130 + nsAutoCString path; 1.1131 + rv = aFile->GetNativePath(path); 1.1132 + if (NS_FAILED(rv)) return rv; 1.1133 + 1.1134 + if (path.IsEmpty()) { 1.1135 + NS_WARNING("empty path"); 1.1136 + return NS_OK; 1.1137 + } 1.1138 + 1.1139 + rv = mPStreamListener->OnFileAvailable(this, path.get()); 1.1140 + return rv; 1.1141 +} 1.1142 + 1.1143 +NS_IMETHODIMP 1.1144 +nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value) 1.1145 +{ 1.1146 + return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(), 1.1147 + PromiseFlatCString(value).get()); 1.1148 +} 1.1149 + 1.1150 +nsresult 1.1151 +nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result) 1.1152 +{ 1.1153 + if (!mPluginInstance) { 1.1154 + return NS_ERROR_FAILURE; 1.1155 + } 1.1156 + 1.1157 + nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner(); 1.1158 + if (owner) { 1.1159 + nsCOMPtr<nsIDocument> doc; 1.1160 + nsresult rv = owner->GetDocument(getter_AddRefs(doc)); 1.1161 + if (NS_SUCCEEDED(rv) && doc) { 1.1162 + nsPIDOMWindow *window = doc->GetWindow(); 1.1163 + if (window) { 1.1164 + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); 1.1165 + nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav); 1.1166 + return ir->GetInterface(aIID, result); 1.1167 + } 1.1168 + } 1.1169 + } 1.1170 + 1.1171 + return NS_ERROR_FAILURE; 1.1172 +} 1.1173 + 1.1174 +NS_IMETHODIMP 1.1175 +nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result) 1.1176 +{ 1.1177 + // Provide nsIChannelEventSink ourselves, otherwise let our document's 1.1178 + // script global object owner provide the interface. 1.1179 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.1180 + return QueryInterface(aIID, result); 1.1181 + } 1.1182 + 1.1183 + return GetInterfaceGlobal(aIID, result); 1.1184 +} 1.1185 + 1.1186 +/** 1.1187 + * Proxy class which forwards async redirect notifications back to the necko 1.1188 + * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with 1.1189 + * which channel is active. 1.1190 + */ 1.1191 +class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback 1.1192 +{ 1.1193 +public: 1.1194 + ChannelRedirectProxyCallback(nsPluginStreamListenerPeer* listener, 1.1195 + nsIAsyncVerifyRedirectCallback* parent, 1.1196 + nsIChannel* oldChannel, 1.1197 + nsIChannel* newChannel) 1.1198 + : mWeakListener(do_GetWeakReference(static_cast<nsIStreamListener*>(listener))) 1.1199 + , mParent(parent) 1.1200 + , mOldChannel(oldChannel) 1.1201 + , mNewChannel(newChannel) 1.1202 + { 1.1203 + } 1.1204 + 1.1205 + ChannelRedirectProxyCallback() {} 1.1206 + virtual ~ChannelRedirectProxyCallback() {} 1.1207 + 1.1208 + NS_DECL_ISUPPORTS 1.1209 + 1.1210 + NS_IMETHODIMP OnRedirectVerifyCallback(nsresult result) 1.1211 + { 1.1212 + if (NS_SUCCEEDED(result)) { 1.1213 + nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener); 1.1214 + if (listener) 1.1215 + static_cast<nsPluginStreamListenerPeer*>(listener.get())->ReplaceRequest(mOldChannel, mNewChannel); 1.1216 + } 1.1217 + return mParent->OnRedirectVerifyCallback(result); 1.1218 + } 1.1219 + 1.1220 +private: 1.1221 + nsWeakPtr mWeakListener; 1.1222 + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent; 1.1223 + nsCOMPtr<nsIChannel> mOldChannel; 1.1224 + nsCOMPtr<nsIChannel> mNewChannel; 1.1225 +}; 1.1226 + 1.1227 +NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback) 1.1228 + 1.1229 + 1.1230 +NS_IMETHODIMP 1.1231 +nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, 1.1232 + uint32_t flags, nsIAsyncVerifyRedirectCallback* callback) 1.1233 +{ 1.1234 + // Disallow redirects if we don't have a stream listener. 1.1235 + if (!mPStreamListener) { 1.1236 + return NS_ERROR_FAILURE; 1.1237 + } 1.1238 + 1.1239 + nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback = 1.1240 + new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel); 1.1241 + 1.1242 + // Give NPAPI a chance to control redirects. 1.1243 + bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback); 1.1244 + if (notificationHandled) { 1.1245 + return NS_OK; 1.1246 + } 1.1247 + 1.1248 + // Don't allow cross-origin 307 POST redirects. 1.1249 + nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel)); 1.1250 + if (oldHttpChannel) { 1.1251 + uint32_t responseStatus; 1.1252 + nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus); 1.1253 + if (NS_FAILED(rv)) { 1.1254 + return rv; 1.1255 + } 1.1256 + if (responseStatus == 307) { 1.1257 + nsAutoCString method; 1.1258 + rv = oldHttpChannel->GetRequestMethod(method); 1.1259 + if (NS_FAILED(rv)) { 1.1260 + return rv; 1.1261 + } 1.1262 + if (method.EqualsLiteral("POST")) { 1.1263 + rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel); 1.1264 + if (NS_FAILED(rv)) { 1.1265 + return rv; 1.1266 + } 1.1267 + } 1.1268 + } 1.1269 + } 1.1270 + 1.1271 + // Fall back to channel event sink for window. 1.1272 + nsCOMPtr<nsIChannelEventSink> channelEventSink; 1.1273 + nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink)); 1.1274 + if (NS_FAILED(rv)) { 1.1275 + return rv; 1.1276 + } 1.1277 + 1.1278 + return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback); 1.1279 +}