dom/plugins/base/nsPluginStreamListenerPeer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsPluginStreamListenerPeer.h"
michael@0 7 #include "nsIStreamConverterService.h"
michael@0 8 #include "nsIHttpChannel.h"
michael@0 9 #include "nsIHttpChannelInternal.h"
michael@0 10 #include "nsIFileChannel.h"
michael@0 11 #include "nsMimeTypes.h"
michael@0 12 #include "nsISupportsPrimitives.h"
michael@0 13 #include "nsNetCID.h"
michael@0 14 #include "nsPluginLogging.h"
michael@0 15 #include "nsIURI.h"
michael@0 16 #include "nsIURL.h"
michael@0 17 #include "nsPluginHost.h"
michael@0 18 #include "nsIByteRangeRequest.h"
michael@0 19 #include "nsIMultiPartChannel.h"
michael@0 20 #include "nsIInputStreamTee.h"
michael@0 21 #include "nsPrintfCString.h"
michael@0 22 #include "nsIScriptGlobalObject.h"
michael@0 23 #include "nsIDocument.h"
michael@0 24 #include "nsIWebNavigation.h"
michael@0 25 #include "nsContentUtils.h"
michael@0 26 #include "nsNetUtil.h"
michael@0 27 #include "nsPluginNativeWindow.h"
michael@0 28 #include "GeckoProfiler.h"
michael@0 29 #include "nsPluginInstanceOwner.h"
michael@0 30 #include "nsDataHashtable.h"
michael@0 31
michael@0 32 #define MAGIC_REQUEST_CONTEXT 0x01020304
michael@0 33
michael@0 34 // nsPluginByteRangeStreamListener
michael@0 35
michael@0 36 class nsPluginByteRangeStreamListener
michael@0 37 : public nsIStreamListener
michael@0 38 , public nsIInterfaceRequestor
michael@0 39 {
michael@0 40 public:
michael@0 41 nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
michael@0 42 virtual ~nsPluginByteRangeStreamListener();
michael@0 43
michael@0 44 NS_DECL_ISUPPORTS
michael@0 45 NS_DECL_NSIREQUESTOBSERVER
michael@0 46 NS_DECL_NSISTREAMLISTENER
michael@0 47 NS_DECL_NSIINTERFACEREQUESTOR
michael@0 48
michael@0 49 private:
michael@0 50 nsCOMPtr<nsIStreamListener> mStreamConverter;
michael@0 51 nsWeakPtr mWeakPtrPluginStreamListenerPeer;
michael@0 52 bool mRemoveMagicNumber;
michael@0 53 };
michael@0 54
michael@0 55 NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener,
michael@0 56 nsIRequestObserver,
michael@0 57 nsIStreamListener,
michael@0 58 nsIInterfaceRequestor)
michael@0 59
michael@0 60 nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
michael@0 61 {
michael@0 62 mWeakPtrPluginStreamListenerPeer = aWeakPtr;
michael@0 63 mRemoveMagicNumber = false;
michael@0 64 }
michael@0 65
michael@0 66 nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
michael@0 67 {
michael@0 68 mStreamConverter = 0;
michael@0 69 mWeakPtrPluginStreamListenerPeer = 0;
michael@0 70 }
michael@0 71
michael@0 72 /**
michael@0 73 * Unwrap any byte-range requests so that we can check whether the base channel
michael@0 74 * is being tracked properly.
michael@0 75 */
michael@0 76 static nsCOMPtr<nsIRequest>
michael@0 77 GetBaseRequest(nsIRequest* r)
michael@0 78 {
michael@0 79 nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(r);
michael@0 80 if (!mp)
michael@0 81 return r;
michael@0 82
michael@0 83 nsCOMPtr<nsIChannel> base;
michael@0 84 mp->GetBaseChannel(getter_AddRefs(base));
michael@0 85 return already_AddRefed<nsIRequest>(base.forget());
michael@0 86 }
michael@0 87
michael@0 88 NS_IMETHODIMP
michael@0 89 nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
michael@0 90 {
michael@0 91 nsresult rv;
michael@0 92
michael@0 93 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
michael@0 94 if (!finalStreamListener)
michael@0 95 return NS_ERROR_FAILURE;
michael@0 96
michael@0 97 nsPluginStreamListenerPeer *pslp =
michael@0 98 static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
michael@0 99
michael@0 100 NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
michael@0 101 "Untracked byte-range request?");
michael@0 102
michael@0 103 nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
michael@0 104 if (NS_SUCCEEDED(rv)) {
michael@0 105 rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
michael@0 106 "*/*",
michael@0 107 finalStreamListener,
michael@0 108 nullptr,
michael@0 109 getter_AddRefs(mStreamConverter));
michael@0 110 if (NS_SUCCEEDED(rv)) {
michael@0 111 rv = mStreamConverter->OnStartRequest(request, ctxt);
michael@0 112 if (NS_SUCCEEDED(rv))
michael@0 113 return rv;
michael@0 114 }
michael@0 115 }
michael@0 116 mStreamConverter = 0;
michael@0 117
michael@0 118 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
michael@0 119 if (!httpChannel) {
michael@0 120 return NS_ERROR_FAILURE;
michael@0 121 }
michael@0 122
michael@0 123 uint32_t responseCode = 0;
michael@0 124 rv = httpChannel->GetResponseStatus(&responseCode);
michael@0 125 if (NS_FAILED(rv)) {
michael@0 126 return NS_ERROR_FAILURE;
michael@0 127 }
michael@0 128
michael@0 129 if (responseCode != 200) {
michael@0 130 uint32_t wantsAllNetworkStreams = 0;
michael@0 131 rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
michael@0 132 &wantsAllNetworkStreams);
michael@0 133 // If the call returned an error code make sure we still use our default value.
michael@0 134 if (NS_FAILED(rv)) {
michael@0 135 wantsAllNetworkStreams = 0;
michael@0 136 }
michael@0 137
michael@0 138 if (!wantsAllNetworkStreams){
michael@0 139 return NS_ERROR_FAILURE;
michael@0 140 }
michael@0 141 }
michael@0 142
michael@0 143 // if server cannot continue with byte range (206 status) and sending us whole object (200 status)
michael@0 144 // reset this seekable stream & try serve it to plugin instance as a file
michael@0 145 mStreamConverter = finalStreamListener;
michael@0 146 mRemoveMagicNumber = true;
michael@0 147
michael@0 148 rv = pslp->ServeStreamAsFile(request, ctxt);
michael@0 149 return rv;
michael@0 150 }
michael@0 151
michael@0 152 NS_IMETHODIMP
michael@0 153 nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
michael@0 154 nsresult status)
michael@0 155 {
michael@0 156 if (!mStreamConverter)
michael@0 157 return NS_ERROR_FAILURE;
michael@0 158
michael@0 159 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
michael@0 160 if (!finalStreamListener)
michael@0 161 return NS_ERROR_FAILURE;
michael@0 162
michael@0 163 nsPluginStreamListenerPeer *pslp =
michael@0 164 static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
michael@0 165 bool found = pslp->mRequests.RemoveObject(request);
michael@0 166 if (!found) {
michael@0 167 NS_ERROR("OnStopRequest received for untracked byte-range request!");
michael@0 168 }
michael@0 169
michael@0 170 if (mRemoveMagicNumber) {
michael@0 171 // remove magic number from container
michael@0 172 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt);
michael@0 173 if (container) {
michael@0 174 uint32_t magicNumber = 0;
michael@0 175 container->GetData(&magicNumber);
michael@0 176 if (magicNumber == MAGIC_REQUEST_CONTEXT) {
michael@0 177 // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest()
michael@0 178 // set it to something that is not the magic number.
michael@0 179 container->SetData(0);
michael@0 180 }
michael@0 181 } else {
michael@0 182 NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
michael@0 183 }
michael@0 184 }
michael@0 185
michael@0 186 return mStreamConverter->OnStopRequest(request, ctxt, status);
michael@0 187 }
michael@0 188
michael@0 189 // CachedFileHolder
michael@0 190
michael@0 191 CachedFileHolder::CachedFileHolder(nsIFile* cacheFile)
michael@0 192 : mFile(cacheFile)
michael@0 193 {
michael@0 194 NS_ASSERTION(mFile, "Empty CachedFileHolder");
michael@0 195 }
michael@0 196
michael@0 197 CachedFileHolder::~CachedFileHolder()
michael@0 198 {
michael@0 199 mFile->Remove(false);
michael@0 200 }
michael@0 201
michael@0 202 void
michael@0 203 CachedFileHolder::AddRef()
michael@0 204 {
michael@0 205 ++mRefCnt;
michael@0 206 NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this));
michael@0 207 }
michael@0 208
michael@0 209 void
michael@0 210 CachedFileHolder::Release()
michael@0 211 {
michael@0 212 --mRefCnt;
michael@0 213 NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder");
michael@0 214 if (0 == mRefCnt)
michael@0 215 delete this;
michael@0 216 }
michael@0 217
michael@0 218
michael@0 219 NS_IMETHODIMP
michael@0 220 nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
michael@0 221 nsIInputStream *inStr,
michael@0 222 uint64_t sourceOffset,
michael@0 223 uint32_t count)
michael@0 224 {
michael@0 225 if (!mStreamConverter)
michael@0 226 return NS_ERROR_FAILURE;
michael@0 227
michael@0 228 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
michael@0 229 if (!finalStreamListener)
michael@0 230 return NS_ERROR_FAILURE;
michael@0 231
michael@0 232 return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
michael@0 233 }
michael@0 234
michael@0 235 NS_IMETHODIMP
michael@0 236 nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result)
michael@0 237 {
michael@0 238 // Forward interface requests to our parent
michael@0 239 nsCOMPtr<nsIInterfaceRequestor> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
michael@0 240 if (!finalStreamListener)
michael@0 241 return NS_ERROR_FAILURE;
michael@0 242
michael@0 243 return finalStreamListener->GetInterface(aIID, result);
michael@0 244 }
michael@0 245
michael@0 246 // nsPluginStreamListenerPeer
michael@0 247
michael@0 248 NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer,
michael@0 249 nsIStreamListener,
michael@0 250 nsIRequestObserver,
michael@0 251 nsIHttpHeaderVisitor,
michael@0 252 nsISupportsWeakReference,
michael@0 253 nsIInterfaceRequestor,
michael@0 254 nsIChannelEventSink)
michael@0 255
michael@0 256 nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
michael@0 257 {
michael@0 258 mStreamType = NP_NORMAL;
michael@0 259 mStartBinding = false;
michael@0 260 mAbort = false;
michael@0 261 mRequestFailed = false;
michael@0 262
michael@0 263 mPendingRequests = 0;
michael@0 264 mHaveFiredOnStartRequest = false;
michael@0 265 mDataForwardToRequest = nullptr;
michael@0 266
michael@0 267 mSeekable = false;
michael@0 268 mModified = 0;
michael@0 269 mStreamOffset = 0;
michael@0 270 mStreamComplete = 0;
michael@0 271 }
michael@0 272
michael@0 273 nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
michael@0 274 {
michael@0 275 #ifdef PLUGIN_LOGGING
michael@0 276 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
michael@0 277 ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n",this, mURLSpec.get()));
michael@0 278 #endif
michael@0 279
michael@0 280 if (mPStreamListener) {
michael@0 281 mPStreamListener->SetStreamListenerPeer(nullptr);
michael@0 282 }
michael@0 283
michael@0 284 // close FD of mFileCacheOutputStream if it's still open
michael@0 285 // or we won't be able to remove the cache file
michael@0 286 if (mFileCacheOutputStream)
michael@0 287 mFileCacheOutputStream = nullptr;
michael@0 288
michael@0 289 delete mDataForwardToRequest;
michael@0 290
michael@0 291 if (mPluginInstance)
michael@0 292 mPluginInstance->FileCachedStreamListeners()->RemoveElement(this);
michael@0 293 }
michael@0 294
michael@0 295 // Called as a result of GetURL and PostURL, or by the host in the case of the
michael@0 296 // initial plugin stream.
michael@0 297 nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
michael@0 298 nsNPAPIPluginInstance *aInstance,
michael@0 299 nsNPAPIPluginStreamListener* aListener)
michael@0 300 {
michael@0 301 #ifdef PLUGIN_LOGGING
michael@0 302 nsAutoCString urlSpec;
michael@0 303 if (aURL != nullptr) aURL->GetSpec(urlSpec);
michael@0 304
michael@0 305 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
michael@0 306 ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
michael@0 307
michael@0 308 PR_LogFlush();
michael@0 309 #endif
michael@0 310
michael@0 311 // Not gonna work out
michael@0 312 if (!aInstance) {
michael@0 313 return NS_ERROR_FAILURE;
michael@0 314 }
michael@0 315
michael@0 316 mURL = aURL;
michael@0 317
michael@0 318 NS_ASSERTION(mPluginInstance == nullptr,
michael@0 319 "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr");
michael@0 320 mPluginInstance = aInstance;
michael@0 321
michael@0 322 // If the plugin did not request this stream, e.g. the initial stream, we wont
michael@0 323 // have a nsNPAPIPluginStreamListener yet - this will be handled by
michael@0 324 // SetUpStreamListener
michael@0 325 if (aListener) {
michael@0 326 mPStreamListener = aListener;
michael@0 327 mPStreamListener->SetStreamListenerPeer(this);
michael@0 328 }
michael@0 329
michael@0 330 mPendingRequests = 1;
michael@0 331
michael@0 332 mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>();
michael@0 333
michael@0 334 return NS_OK;
michael@0 335 }
michael@0 336
michael@0 337 // SetupPluginCacheFile is called if we have to save the stream to disk.
michael@0 338 //
michael@0 339 // These files will be deleted when the host is destroyed.
michael@0 340 //
michael@0 341 // TODO? What if we fill up the the dest dir?
michael@0 342 nsresult
michael@0 343 nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
michael@0 344 {
michael@0 345 nsresult rv = NS_OK;
michael@0 346
michael@0 347 bool useExistingCacheFile = false;
michael@0 348 nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
michael@0 349
michael@0 350 // Look for an existing cache file for the URI.
michael@0 351 nsTArray< nsRefPtr<nsNPAPIPluginInstance> > *instances = pluginHost->InstanceArray();
michael@0 352 for (uint32_t i = 0; i < instances->Length(); i++) {
michael@0 353 // most recent streams are at the end of list
michael@0 354 nsTArray<nsPluginStreamListenerPeer*> *streamListeners = instances->ElementAt(i)->FileCachedStreamListeners();
michael@0 355 for (int32_t i = streamListeners->Length() - 1; i >= 0; --i) {
michael@0 356 nsPluginStreamListenerPeer *lp = streamListeners->ElementAt(i);
michael@0 357 if (lp && lp->mLocalCachedFileHolder) {
michael@0 358 useExistingCacheFile = lp->UseExistingPluginCacheFile(this);
michael@0 359 if (useExistingCacheFile) {
michael@0 360 mLocalCachedFileHolder = lp->mLocalCachedFileHolder;
michael@0 361 break;
michael@0 362 }
michael@0 363 }
michael@0 364 if (useExistingCacheFile)
michael@0 365 break;
michael@0 366 }
michael@0 367 }
michael@0 368
michael@0 369 // Create a new cache file if one could not be found.
michael@0 370 if (!useExistingCacheFile) {
michael@0 371 nsCOMPtr<nsIFile> pluginTmp;
michael@0 372 rv = nsPluginHost::GetPluginTempDir(getter_AddRefs(pluginTmp));
michael@0 373 if (NS_FAILED(rv)) {
michael@0 374 return rv;
michael@0 375 }
michael@0 376
michael@0 377 // Get the filename from the channel
michael@0 378 nsCOMPtr<nsIURI> uri;
michael@0 379 rv = channel->GetURI(getter_AddRefs(uri));
michael@0 380 if (NS_FAILED(rv)) return rv;
michael@0 381
michael@0 382 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
michael@0 383 if (!url)
michael@0 384 return NS_ERROR_FAILURE;
michael@0 385
michael@0 386 nsAutoCString filename;
michael@0 387 url->GetFileName(filename);
michael@0 388 if (NS_FAILED(rv))
michael@0 389 return rv;
michael@0 390
michael@0 391 // Create a file to save our stream into. Should we scramble the name?
michael@0 392 filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
michael@0 393 rv = pluginTmp->AppendNative(filename);
michael@0 394 if (NS_FAILED(rv))
michael@0 395 return rv;
michael@0 396
michael@0 397 // Yes, make it unique.
michael@0 398 rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
michael@0 399 if (NS_FAILED(rv))
michael@0 400 return rv;
michael@0 401
michael@0 402 // create a file output stream to write to...
michael@0 403 nsCOMPtr<nsIOutputStream> outstream;
michael@0 404 rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
michael@0 405 if (NS_FAILED(rv))
michael@0 406 return rv;
michael@0 407
michael@0 408 // save the file.
michael@0 409 mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
michael@0 410 }
michael@0 411
michael@0 412 // add this listenerPeer to list of stream peers for this instance
michael@0 413 mPluginInstance->FileCachedStreamListeners()->AppendElement(this);
michael@0 414
michael@0 415 return rv;
michael@0 416 }
michael@0 417
michael@0 418 NS_IMETHODIMP
michael@0 419 nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
michael@0 420 nsISupports* aContext)
michael@0 421 {
michael@0 422 nsresult rv = NS_OK;
michael@0 423 PROFILER_LABEL("nsPluginStreamListenerPeer", "OnStartRequest");
michael@0 424
michael@0 425 if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) {
michael@0 426 NS_ASSERTION(mRequests.Count() == 0,
michael@0 427 "Only our initial stream should be unknown!");
michael@0 428 TrackRequest(request);
michael@0 429 }
michael@0 430
michael@0 431 if (mHaveFiredOnStartRequest) {
michael@0 432 return NS_OK;
michael@0 433 }
michael@0 434
michael@0 435 mHaveFiredOnStartRequest = true;
michael@0 436
michael@0 437 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 438 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
michael@0 439
michael@0 440 // deal with 404 (Not Found) HTTP response,
michael@0 441 // just return, this causes the request to be ignored.
michael@0 442 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
michael@0 443 if (httpChannel) {
michael@0 444 uint32_t responseCode = 0;
michael@0 445 rv = httpChannel->GetResponseStatus(&responseCode);
michael@0 446 if (NS_FAILED(rv)) {
michael@0 447 // NPP_Notify() will be called from OnStopRequest
michael@0 448 // in nsNPAPIPluginStreamListener::CleanUpStream
michael@0 449 // return error will cancel this request
michael@0 450 // ...and we also need to tell the plugin that
michael@0 451 mRequestFailed = true;
michael@0 452 return NS_ERROR_FAILURE;
michael@0 453 }
michael@0 454
michael@0 455 if (responseCode > 206) { // not normal
michael@0 456 uint32_t wantsAllNetworkStreams = 0;
michael@0 457
michael@0 458 // We don't always have an instance here already, but if we do, check
michael@0 459 // to see if it wants all streams.
michael@0 460 if (mPluginInstance) {
michael@0 461 rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
michael@0 462 &wantsAllNetworkStreams);
michael@0 463 // If the call returned an error code make sure we still use our default value.
michael@0 464 if (NS_FAILED(rv)) {
michael@0 465 wantsAllNetworkStreams = 0;
michael@0 466 }
michael@0 467 }
michael@0 468
michael@0 469 if (!wantsAllNetworkStreams) {
michael@0 470 mRequestFailed = true;
michael@0 471 return NS_ERROR_FAILURE;
michael@0 472 }
michael@0 473 }
michael@0 474 }
michael@0 475
michael@0 476 // Get the notification callbacks from the channel and save it as
michael@0 477 // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
michael@0 478 // we'll create channel for byte range request.
michael@0 479 nsCOMPtr<nsIInterfaceRequestor> callbacks;
michael@0 480 channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
michael@0 481 if (callbacks)
michael@0 482 mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
michael@0 483
michael@0 484 nsCOMPtr<nsILoadGroup> loadGroup;
michael@0 485 channel->GetLoadGroup(getter_AddRefs(loadGroup));
michael@0 486 if (loadGroup)
michael@0 487 mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
michael@0 488
michael@0 489 int64_t length;
michael@0 490 rv = channel->GetContentLength(&length);
michael@0 491
michael@0 492 // it's possible for the server to not send a Content-Length.
michael@0 493 // we should still work in this case.
michael@0 494 if (NS_FAILED(rv) || length < 0 || length > UINT32_MAX) {
michael@0 495 // check out if this is file channel
michael@0 496 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
michael@0 497 if (fileChannel) {
michael@0 498 // file does not exist
michael@0 499 mRequestFailed = true;
michael@0 500 return NS_ERROR_FAILURE;
michael@0 501 }
michael@0 502 mLength = 0;
michael@0 503 }
michael@0 504 else {
michael@0 505 mLength = uint32_t(length);
michael@0 506 }
michael@0 507
michael@0 508 nsAutoCString aContentType; // XXX but we already got the type above!
michael@0 509 rv = channel->GetContentType(aContentType);
michael@0 510 if (NS_FAILED(rv))
michael@0 511 return rv;
michael@0 512
michael@0 513 nsCOMPtr<nsIURI> aURL;
michael@0 514 rv = channel->GetURI(getter_AddRefs(aURL));
michael@0 515 if (NS_FAILED(rv))
michael@0 516 return rv;
michael@0 517
michael@0 518 aURL->GetSpec(mURLSpec);
michael@0 519
michael@0 520 if (!aContentType.IsEmpty())
michael@0 521 mContentType = aContentType;
michael@0 522
michael@0 523 #ifdef PLUGIN_LOGGING
michael@0 524 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
michael@0 525 ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
michael@0 526 this, request, aContentType.get(), mURLSpec.get()));
michael@0 527
michael@0 528 PR_LogFlush();
michael@0 529 #endif
michael@0 530
michael@0 531 // Set up the stream listener...
michael@0 532 rv = SetUpStreamListener(request, aURL);
michael@0 533 if (NS_FAILED(rv)) return rv;
michael@0 534
michael@0 535 return rv;
michael@0 536 }
michael@0 537
michael@0 538 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
michael@0 539 nsISupports* aContext,
michael@0 540 uint64_t aProgress,
michael@0 541 uint64_t aProgressMax)
michael@0 542 {
michael@0 543 nsresult rv = NS_OK;
michael@0 544 return rv;
michael@0 545 }
michael@0 546
michael@0 547 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
michael@0 548 nsISupports* aContext,
michael@0 549 nsresult aStatus,
michael@0 550 const char16_t* aStatusArg)
michael@0 551 {
michael@0 552 return NS_OK;
michael@0 553 }
michael@0 554
michael@0 555 nsresult
michael@0 556 nsPluginStreamListenerPeer::GetContentType(char** result)
michael@0 557 {
michael@0 558 *result = const_cast<char*>(mContentType.get());
michael@0 559 return NS_OK;
michael@0 560 }
michael@0 561
michael@0 562
michael@0 563 nsresult
michael@0 564 nsPluginStreamListenerPeer::IsSeekable(bool* result)
michael@0 565 {
michael@0 566 *result = mSeekable;
michael@0 567 return NS_OK;
michael@0 568 }
michael@0 569
michael@0 570 nsresult
michael@0 571 nsPluginStreamListenerPeer::GetLength(uint32_t* result)
michael@0 572 {
michael@0 573 *result = mLength;
michael@0 574 return NS_OK;
michael@0 575 }
michael@0 576
michael@0 577 nsresult
michael@0 578 nsPluginStreamListenerPeer::GetLastModified(uint32_t* result)
michael@0 579 {
michael@0 580 *result = mModified;
michael@0 581 return NS_OK;
michael@0 582 }
michael@0 583
michael@0 584 nsresult
michael@0 585 nsPluginStreamListenerPeer::GetURL(const char** result)
michael@0 586 {
michael@0 587 *result = mURLSpec.get();
michael@0 588 return NS_OK;
michael@0 589 }
michael@0 590
michael@0 591 void
michael@0 592 nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest,
michael@0 593 int32_t *numRequests)
michael@0 594 {
michael@0 595 rangeRequest.Truncate();
michael@0 596 *numRequests = 0;
michael@0 597 //the string should look like this: bytes=500-700,601-999
michael@0 598 if (!aRangeList)
michael@0 599 return;
michael@0 600
michael@0 601 int32_t requestCnt = 0;
michael@0 602 nsAutoCString string("bytes=");
michael@0 603
michael@0 604 for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
michael@0 605 // XXX zero length?
michael@0 606 if (!range->length)
michael@0 607 continue;
michael@0 608
michael@0 609 // XXX needs to be fixed for negative offsets
michael@0 610 string.AppendInt(range->offset);
michael@0 611 string.Append("-");
michael@0 612 string.AppendInt(range->offset + range->length - 1);
michael@0 613 if (range->next)
michael@0 614 string += ",";
michael@0 615
michael@0 616 requestCnt++;
michael@0 617 }
michael@0 618
michael@0 619 // get rid of possible trailing comma
michael@0 620 string.Trim(",", false);
michael@0 621
michael@0 622 rangeRequest = string;
michael@0 623 *numRequests = requestCnt;
michael@0 624 return;
michael@0 625 }
michael@0 626
michael@0 627 nsresult
michael@0 628 nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
michael@0 629 {
michael@0 630 nsAutoCString rangeString;
michael@0 631 int32_t numRequests;
michael@0 632
michael@0 633 MakeByteRangeString(rangeList, rangeString, &numRequests);
michael@0 634
michael@0 635 if (numRequests == 0)
michael@0 636 return NS_ERROR_FAILURE;
michael@0 637
michael@0 638 nsresult rv = NS_OK;
michael@0 639
michael@0 640 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
michael@0 641 nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
michael@0 642 nsCOMPtr<nsIChannel> channel;
michael@0 643 rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks);
michael@0 644 if (NS_FAILED(rv))
michael@0 645 return rv;
michael@0 646
michael@0 647 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
michael@0 648 if (!httpChannel)
michael@0 649 return NS_ERROR_FAILURE;
michael@0 650
michael@0 651 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
michael@0 652
michael@0 653 mAbort = true; // instruct old stream listener to cancel
michael@0 654 // the request on the next ODA.
michael@0 655
michael@0 656 nsCOMPtr<nsIStreamListener> converter;
michael@0 657
michael@0 658 if (numRequests == 1) {
michael@0 659 converter = this;
michael@0 660 // set current stream offset equal to the first offset in the range list
michael@0 661 // it will work for single byte range request
michael@0 662 // for multy range we'll reset it in ODA
michael@0 663 SetStreamOffset(rangeList->offset);
michael@0 664 } else {
michael@0 665 nsWeakPtr weakpeer =
michael@0 666 do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
michael@0 667 nsPluginByteRangeStreamListener *brrListener =
michael@0 668 new nsPluginByteRangeStreamListener(weakpeer);
michael@0 669 if (brrListener)
michael@0 670 converter = brrListener;
michael@0 671 else
michael@0 672 return NS_ERROR_OUT_OF_MEMORY;
michael@0 673 }
michael@0 674
michael@0 675 mPendingRequests += numRequests;
michael@0 676
michael@0 677 nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
michael@0 678 if (NS_FAILED(rv))
michael@0 679 return rv;
michael@0 680 rv = container->SetData(MAGIC_REQUEST_CONTEXT);
michael@0 681 if (NS_FAILED(rv))
michael@0 682 return rv;
michael@0 683
michael@0 684 rv = channel->AsyncOpen(converter, container);
michael@0 685 if (NS_SUCCEEDED(rv))
michael@0 686 TrackRequest(channel);
michael@0 687 return rv;
michael@0 688 }
michael@0 689
michael@0 690 nsresult
michael@0 691 nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result)
michael@0 692 {
michael@0 693 *result = mStreamOffset;
michael@0 694 return NS_OK;
michael@0 695 }
michael@0 696
michael@0 697 nsresult
michael@0 698 nsPluginStreamListenerPeer::SetStreamOffset(int32_t value)
michael@0 699 {
michael@0 700 mStreamOffset = value;
michael@0 701 return NS_OK;
michael@0 702 }
michael@0 703
michael@0 704 nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
michael@0 705 nsISupports* aContext)
michael@0 706 {
michael@0 707 if (!mPluginInstance)
michael@0 708 return NS_ERROR_FAILURE;
michael@0 709
michael@0 710 // mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
michael@0 711 mPluginInstance->Stop();
michael@0 712 mPluginInstance->Start();
michael@0 713 nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
michael@0 714 if (owner) {
michael@0 715 NPWindow* window = nullptr;
michael@0 716 owner->GetWindow(window);
michael@0 717 #if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT)
michael@0 718 // Should call GetPluginPort() here.
michael@0 719 // This part is copied from nsPluginInstanceOwner::GetPluginPort().
michael@0 720 nsCOMPtr<nsIWidget> widget;
michael@0 721 ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
michael@0 722 if (widget) {
michael@0 723 window->window = widget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
michael@0 724 }
michael@0 725 #endif
michael@0 726 owner->CallSetWindow();
michael@0 727 }
michael@0 728
michael@0 729 mSeekable = false;
michael@0 730 mPStreamListener->OnStartBinding(this);
michael@0 731 mStreamOffset = 0;
michael@0 732
michael@0 733 // force the plugin to use stream as file
michael@0 734 mStreamType = NP_ASFILE;
michael@0 735
michael@0 736 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 737 if (channel) {
michael@0 738 SetupPluginCacheFile(channel);
michael@0 739 }
michael@0 740
michael@0 741 // unset mPendingRequests
michael@0 742 mPendingRequests = 0;
michael@0 743
michael@0 744 return NS_OK;
michael@0 745 }
michael@0 746
michael@0 747 bool
michael@0 748 nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
michael@0 749 {
michael@0 750 NS_ENSURE_TRUE(psi, false);
michael@0 751
michael@0 752 if (psi->mLength == mLength &&
michael@0 753 psi->mModified == mModified &&
michael@0 754 mStreamComplete &&
michael@0 755 mURLSpec.Equals(psi->mURLSpec))
michael@0 756 {
michael@0 757 return true;
michael@0 758 }
michael@0 759 return false;
michael@0 760 }
michael@0 761
michael@0 762 NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
michael@0 763 nsISupports* aContext,
michael@0 764 nsIInputStream *aIStream,
michael@0 765 uint64_t sourceOffset,
michael@0 766 uint32_t aLength)
michael@0 767 {
michael@0 768 if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) {
michael@0 769 MOZ_ASSERT(false, "Received OnDataAvailable for untracked request.");
michael@0 770 return NS_ERROR_UNEXPECTED;
michael@0 771 }
michael@0 772
michael@0 773 if (mRequestFailed)
michael@0 774 return NS_ERROR_FAILURE;
michael@0 775
michael@0 776 if (mAbort) {
michael@0 777 uint32_t magicNumber = 0; // set it to something that is not the magic number.
michael@0 778 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
michael@0 779 if (container)
michael@0 780 container->GetData(&magicNumber);
michael@0 781
michael@0 782 if (magicNumber != MAGIC_REQUEST_CONTEXT) {
michael@0 783 // this is not one of our range requests
michael@0 784 mAbort = false;
michael@0 785 return NS_BINDING_ABORTED;
michael@0 786 }
michael@0 787 }
michael@0 788
michael@0 789 nsresult rv = NS_OK;
michael@0 790
michael@0 791 if (!mPStreamListener)
michael@0 792 return NS_ERROR_FAILURE;
michael@0 793
michael@0 794 const char * url = nullptr;
michael@0 795 GetURL(&url);
michael@0 796
michael@0 797 PLUGIN_LOG(PLUGIN_LOG_NOISY,
michael@0 798 ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n",
michael@0 799 this, request, sourceOffset, aLength, url ? url : "no url set"));
michael@0 800
michael@0 801 // if the plugin has requested an AsFileOnly stream, then don't
michael@0 802 // call OnDataAvailable
michael@0 803 if (mStreamType != NP_ASFILEONLY) {
michael@0 804 // get the absolute offset of the request, if one exists.
michael@0 805 nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
michael@0 806 if (brr) {
michael@0 807 if (!mDataForwardToRequest)
michael@0 808 return NS_ERROR_FAILURE;
michael@0 809
michael@0 810 int64_t absoluteOffset64 = 0;
michael@0 811 brr->GetStartRange(&absoluteOffset64);
michael@0 812
michael@0 813 // XXX handle 64-bit for real
michael@0 814 int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
michael@0 815
michael@0 816 // we need to track how much data we have forwarded to the
michael@0 817 // plugin.
michael@0 818
michael@0 819 // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
michael@0 820 //
michael@0 821 // Why couldn't this be tracked on the plugin info, and not in a
michael@0 822 // *hash table*?
michael@0 823 int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset);
michael@0 824 mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength));
michael@0 825
michael@0 826 SetStreamOffset(absoluteOffset + amtForwardToPlugin);
michael@0 827 }
michael@0 828
michael@0 829 nsCOMPtr<nsIInputStream> stream = aIStream;
michael@0 830
michael@0 831 // if we are caching the file ourselves to disk, we want to 'tee' off
michael@0 832 // the data as the plugin read from the stream. We do this by the magic
michael@0 833 // of an input stream tee.
michael@0 834
michael@0 835 if (mFileCacheOutputStream) {
michael@0 836 rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
michael@0 837 if (NS_FAILED(rv))
michael@0 838 return rv;
michael@0 839 }
michael@0 840
michael@0 841 rv = mPStreamListener->OnDataAvailable(this,
michael@0 842 stream,
michael@0 843 aLength);
michael@0 844
michael@0 845 // if a plugin returns an error, the peer must kill the stream
michael@0 846 // else the stream and PluginStreamListener leak
michael@0 847 if (NS_FAILED(rv))
michael@0 848 request->Cancel(rv);
michael@0 849 }
michael@0 850 else
michael@0 851 {
michael@0 852 // if we don't read from the stream, OnStopRequest will never be called
michael@0 853 char* buffer = new char[aLength];
michael@0 854 uint32_t amountRead, amountWrote = 0;
michael@0 855 rv = aIStream->Read(buffer, aLength, &amountRead);
michael@0 856
michael@0 857 // if we are caching this to disk ourselves, lets write the bytes out.
michael@0 858 if (mFileCacheOutputStream) {
michael@0 859 while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
michael@0 860 rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote);
michael@0 861 }
michael@0 862 }
michael@0 863 delete [] buffer;
michael@0 864 }
michael@0 865 return rv;
michael@0 866 }
michael@0 867
michael@0 868 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
michael@0 869 nsISupports* aContext,
michael@0 870 nsresult aStatus)
michael@0 871 {
michael@0 872 nsresult rv = NS_OK;
michael@0 873
michael@0 874 nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request);
michael@0 875 if (!mp) {
michael@0 876 bool found = mRequests.RemoveObject(request);
michael@0 877 if (!found) {
michael@0 878 NS_ERROR("Received OnStopRequest for untracked request.");
michael@0 879 }
michael@0 880 }
michael@0 881
michael@0 882 PLUGIN_LOG(PLUGIN_LOG_NOISY,
michael@0 883 ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
michael@0 884 this, aStatus, request));
michael@0 885
michael@0 886 // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
michael@0 887 nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
michael@0 888 if (brr) {
michael@0 889 int64_t absoluteOffset64 = 0;
michael@0 890 brr->GetStartRange(&absoluteOffset64);
michael@0 891 // XXX support 64-bit offsets
michael@0 892 int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
michael@0 893
michael@0 894 // remove the request from our data forwarding count hash.
michael@0 895 mDataForwardToRequest->Remove(absoluteOffset);
michael@0 896
michael@0 897
michael@0 898 PLUGIN_LOG(PLUGIN_LOG_NOISY,
michael@0 899 (" ::OnStopRequest for ByteRangeRequest Started=%d\n",
michael@0 900 absoluteOffset));
michael@0 901 } else {
michael@0 902 // if this is not byte range request and
michael@0 903 // if we are writting the stream to disk ourselves,
michael@0 904 // close & tear it down here
michael@0 905 mFileCacheOutputStream = nullptr;
michael@0 906 }
michael@0 907
michael@0 908 // if we still have pending stuff to do, lets not close the plugin socket.
michael@0 909 if (--mPendingRequests > 0)
michael@0 910 return NS_OK;
michael@0 911
michael@0 912 // we keep our connections around...
michael@0 913 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
michael@0 914 if (container) {
michael@0 915 uint32_t magicNumber = 0; // set it to something that is not the magic number.
michael@0 916 container->GetData(&magicNumber);
michael@0 917 if (magicNumber == MAGIC_REQUEST_CONTEXT) {
michael@0 918 // this is one of our range requests
michael@0 919 return NS_OK;
michael@0 920 }
michael@0 921 }
michael@0 922
michael@0 923 if (!mPStreamListener)
michael@0 924 return NS_ERROR_FAILURE;
michael@0 925
michael@0 926 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 927 if (!channel)
michael@0 928 return NS_ERROR_FAILURE;
michael@0 929 // Set the content type to ensure we don't pass null to the plugin
michael@0 930 nsAutoCString aContentType;
michael@0 931 rv = channel->GetContentType(aContentType);
michael@0 932 if (NS_FAILED(rv) && !mRequestFailed)
michael@0 933 return rv;
michael@0 934
michael@0 935 if (!aContentType.IsEmpty())
michael@0 936 mContentType = aContentType;
michael@0 937
michael@0 938 // set error status if stream failed so we notify the plugin
michael@0 939 if (mRequestFailed)
michael@0 940 aStatus = NS_ERROR_FAILURE;
michael@0 941
michael@0 942 if (NS_FAILED(aStatus)) {
michael@0 943 // on error status cleanup the stream
michael@0 944 // and return w/o OnFileAvailable()
michael@0 945 mPStreamListener->OnStopBinding(this, aStatus);
michael@0 946 return NS_OK;
michael@0 947 }
michael@0 948
michael@0 949 // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
michael@0 950 if (mStreamType >= NP_ASFILE) {
michael@0 951 nsCOMPtr<nsIFile> localFile;
michael@0 952 if (mLocalCachedFileHolder)
michael@0 953 localFile = mLocalCachedFileHolder->file();
michael@0 954 else {
michael@0 955 // see if it is a file channel.
michael@0 956 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
michael@0 957 if (fileChannel) {
michael@0 958 fileChannel->GetFile(getter_AddRefs(localFile));
michael@0 959 }
michael@0 960 }
michael@0 961
michael@0 962 if (localFile) {
michael@0 963 OnFileAvailable(localFile);
michael@0 964 }
michael@0 965 }
michael@0 966
michael@0 967 if (mStartBinding) {
michael@0 968 // On start binding has been called
michael@0 969 mPStreamListener->OnStopBinding(this, aStatus);
michael@0 970 } else {
michael@0 971 // OnStartBinding hasn't been called, so complete the action.
michael@0 972 mPStreamListener->OnStartBinding(this);
michael@0 973 mPStreamListener->OnStopBinding(this, aStatus);
michael@0 974 }
michael@0 975
michael@0 976 if (NS_SUCCEEDED(aStatus)) {
michael@0 977 mStreamComplete = true;
michael@0 978 }
michael@0 979
michael@0 980 return NS_OK;
michael@0 981 }
michael@0 982
michael@0 983 nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
michael@0 984 nsIURI* aURL)
michael@0 985 {
michael@0 986 nsresult rv = NS_OK;
michael@0 987
michael@0 988 // If we don't yet have a stream listener, we need to get
michael@0 989 // one from the plugin.
michael@0 990 // NOTE: this should only happen when a stream was NOT created
michael@0 991 // with GetURL or PostURL (i.e. it's the initial stream we
michael@0 992 // send to the plugin as determined by the SRC or DATA attribute)
michael@0 993 if (!mPStreamListener) {
michael@0 994 if (!mPluginInstance) {
michael@0 995 return NS_ERROR_FAILURE;
michael@0 996 }
michael@0 997
michael@0 998 nsRefPtr<nsNPAPIPluginStreamListener> streamListener;
michael@0 999 rv = mPluginInstance->NewStreamListener(nullptr, nullptr,
michael@0 1000 getter_AddRefs(streamListener));
michael@0 1001 if (NS_FAILED(rv) || !streamListener) {
michael@0 1002 return NS_ERROR_FAILURE;
michael@0 1003 }
michael@0 1004
michael@0 1005 mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
michael@0 1006 }
michael@0 1007
michael@0 1008 mPStreamListener->SetStreamListenerPeer(this);
michael@0 1009
michael@0 1010 bool useLocalCache = false;
michael@0 1011
michael@0 1012 // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
michael@0 1013 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 1014 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
michael@0 1015
michael@0 1016 /*
michael@0 1017 * Assumption
michael@0 1018 * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
michael@0 1019 * called, all the headers have been read.
michael@0 1020 */
michael@0 1021 if (httpChannel) {
michael@0 1022 // Reassemble the HTTP response status line and provide it to our
michael@0 1023 // listener. Would be nice if we could get the raw status line,
michael@0 1024 // but nsIHttpChannel doesn't currently provide that.
michael@0 1025 // Status code: required; the status line isn't useful without it.
michael@0 1026 uint32_t statusNum;
michael@0 1027 if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) &&
michael@0 1028 statusNum < 1000) {
michael@0 1029 // HTTP version: provide if available. Defaults to empty string.
michael@0 1030 nsCString ver;
michael@0 1031 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
michael@0 1032 do_QueryInterface(channel);
michael@0 1033 if (httpChannelInternal) {
michael@0 1034 uint32_t major, minor;
michael@0 1035 if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major,
michael@0 1036 &minor))) {
michael@0 1037 ver = nsPrintfCString("/%lu.%lu", major, minor);
michael@0 1038 }
michael@0 1039 }
michael@0 1040
michael@0 1041 // Status text: provide if available. Defaults to "OK".
michael@0 1042 nsCString statusText;
michael@0 1043 if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
michael@0 1044 statusText = "OK";
michael@0 1045 }
michael@0 1046
michael@0 1047 // Assemble everything and pass to listener.
michael@0 1048 nsPrintfCString status("HTTP%s %lu %s", ver.get(), statusNum,
michael@0 1049 statusText.get());
michael@0 1050 static_cast<nsIHTTPHeaderListener*>(mPStreamListener)->StatusLine(status.get());
michael@0 1051 }
michael@0 1052
michael@0 1053 // Also provide all HTTP response headers to our listener.
michael@0 1054 httpChannel->VisitResponseHeaders(this);
michael@0 1055
michael@0 1056 mSeekable = false;
michael@0 1057 // first we look for a content-encoding header. If we find one, we tell the
michael@0 1058 // plugin that stream is not seekable, because the plugin always sees
michael@0 1059 // uncompressed data, so it can't make meaningful range requests on a
michael@0 1060 // compressed entity. Also, we force the plugin to use
michael@0 1061 // nsPluginStreamType_AsFile stream type and we have to save decompressed
michael@0 1062 // file into local plugin cache, because necko cache contains original
michael@0 1063 // compressed file.
michael@0 1064 nsAutoCString contentEncoding;
michael@0 1065 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
michael@0 1066 contentEncoding))) {
michael@0 1067 useLocalCache = true;
michael@0 1068 } else {
michael@0 1069 // set seekability (seekable if the stream has a known length and if the
michael@0 1070 // http server accepts byte ranges).
michael@0 1071 uint32_t length;
michael@0 1072 GetLength(&length);
michael@0 1073 if (length) {
michael@0 1074 nsAutoCString range;
michael@0 1075 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
michael@0 1076 range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) {
michael@0 1077 mSeekable = true;
michael@0 1078 }
michael@0 1079 }
michael@0 1080 }
michael@0 1081
michael@0 1082 // we require a content len
michael@0 1083 // get Last-Modified header for plugin info
michael@0 1084 nsAutoCString lastModified;
michael@0 1085 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) &&
michael@0 1086 !lastModified.IsEmpty()) {
michael@0 1087 PRTime time64;
michael@0 1088 PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time
michael@0 1089
michael@0 1090 // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
michael@0 1091 double fpTime = double(time64);
michael@0 1092 mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
michael@0 1093 }
michael@0 1094 }
michael@0 1095
michael@0 1096 rv = mPStreamListener->OnStartBinding(this);
michael@0 1097
michael@0 1098 mStartBinding = true;
michael@0 1099
michael@0 1100 if (NS_FAILED(rv))
michael@0 1101 return rv;
michael@0 1102
michael@0 1103 mPStreamListener->GetStreamType(&mStreamType);
michael@0 1104
michael@0 1105 if (!useLocalCache && mStreamType >= NP_ASFILE) {
michael@0 1106 // check it out if this is not a file channel.
michael@0 1107 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
michael@0 1108 if (!fileChannel) {
michael@0 1109 useLocalCache = true;
michael@0 1110 }
michael@0 1111 }
michael@0 1112
michael@0 1113 if (useLocalCache) {
michael@0 1114 SetupPluginCacheFile(channel);
michael@0 1115 }
michael@0 1116
michael@0 1117 return NS_OK;
michael@0 1118 }
michael@0 1119
michael@0 1120 nsresult
michael@0 1121 nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
michael@0 1122 {
michael@0 1123 nsresult rv;
michael@0 1124 if (!mPStreamListener)
michael@0 1125 return NS_ERROR_FAILURE;
michael@0 1126
michael@0 1127 nsAutoCString path;
michael@0 1128 rv = aFile->GetNativePath(path);
michael@0 1129 if (NS_FAILED(rv)) return rv;
michael@0 1130
michael@0 1131 if (path.IsEmpty()) {
michael@0 1132 NS_WARNING("empty path");
michael@0 1133 return NS_OK;
michael@0 1134 }
michael@0 1135
michael@0 1136 rv = mPStreamListener->OnFileAvailable(this, path.get());
michael@0 1137 return rv;
michael@0 1138 }
michael@0 1139
michael@0 1140 NS_IMETHODIMP
michael@0 1141 nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
michael@0 1142 {
michael@0 1143 return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(),
michael@0 1144 PromiseFlatCString(value).get());
michael@0 1145 }
michael@0 1146
michael@0 1147 nsresult
michael@0 1148 nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result)
michael@0 1149 {
michael@0 1150 if (!mPluginInstance) {
michael@0 1151 return NS_ERROR_FAILURE;
michael@0 1152 }
michael@0 1153
michael@0 1154 nsRefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
michael@0 1155 if (owner) {
michael@0 1156 nsCOMPtr<nsIDocument> doc;
michael@0 1157 nsresult rv = owner->GetDocument(getter_AddRefs(doc));
michael@0 1158 if (NS_SUCCEEDED(rv) && doc) {
michael@0 1159 nsPIDOMWindow *window = doc->GetWindow();
michael@0 1160 if (window) {
michael@0 1161 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
michael@0 1162 nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav);
michael@0 1163 return ir->GetInterface(aIID, result);
michael@0 1164 }
michael@0 1165 }
michael@0 1166 }
michael@0 1167
michael@0 1168 return NS_ERROR_FAILURE;
michael@0 1169 }
michael@0 1170
michael@0 1171 NS_IMETHODIMP
michael@0 1172 nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result)
michael@0 1173 {
michael@0 1174 // Provide nsIChannelEventSink ourselves, otherwise let our document's
michael@0 1175 // script global object owner provide the interface.
michael@0 1176 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
michael@0 1177 return QueryInterface(aIID, result);
michael@0 1178 }
michael@0 1179
michael@0 1180 return GetInterfaceGlobal(aIID, result);
michael@0 1181 }
michael@0 1182
michael@0 1183 /**
michael@0 1184 * Proxy class which forwards async redirect notifications back to the necko
michael@0 1185 * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with
michael@0 1186 * which channel is active.
michael@0 1187 */
michael@0 1188 class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback
michael@0 1189 {
michael@0 1190 public:
michael@0 1191 ChannelRedirectProxyCallback(nsPluginStreamListenerPeer* listener,
michael@0 1192 nsIAsyncVerifyRedirectCallback* parent,
michael@0 1193 nsIChannel* oldChannel,
michael@0 1194 nsIChannel* newChannel)
michael@0 1195 : mWeakListener(do_GetWeakReference(static_cast<nsIStreamListener*>(listener)))
michael@0 1196 , mParent(parent)
michael@0 1197 , mOldChannel(oldChannel)
michael@0 1198 , mNewChannel(newChannel)
michael@0 1199 {
michael@0 1200 }
michael@0 1201
michael@0 1202 ChannelRedirectProxyCallback() {}
michael@0 1203 virtual ~ChannelRedirectProxyCallback() {}
michael@0 1204
michael@0 1205 NS_DECL_ISUPPORTS
michael@0 1206
michael@0 1207 NS_IMETHODIMP OnRedirectVerifyCallback(nsresult result)
michael@0 1208 {
michael@0 1209 if (NS_SUCCEEDED(result)) {
michael@0 1210 nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener);
michael@0 1211 if (listener)
michael@0 1212 static_cast<nsPluginStreamListenerPeer*>(listener.get())->ReplaceRequest(mOldChannel, mNewChannel);
michael@0 1213 }
michael@0 1214 return mParent->OnRedirectVerifyCallback(result);
michael@0 1215 }
michael@0 1216
michael@0 1217 private:
michael@0 1218 nsWeakPtr mWeakListener;
michael@0 1219 nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent;
michael@0 1220 nsCOMPtr<nsIChannel> mOldChannel;
michael@0 1221 nsCOMPtr<nsIChannel> mNewChannel;
michael@0 1222 };
michael@0 1223
michael@0 1224 NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
michael@0 1225
michael@0 1226
michael@0 1227 NS_IMETHODIMP
michael@0 1228 nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
michael@0 1229 uint32_t flags, nsIAsyncVerifyRedirectCallback* callback)
michael@0 1230 {
michael@0 1231 // Disallow redirects if we don't have a stream listener.
michael@0 1232 if (!mPStreamListener) {
michael@0 1233 return NS_ERROR_FAILURE;
michael@0 1234 }
michael@0 1235
michael@0 1236 nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
michael@0 1237 new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
michael@0 1238
michael@0 1239 // Give NPAPI a chance to control redirects.
michael@0 1240 bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
michael@0 1241 if (notificationHandled) {
michael@0 1242 return NS_OK;
michael@0 1243 }
michael@0 1244
michael@0 1245 // Don't allow cross-origin 307 POST redirects.
michael@0 1246 nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel));
michael@0 1247 if (oldHttpChannel) {
michael@0 1248 uint32_t responseStatus;
michael@0 1249 nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus);
michael@0 1250 if (NS_FAILED(rv)) {
michael@0 1251 return rv;
michael@0 1252 }
michael@0 1253 if (responseStatus == 307) {
michael@0 1254 nsAutoCString method;
michael@0 1255 rv = oldHttpChannel->GetRequestMethod(method);
michael@0 1256 if (NS_FAILED(rv)) {
michael@0 1257 return rv;
michael@0 1258 }
michael@0 1259 if (method.EqualsLiteral("POST")) {
michael@0 1260 rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel);
michael@0 1261 if (NS_FAILED(rv)) {
michael@0 1262 return rv;
michael@0 1263 }
michael@0 1264 }
michael@0 1265 }
michael@0 1266 }
michael@0 1267
michael@0 1268 // Fall back to channel event sink for window.
michael@0 1269 nsCOMPtr<nsIChannelEventSink> channelEventSink;
michael@0 1270 nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink));
michael@0 1271 if (NS_FAILED(rv)) {
michael@0 1272 return rv;
michael@0 1273 }
michael@0 1274
michael@0 1275 return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback);
michael@0 1276 }

mercurial