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

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/DebugOnly.h"
       8             : 
       9             : #include "DecoderTraits.h"
      10             : #include "MediaResource.h"
      11             : #include "MediaResourceCallback.h"
      12             : 
      13             : #include "mozilla/Mutex.h"
      14             : #include "nsDebug.h"
      15             : #include "nsNetUtil.h"
      16             : #include "nsThreadUtils.h"
      17             : #include "nsIFile.h"
      18             : #include "nsIFileChannel.h"
      19             : #include "nsIFileStreams.h"
      20             : #include "nsIHttpChannel.h"
      21             : #include "nsISeekableStream.h"
      22             : #include "nsIInputStream.h"
      23             : #include "nsIRequestObserver.h"
      24             : #include "nsIStreamListener.h"
      25             : #include "nsIScriptSecurityManager.h"
      26             : #include "mozilla/dom/HTMLMediaElement.h"
      27             : #include "nsError.h"
      28             : #include "nsICachingChannel.h"
      29             : #include "nsIAsyncVerifyRedirectCallback.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsHostObjectProtocolHandler.h"
      32             : #include <algorithm>
      33             : #include "nsProxyRelease.h"
      34             : #include "nsIContentPolicy.h"
      35             : 
      36             : using mozilla::media::TimeUnit;
      37             : 
      38             : #undef LOG
      39             : #undef ILOG
      40             : 
      41             : mozilla::LazyLogModule gMediaResourceLog("MediaResource");
      42             : // Debug logging macro with object pointer and class name.
      43             : #define LOG(msg, ...) MOZ_LOG(gMediaResourceLog, mozilla::LogLevel::Debug, \
      44             :   ("%p " msg, this, ##__VA_ARGS__))
      45             : 
      46             : mozilla::LazyLogModule gMediaResourceIndexLog("MediaResourceIndex");
      47             : // Debug logging macro with object pointer and class name.
      48             : #define ILOG(msg, ...)                                                         \
      49             :   MOZ_LOG(gMediaResourceIndexLog,                                              \
      50             :           mozilla::LogLevel::Debug,                                            \
      51             :           ("%p " msg, this, ##__VA_ARGS__))
      52             : 
      53             : static const uint32_t HTTP_OK_CODE = 200;
      54             : static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
      55             : 
      56             : namespace mozilla {
      57             : 
      58             : void
      59           0 : MediaResource::Destroy()
      60             : {
      61             :   // Ensures we only delete the MediaResource on the main thread.
      62           0 :   if (NS_IsMainThread()) {
      63           0 :     delete this;
      64           0 :     return;
      65             :   }
      66           0 :   nsresult rv = SystemGroup::Dispatch(
      67             :     "MediaResource::Destroy",
      68             :     TaskCategory::Other,
      69           0 :     NewNonOwningRunnableMethod(
      70           0 :       "MediaResource::Destroy", this, &MediaResource::Destroy));
      71           0 :   MOZ_ALWAYS_SUCCEEDS(rv);
      72             : }
      73             : 
      74           0 : NS_IMPL_ADDREF(MediaResource)
      75           0 : NS_IMPL_RELEASE_WITH_DESTROY(MediaResource, Destroy())
      76           0 : NS_IMPL_QUERY_INTERFACE0(MediaResource)
      77             : 
      78           0 : ChannelMediaResource::ChannelMediaResource(MediaResourceCallback* aCallback,
      79             :                                            nsIChannel* aChannel,
      80             :                                            nsIURI* aURI,
      81           0 :                                            bool aIsPrivateBrowsing)
      82             :   : BaseMediaResource(aCallback, aChannel, aURI)
      83             :   , mOffset(0)
      84             :   , mReopenOnError(false)
      85             :   , mIgnoreClose(false)
      86             :   , mCacheStream(this, aIsPrivateBrowsing)
      87             :   , mLock("ChannelMediaResource.mLock")
      88             :   , mIgnoreResume(false)
      89           0 :   , mSuspendAgent(mChannel)
      90             : {
      91           0 : }
      92             : 
      93           0 : ChannelMediaResource::ChannelMediaResource(
      94             :   MediaResourceCallback* aCallback,
      95             :   nsIChannel* aChannel,
      96             :   nsIURI* aURI,
      97           0 :   const MediaChannelStatistics& aStatistics)
      98             :   : BaseMediaResource(aCallback, aChannel, aURI)
      99             :   , mOffset(0)
     100             :   , mReopenOnError(false)
     101             :   , mIgnoreClose(false)
     102             :   , mCacheStream(this, /* aIsPrivateBrowsing = */ false)
     103             :   , mLock("ChannelMediaResource.mLock")
     104             :   , mChannelStatistics(aStatistics)
     105             :   , mIgnoreResume(false)
     106           0 :   , mSuspendAgent(mChannel)
     107             : {
     108           0 : }
     109             : 
     110           0 : ChannelMediaResource::~ChannelMediaResource()
     111             : {
     112           0 :   if (mListener) {
     113             :     // Kill its reference to us since we're going away
     114           0 :     mListener->Revoke();
     115             :   }
     116           0 : }
     117             : 
     118             : // ChannelMediaResource::Listener just observes the channel and
     119             : // forwards notifications to the ChannelMediaResource. We use multiple
     120             : // listener objects so that when we open a new stream for a seek we can
     121             : // disconnect the old listener from the ChannelMediaResource and hook up
     122             : // a new listener, so notifications from the old channel are discarded
     123             : // and don't confuse us.
     124           0 : NS_IMPL_ISUPPORTS(ChannelMediaResource::Listener,
     125             :                   nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
     126             :                   nsIInterfaceRequestor)
     127             : 
     128             : nsresult
     129           0 : ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest,
     130             :                                                nsISupports* aContext)
     131             : {
     132           0 :   if (!mResource)
     133           0 :     return NS_OK;
     134           0 :   return mResource->OnStartRequest(aRequest);
     135             : }
     136             : 
     137             : nsresult
     138           0 : ChannelMediaResource::Listener::OnStopRequest(nsIRequest* aRequest,
     139             :                                               nsISupports* aContext,
     140             :                                               nsresult aStatus)
     141             : {
     142           0 :   if (!mResource)
     143           0 :     return NS_OK;
     144           0 :   return mResource->OnStopRequest(aRequest, aStatus);
     145             : }
     146             : 
     147             : nsresult
     148           0 : ChannelMediaResource::Listener::OnDataAvailable(nsIRequest* aRequest,
     149             :                                                 nsISupports* aContext,
     150             :                                                 nsIInputStream* aStream,
     151             :                                                 uint64_t aOffset,
     152             :                                                 uint32_t aCount)
     153             : {
     154           0 :   if (!mResource)
     155           0 :     return NS_OK;
     156           0 :   return mResource->OnDataAvailable(aRequest, aStream, aCount);
     157             : }
     158             : 
     159             : nsresult
     160           0 : ChannelMediaResource::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
     161             :                                                        nsIChannel* aNewChannel,
     162             :                                                        uint32_t aFlags,
     163             :                                                        nsIAsyncVerifyRedirectCallback* cb)
     164             : {
     165           0 :   nsresult rv = NS_OK;
     166           0 :   if (mResource)
     167           0 :     rv = mResource->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
     168             : 
     169           0 :   if (NS_FAILED(rv))
     170           0 :     return rv;
     171             : 
     172           0 :   cb->OnRedirectVerifyCallback(NS_OK);
     173           0 :   return NS_OK;
     174             : }
     175             : 
     176             : nsresult
     177           0 : ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
     178             : {
     179           0 :   return QueryInterface(aIID, aResult);
     180             : }
     181             : 
     182             : static bool
     183           0 : IsPayloadCompressed(nsIHttpChannel* aChannel)
     184             : {
     185           0 :   nsAutoCString encoding;
     186           0 :   Unused << aChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), encoding);
     187           0 :   return encoding.Length() > 0;
     188             : }
     189             : 
     190             : nsresult
     191           0 : ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
     192             : {
     193           0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     194             : 
     195           0 :   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     196           0 :   NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     197           0 :   dom::HTMLMediaElement* element = owner->GetMediaElement();
     198           0 :   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     199             :   nsresult status;
     200           0 :   nsresult rv = aRequest->GetStatus(&status);
     201           0 :   NS_ENSURE_SUCCESS(rv, rv);
     202             : 
     203           0 :   if (status == NS_BINDING_ABORTED) {
     204             :     // Request was aborted before we had a chance to receive any data, or
     205             :     // even an OnStartRequest(). Close the channel. This is important, as
     206             :     // we don't want to mess up our state, as if we're cloned that would
     207             :     // cause the clone to copy incorrect metadata (like whether we're
     208             :     // infinite for example).
     209           0 :     CloseChannel();
     210           0 :     return status;
     211             :   }
     212             : 
     213           0 :   if (element->ShouldCheckAllowOrigin()) {
     214             :     // If the request was cancelled by nsCORSListenerProxy due to failing
     215             :     // the CORS security check, send an error through to the media element.
     216           0 :     if (status == NS_ERROR_DOM_BAD_URI) {
     217           0 :       mCallback->NotifyNetworkError();
     218           0 :       return NS_ERROR_DOM_BAD_URI;
     219             :     }
     220             :   }
     221             : 
     222           0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
     223           0 :   bool seekable = false;
     224           0 :   if (hc) {
     225           0 :     uint32_t responseStatus = 0;
     226           0 :     Unused << hc->GetResponseStatus(&responseStatus);
     227           0 :     bool succeeded = false;
     228           0 :     Unused << hc->GetRequestSucceeded(&succeeded);
     229             : 
     230           0 :     if (!succeeded && NS_SUCCEEDED(status)) {
     231             :       // HTTP-level error (e.g. 4xx); treat this as a fatal network-level error.
     232             :       // We might get this on a seek.
     233             :       // (Note that lower-level errors indicated by NS_FAILED(status) are
     234             :       // handled in OnStopRequest.)
     235             :       // A 416 error should treated as EOF here... it's possible
     236             :       // that we don't get Content-Length, we read N bytes, then we
     237             :       // suspend and resume, the resume reopens the channel and we seek to
     238             :       // offset N, but there are no more bytes, so we get a 416
     239             :       // "Requested Range Not Satisfiable".
     240           0 :       if (responseStatus == HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE) {
     241             :         // OnStopRequest will not be fired, so we need to do some of its
     242             :         // work here.
     243           0 :         mCacheStream.NotifyDataEnded(status);
     244             :       } else {
     245           0 :         mCallback->NotifyNetworkError();
     246             :       }
     247             : 
     248             :       // This disconnects our listener so we don't get any more data. We
     249             :       // certainly don't want an error page to end up in our cache!
     250           0 :       CloseChannel();
     251           0 :       return NS_OK;
     252             :     }
     253             : 
     254           0 :     nsAutoCString ranges;
     255           0 :     Unused << hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
     256           0 :                                     ranges);
     257           0 :     bool acceptsRanges = ranges.EqualsLiteral("bytes");
     258             :     // True if this channel will not return an unbounded amount of data
     259           0 :     bool dataIsBounded = false;
     260             : 
     261           0 :     int64_t contentLength = -1;
     262           0 :     const bool isCompressed = IsPayloadCompressed(hc);
     263           0 :     if (!isCompressed) {
     264           0 :       hc->GetContentLength(&contentLength);
     265             :     }
     266           0 :     if (contentLength >= 0 &&
     267           0 :         (responseStatus == HTTP_OK_CODE ||
     268           0 :          responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
     269             :       // "OK" status means Content-Length is for the whole resource.
     270             :       // Since that's bounded, we know we have a finite-length resource.
     271           0 :       dataIsBounded = true;
     272             :     }
     273             : 
     274             :     // Assume Range requests have a bounded upper limit unless the
     275             :     // Content-Range header tells us otherwise.
     276           0 :     bool boundedSeekLimit = true;
     277             :     // Check response code for byte-range requests (seeking, chunk requests).
     278             :     // We don't expect to get a 206 response for a compressed stream, but
     279             :     // double check just to be sure.
     280           0 :     if (!isCompressed && responseStatus == HTTP_PARTIAL_RESPONSE_CODE) {
     281             :       // Parse Content-Range header.
     282           0 :       int64_t rangeStart = 0;
     283           0 :       int64_t rangeEnd = 0;
     284           0 :       int64_t rangeTotal = 0;
     285           0 :       rv = ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal);
     286             : 
     287             :       // We received 'Content-Range', so the server accepts range requests.
     288           0 :       bool gotRangeHeader = NS_SUCCEEDED(rv);
     289             : 
     290           0 :       if (gotRangeHeader) {
     291             :         // We received 'Content-Range', so the server accepts range requests.
     292             :         // Notify media cache about the length and start offset of data received.
     293             :         // Note: If aRangeTotal == -1, then the total bytes is unknown at this stage.
     294             :         //       For now, tell the decoder that the stream is infinite.
     295           0 :         if (rangeTotal == -1) {
     296           0 :           boundedSeekLimit = false;
     297             :         } else {
     298           0 :           contentLength = std::max(contentLength, rangeTotal);
     299             :         }
     300             :         // Give some warnings if the ranges are unexpected.
     301             :         // XXX These could be error conditions.
     302           0 :         NS_WARNING_ASSERTION(
     303             :           mOffset == rangeStart,
     304             :           "response range start does not match current offset");
     305           0 :         mOffset = rangeStart;
     306           0 :         mCacheStream.NotifyDataStarted(rangeStart);
     307             :       }
     308           0 :       acceptsRanges = gotRangeHeader;
     309           0 :     } else if (mOffset > 0 && responseStatus == HTTP_OK_CODE) {
     310             :       // If we get an OK response but we were seeking, or requesting a byte
     311             :       // range, then we have to assume that seeking doesn't work. We also need
     312             :       // to tell the cache that it's getting data for the start of the stream.
     313           0 :       mCacheStream.NotifyDataStarted(0);
     314           0 :       mOffset = 0;
     315             : 
     316             :       // The server claimed it supported range requests.  It lied.
     317           0 :       acceptsRanges = false;
     318             :     }
     319           0 :     if (mOffset == 0 && contentLength >= 0 &&
     320           0 :         (responseStatus == HTTP_OK_CODE ||
     321           0 :          responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
     322           0 :       mCacheStream.NotifyDataLength(contentLength);
     323             :     }
     324             :     // XXX we probably should examine the Content-Range header in case
     325             :     // the server gave us a range which is not quite what we asked for
     326             : 
     327             :     // If we get an HTTP_OK_CODE response to our byte range request,
     328             :     // and the server isn't sending Accept-Ranges:bytes then we don't
     329             :     // support seeking. We also can't seek in compressed streams.
     330           0 :     seekable = !isCompressed && acceptsRanges;
     331           0 :     if (seekable && boundedSeekLimit) {
     332             :       // If range requests are supported, and we did not see an unbounded
     333             :       // upper range limit, we assume the resource is bounded.
     334           0 :       dataIsBounded = true;
     335             :     }
     336             : 
     337           0 :     mCallback->SetInfinite(!dataIsBounded);
     338             :   }
     339           0 :   mCacheStream.SetTransportSeekable(seekable);
     340             : 
     341             :   {
     342           0 :     MutexAutoLock lock(mLock);
     343           0 :     mChannelStatistics.Start();
     344             :   }
     345             : 
     346           0 :   mReopenOnError = false;
     347           0 :   mIgnoreClose = false;
     348             : 
     349           0 :   mSuspendAgent.UpdateSuspendedStatusIfNeeded();
     350             : 
     351             :   // Fires an initial progress event.
     352           0 :   owner->DownloadProgressed();
     353             : 
     354           0 :   return NS_OK;
     355             : }
     356             : 
     357             : bool
     358           0 : ChannelMediaResource::IsTransportSeekable()
     359             : {
     360           0 :   return mCacheStream.IsTransportSeekable();
     361             : }
     362             : 
     363             : nsresult
     364           0 : ChannelMediaResource::ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
     365             :                                               int64_t& aRangeStart,
     366             :                                               int64_t& aRangeEnd,
     367             :                                               int64_t& aRangeTotal)
     368             : {
     369           0 :   NS_ENSURE_ARG(aHttpChan);
     370             : 
     371           0 :   nsAutoCString rangeStr;
     372           0 :   nsresult rv = aHttpChan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Range"),
     373           0 :                                              rangeStr);
     374           0 :   NS_ENSURE_SUCCESS(rv, rv);
     375           0 :   NS_ENSURE_FALSE(rangeStr.IsEmpty(), NS_ERROR_ILLEGAL_VALUE);
     376             : 
     377             :   // Parse the range header: e.g. Content-Range: bytes 7000-7999/8000.
     378           0 :   int32_t spacePos = rangeStr.Find(NS_LITERAL_CSTRING(" "));
     379           0 :   int32_t dashPos = rangeStr.Find(NS_LITERAL_CSTRING("-"), true, spacePos);
     380           0 :   int32_t slashPos = rangeStr.Find(NS_LITERAL_CSTRING("/"), true, dashPos);
     381             : 
     382           0 :   nsAutoCString aRangeStartText;
     383           0 :   rangeStr.Mid(aRangeStartText, spacePos+1, dashPos-(spacePos+1));
     384           0 :   aRangeStart = aRangeStartText.ToInteger64(&rv);
     385           0 :   NS_ENSURE_SUCCESS(rv, rv);
     386           0 :   NS_ENSURE_TRUE(0 <= aRangeStart, NS_ERROR_ILLEGAL_VALUE);
     387             : 
     388           0 :   nsAutoCString aRangeEndText;
     389           0 :   rangeStr.Mid(aRangeEndText, dashPos+1, slashPos-(dashPos+1));
     390           0 :   aRangeEnd = aRangeEndText.ToInteger64(&rv);
     391           0 :   NS_ENSURE_SUCCESS(rv, rv);
     392           0 :   NS_ENSURE_TRUE(aRangeStart < aRangeEnd, NS_ERROR_ILLEGAL_VALUE);
     393             : 
     394           0 :   nsAutoCString aRangeTotalText;
     395           0 :   rangeStr.Right(aRangeTotalText, rangeStr.Length()-(slashPos+1));
     396           0 :   if (aRangeTotalText[0] == '*') {
     397           0 :     aRangeTotal = -1;
     398             :   } else {
     399           0 :     aRangeTotal = aRangeTotalText.ToInteger64(&rv);
     400           0 :     NS_ENSURE_TRUE(aRangeEnd < aRangeTotal, NS_ERROR_ILLEGAL_VALUE);
     401           0 :     NS_ENSURE_SUCCESS(rv, rv);
     402             :   }
     403             : 
     404           0 :   LOG("Received bytes [%" PRId64 "] to [%" PRId64 "] of [%" PRId64 "] for decoder[%p]",
     405             :       aRangeStart, aRangeEnd, aRangeTotal, mCallback.get());
     406             : 
     407           0 :   return NS_OK;
     408             : }
     409             : 
     410             : nsresult
     411           0 : ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
     412             : {
     413           0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     414           0 :   NS_ASSERTION(!mSuspendAgent.IsSuspended(),
     415             :                "How can OnStopRequest fire while we're suspended?");
     416             : 
     417             :   {
     418           0 :     MutexAutoLock lock(mLock);
     419           0 :     mChannelStatistics.Stop();
     420             :   }
     421             : 
     422             :   // Note that aStatus might have succeeded --- this might be a normal close
     423             :   // --- even in situations where the server cut us off because we were
     424             :   // suspended. So we need to "reopen on error" in that case too. The only
     425             :   // cases where we don't need to reopen are when *we* closed the stream.
     426             :   // But don't reopen if we need to seek and we don't think we can... that would
     427             :   // cause us to just re-read the stream, which would be really bad.
     428           0 :   if (mReopenOnError && aStatus != NS_ERROR_PARSED_DATA_CACHED &&
     429           0 :       aStatus != NS_BINDING_ABORTED &&
     430           0 :       (mOffset == 0 || (GetLength() > 0 && mOffset != GetLength() &&
     431           0 :                         mCacheStream.IsTransportSeekable()))) {
     432             :     // If the stream did close normally, restart the channel if we're either
     433             :     // at the start of the resource, or if the server is seekable and we're
     434             :     // not at the end of stream. We don't restart the stream if we're at the
     435             :     // end because not all web servers handle this case consistently; see:
     436             :     // https://bugzilla.mozilla.org/show_bug.cgi?id=1373618#c36
     437           0 :     nsresult rv = CacheClientSeek(mOffset, false);
     438           0 :     if (NS_SUCCEEDED(rv)) {
     439           0 :       return rv;
     440             :     }
     441             :     // If the reopen/reseek fails, just fall through and treat this
     442             :     // error as fatal.
     443             :   }
     444             : 
     445           0 :   if (!mIgnoreClose) {
     446           0 :     mCacheStream.NotifyDataEnded(aStatus);
     447             : 
     448             :     // Move this request back into the foreground.  This is necessary for
     449             :     // requests owned by video documents to ensure the load group fires
     450             :     // OnStopRequest when restoring from session history.
     451             :     nsLoadFlags loadFlags;
     452           0 :     DebugOnly<nsresult> rv = mChannel->GetLoadFlags(&loadFlags);
     453           0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");
     454             : 
     455           0 :     if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
     456           0 :       ModifyLoadFlags(loadFlags & ~nsIRequest::LOAD_BACKGROUND);
     457             :     }
     458             :   }
     459             : 
     460           0 :   return NS_OK;
     461             : }
     462             : 
     463             : nsresult
     464           0 : ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
     465             :                                         uint32_t aFlags)
     466             : {
     467           0 :   mChannel = aNew;
     468           0 :   mSuspendAgent.NotifyChannelOpened(mChannel);
     469           0 :   return SetupChannelHeaders();
     470             : }
     471             : 
     472             : nsresult
     473           0 : ChannelMediaResource::CopySegmentToCache(nsIPrincipal* aPrincipal,
     474             :                                          const char* aFromSegment,
     475             :                                          uint32_t aCount,
     476             :                                          uint32_t* aWriteCount)
     477             : {
     478             :   // Keep track of where we're up to.
     479           0 :   LOG("CopySegmentToCache at mOffset [%" PRId64 "] add "
     480             :       "[%d] bytes for decoder[%p]",
     481             :       mOffset, aCount, mCallback.get());
     482           0 :   mOffset += aCount;
     483           0 :   mCacheStream.NotifyDataReceived(aCount, aFromSegment, aPrincipal);
     484           0 :   *aWriteCount = aCount;
     485           0 :   return NS_OK;
     486             : }
     487             : 
     488             : 
     489           0 : struct CopySegmentClosure {
     490             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     491             :   ChannelMediaResource*  mResource;
     492             : };
     493             : 
     494             : nsresult
     495           0 : ChannelMediaResource::CopySegmentToCache(nsIInputStream* aInStream,
     496             :                                          void* aClosure,
     497             :                                          const char* aFromSegment,
     498             :                                          uint32_t aToOffset,
     499             :                                          uint32_t aCount,
     500             :                                          uint32_t* aWriteCount)
     501             : {
     502           0 :   CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
     503           0 :   return closure->mResource->CopySegmentToCache(
     504           0 :     closure->mPrincipal, aFromSegment, aCount, aWriteCount);
     505             : }
     506             : 
     507             : nsresult
     508           0 : ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
     509             :                                       nsIInputStream* aStream,
     510             :                                       uint32_t aCount)
     511             : {
     512           0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     513             : 
     514             :   {
     515           0 :     MutexAutoLock lock(mLock);
     516           0 :     mChannelStatistics.AddBytes(aCount);
     517             :   }
     518             : 
     519           0 :   CopySegmentClosure closure;
     520           0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     521           0 :   if (secMan && mChannel) {
     522           0 :     secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
     523             :   }
     524           0 :   closure.mResource = this;
     525             : 
     526           0 :   uint32_t count = aCount;
     527           0 :   while (count > 0) {
     528             :     uint32_t read;
     529             :     nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
     530           0 :                                         &read);
     531           0 :     if (NS_FAILED(rv))
     532           0 :       return rv;
     533           0 :     NS_ASSERTION(read > 0, "Read 0 bytes while data was available?");
     534           0 :     count -= read;
     535             :   }
     536             : 
     537           0 :   return NS_OK;
     538             : }
     539             : 
     540           0 : nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
     541             : {
     542           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     543             : 
     544           0 :   int64_t cl = -1;
     545           0 :   if (mChannel) {
     546           0 :     nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     547           0 :     if (hc && !IsPayloadCompressed(hc)) {
     548           0 :       if (NS_FAILED(hc->GetContentLength(&cl))) {
     549           0 :         cl = -1;
     550             :       }
     551             :     }
     552             :   }
     553             : 
     554           0 :   nsresult rv = mCacheStream.Init(cl);
     555           0 :   if (NS_FAILED(rv))
     556           0 :     return rv;
     557           0 :   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
     558             : 
     559           0 :   if (!mChannel) {
     560             :     // When we're a clone, the decoder might ask us to Open even though
     561             :     // we haven't established an mChannel (because we might not need one)
     562           0 :     NS_ASSERTION(!aStreamListener,
     563             :                  "Should have already been given a channel if we're to return a stream listener");
     564           0 :     return NS_OK;
     565             :   }
     566             : 
     567           0 :   return OpenChannel(aStreamListener);
     568             : }
     569             : 
     570           0 : nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
     571             : {
     572           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     573           0 :   NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
     574           0 :   NS_ASSERTION(!mListener, "Listener should have been removed by now");
     575             : 
     576           0 :   if (aStreamListener) {
     577           0 :     *aStreamListener = nullptr;
     578             :   }
     579             : 
     580             :   // Set the content length, if it's available as an HTTP header.
     581             :   // This ensures that MediaResource wrapping objects for platform libraries
     582             :   // that expect to know the length of a resource can get it before
     583             :   // OnStartRequest() fires.
     584           0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     585           0 :   if (hc && !IsPayloadCompressed(hc)) {
     586           0 :     int64_t cl = -1;
     587           0 :     if (NS_SUCCEEDED(hc->GetContentLength(&cl)) && cl != -1) {
     588           0 :       mCacheStream.NotifyDataLength(cl);
     589             :     }
     590             :   }
     591             : 
     592           0 :   mListener = new Listener(this);
     593           0 :   if (aStreamListener) {
     594           0 :     *aStreamListener = mListener;
     595           0 :     NS_ADDREF(*aStreamListener);
     596             :   } else {
     597           0 :     nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
     598           0 :     NS_ENSURE_SUCCESS(rv, rv);
     599             : 
     600           0 :     rv = SetupChannelHeaders();
     601           0 :     NS_ENSURE_SUCCESS(rv, rv);
     602             : 
     603           0 :     rv = mChannel->AsyncOpen2(mListener);
     604           0 :     NS_ENSURE_SUCCESS(rv, rv);
     605             : 
     606             :     // Tell the media element that we are fetching data from a channel.
     607           0 :     MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     608           0 :     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     609           0 :     dom::HTMLMediaElement* element = owner->GetMediaElement();
     610           0 :     element->DownloadResumed(true);
     611             :   }
     612             : 
     613           0 :   return NS_OK;
     614             : }
     615             : 
     616           0 : nsresult ChannelMediaResource::SetupChannelHeaders()
     617             : {
     618             :   // Always use a byte range request even if we're reading from the start
     619             :   // of the resource.
     620             :   // This enables us to detect if the stream supports byte range
     621             :   // requests, and therefore seeking, early.
     622           0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     623           0 :   if (hc) {
     624             :     // Use |mOffset| if seeking in a complete file download.
     625           0 :     nsAutoCString rangeString("bytes=");
     626           0 :     rangeString.AppendInt(mOffset);
     627           0 :     rangeString.Append('-');
     628           0 :     nsresult rv = hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
     629           0 :     NS_ENSURE_SUCCESS(rv, rv);
     630             : 
     631             :     // Send Accept header for video and audio types only (Bug 489071)
     632           0 :     NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     633           0 :     MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     634           0 :     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     635           0 :     dom::HTMLMediaElement* element = owner->GetMediaElement();
     636           0 :     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     637           0 :     element->SetRequestHeaders(hc);
     638             :   } else {
     639           0 :     NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
     640           0 :     return NS_ERROR_FAILURE;
     641             :   }
     642           0 :   return NS_OK;
     643             : }
     644             : 
     645           0 : nsresult ChannelMediaResource::Close()
     646             : {
     647           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     648             : 
     649           0 :   mCacheStream.Close();
     650           0 :   CloseChannel();
     651           0 :   return NS_OK;
     652             : }
     653             : 
     654           0 : already_AddRefed<nsIPrincipal> ChannelMediaResource::GetCurrentPrincipal()
     655             : {
     656           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     657             : 
     658           0 :   nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal();
     659           0 :   return principal.forget();
     660             : }
     661             : 
     662           0 : bool ChannelMediaResource::CanClone()
     663             : {
     664           0 :   return mCacheStream.IsAvailableForSharing();
     665             : }
     666             : 
     667           0 : already_AddRefed<MediaResource> ChannelMediaResource::CloneData(MediaResourceCallback* aCallback)
     668             : {
     669           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     670           0 :   NS_ASSERTION(mCacheStream.IsAvailableForSharing(), "Stream can't be cloned");
     671             : 
     672             :   RefPtr<ChannelMediaResource> resource =
     673           0 :     new ChannelMediaResource(aCallback, nullptr, mURI, mChannelStatistics);
     674           0 :   if (resource) {
     675             :     // Initially the clone is treated as suspended by the cache, because
     676             :     // we don't have a channel. If the cache needs to read data from the clone
     677             :     // it will call CacheClientResume (or CacheClientSeek with aResume true)
     678             :     // which will recreate the channel. This way, if all of the media data
     679             :     // is already in the cache we don't create an unnecessary HTTP channel
     680             :     // and perform a useless HTTP transaction.
     681           0 :     resource->mSuspendAgent.Suspend();
     682           0 :     resource->mCacheStream.InitAsClone(&mCacheStream);
     683           0 :     resource->mChannelStatistics.Stop();
     684             :   }
     685           0 :   return resource.forget();
     686             : }
     687             : 
     688           0 : void ChannelMediaResource::CloseChannel()
     689             : {
     690           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     691             : 
     692             :   {
     693           0 :     MutexAutoLock lock(mLock);
     694           0 :     mChannelStatistics.Stop();
     695             :   }
     696             : 
     697           0 :   if (mListener) {
     698           0 :     mListener->Revoke();
     699           0 :     mListener = nullptr;
     700             :   }
     701             : 
     702           0 :   if (mChannel) {
     703           0 :     mSuspendAgent.NotifyChannelClosing();
     704             :     // The status we use here won't be passed to the decoder, since
     705             :     // we've already revoked the listener. It can however be passed
     706             :     // to nsDocumentViewer::LoadComplete if our channel is the one
     707             :     // that kicked off creation of a video document. We don't want that
     708             :     // document load to think there was an error.
     709             :     // NS_ERROR_PARSED_DATA_CACHED is the best thing we have for that
     710             :     // at the moment.
     711           0 :     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     712           0 :     mChannel = nullptr;
     713             :   }
     714           0 : }
     715             : 
     716           0 : nsresult ChannelMediaResource::ReadFromCache(char* aBuffer,
     717             :                                              int64_t aOffset,
     718             :                                              uint32_t aCount)
     719             : {
     720           0 :   return mCacheStream.ReadFromCache(aBuffer, aOffset, aCount);
     721             : }
     722             : 
     723           0 : nsresult ChannelMediaResource::ReadAt(int64_t aOffset,
     724             :                                       char* aBuffer,
     725             :                                       uint32_t aCount,
     726             :                                       uint32_t* aBytes)
     727             : {
     728           0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     729             : 
     730           0 :   nsresult rv = mCacheStream.ReadAt(aOffset, aBuffer, aCount, aBytes);
     731           0 :   if (NS_SUCCEEDED(rv)) {
     732           0 :     DispatchBytesConsumed(*aBytes, aOffset);
     733             :   }
     734           0 :   return rv;
     735             : }
     736             : 
     737             : void
     738           0 : ChannelMediaResource::ThrottleReadahead(bool bThrottle)
     739             : {
     740           0 :   mCacheStream.ThrottleReadahead(bThrottle);
     741           0 : }
     742             : 
     743           0 : int64_t ChannelMediaResource::Tell()
     744             : {
     745           0 :   return mCacheStream.Tell();
     746             : }
     747             : 
     748           0 : nsresult ChannelMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges)
     749             : {
     750           0 :   return mCacheStream.GetCachedRanges(aRanges);
     751             : }
     752             : 
     753           0 : void ChannelMediaResource::Suspend(bool aCloseImmediately)
     754             : {
     755           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     756             : 
     757           0 :   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     758           0 :   if (!owner) {
     759             :     // Shutting down; do nothing.
     760           0 :     return;
     761             :   }
     762           0 :   dom::HTMLMediaElement* element = owner->GetMediaElement();
     763           0 :   if (!element) {
     764             :     // Shutting down; do nothing.
     765           0 :     return;
     766             :   }
     767             : 
     768           0 :   if (mChannel && aCloseImmediately && mCacheStream.IsTransportSeekable()) {
     769             :     // Kill off our channel right now, but don't tell anyone about it.
     770           0 :     mIgnoreClose = true;
     771           0 :     CloseChannel();
     772           0 :     element->DownloadSuspended();
     773             :   }
     774             : 
     775           0 :   if (mSuspendAgent.Suspend()) {
     776           0 :     if (mChannel) {
     777             :       {
     778           0 :         MutexAutoLock lock(mLock);
     779           0 :         mChannelStatistics.Stop();
     780             :       }
     781           0 :       element->DownloadSuspended();
     782             :     }
     783             :   }
     784             : }
     785             : 
     786           0 : void ChannelMediaResource::Resume()
     787             : {
     788           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     789             : 
     790           0 :   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     791           0 :   if (!owner) {
     792             :     // Shutting down; do nothing.
     793           0 :     return;
     794             :   }
     795           0 :   dom::HTMLMediaElement* element = owner->GetMediaElement();
     796           0 :   if (!element) {
     797             :     // Shutting down; do nothing.
     798           0 :     return;
     799             :   }
     800             : 
     801           0 :   if (mSuspendAgent.Resume()) {
     802           0 :     if (mChannel) {
     803             :       // Just wake up our existing channel
     804             :       {
     805           0 :         MutexAutoLock lock(mLock);
     806           0 :         mChannelStatistics.Start();
     807             :       }
     808             :       // if an error occurs after Resume, assume it's because the server
     809             :       // timed out the connection and we should reopen it.
     810           0 :       mReopenOnError = true;
     811           0 :       element->DownloadResumed();
     812             :     } else {
     813           0 :       int64_t totalLength = mCacheStream.GetLength();
     814             :       // If mOffset is at the end of the stream, then we shouldn't try to
     815             :       // seek to it. The seek will fail and be wasted anyway. We can leave
     816             :       // the channel dead; if the media cache wants to read some other data
     817             :       // in the future, it will call CacheClientSeek itself which will reopen the
     818             :       // channel.
     819           0 :       if (totalLength < 0 || mOffset < totalLength) {
     820             :         // There is (or may be) data to read at mOffset, so start reading it.
     821             :         // Need to recreate the channel.
     822           0 :         CacheClientSeek(mOffset, false);
     823           0 :         element->DownloadResumed();
     824             :       } else {
     825             :         // The channel remains dead. Do not notify DownloadResumed() which
     826             :         // will leave the media element in NETWORK_LOADING state.
     827             :       }
     828             :     }
     829             :   }
     830             : }
     831             : 
     832             : nsresult
     833           0 : ChannelMediaResource::RecreateChannel()
     834             : {
     835             :   nsLoadFlags loadFlags =
     836             :     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
     837           0 :     nsIChannel::LOAD_CLASSIFY_URI |
     838           0 :     (mLoadInBackground ? nsIRequest::LOAD_BACKGROUND : 0);
     839             : 
     840           0 :   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
     841           0 :   if (!owner) {
     842             :     // The decoder is being shut down, so don't bother opening a new channel
     843           0 :     return NS_OK;
     844             :   }
     845           0 :   dom::HTMLMediaElement* element = owner->GetMediaElement();
     846           0 :   if (!element) {
     847             :     // The decoder is being shut down, so don't bother opening a new channel
     848           0 :     return NS_OK;
     849             :   }
     850           0 :   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
     851           0 :   NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
     852             : 
     853           0 :   nsSecurityFlags securityFlags = element->ShouldCheckAllowOrigin()
     854           0 :                                   ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
     855           0 :                                   : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     856             : 
     857           0 :   MOZ_ASSERT(element->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
     858           0 :   nsContentPolicyType contentPolicyType = element->IsHTMLElement(nsGkAtoms::audio) ?
     859           0 :     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
     860             : 
     861           0 :   nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
     862             :                               mURI,
     863             :                               element,
     864             :                               securityFlags,
     865             :                               contentPolicyType,
     866             :                               loadGroup,
     867             :                               nullptr,  // aCallbacks
     868           0 :                               loadFlags);
     869           0 :   NS_ENSURE_SUCCESS(rv, rv);
     870             : 
     871           0 :   mSuspendAgent.NotifyChannelOpened(mChannel);
     872             : 
     873             :   // Tell the cache to reset the download status when the channel is reopened.
     874           0 :   mCacheStream.NotifyChannelRecreated();
     875             : 
     876           0 :   return rv;
     877             : }
     878             : 
     879             : void
     880           0 : ChannelMediaResource::DoNotifyDataReceived()
     881             : {
     882           0 :   mDataReceivedEvent.Revoke();
     883           0 :   mCallback->NotifyDataArrived();
     884           0 : }
     885             : 
     886             : void
     887           0 : ChannelMediaResource::CacheClientNotifyDataReceived()
     888             : {
     889           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     890             :   // NOTE: this can be called with the media cache lock held, so don't
     891             :   // block or do anything which might try to acquire a lock!
     892             : 
     893           0 :   if (mDataReceivedEvent.IsPending())
     894           0 :     return;
     895             : 
     896             :   mDataReceivedEvent =
     897           0 :     NewNonOwningRunnableMethod("ChannelMediaResource::DoNotifyDataReceived",
     898           0 :                                this, &ChannelMediaResource::DoNotifyDataReceived);
     899             : 
     900           0 :   nsCOMPtr<nsIRunnable> event = mDataReceivedEvent.get();
     901             : 
     902           0 :   SystemGroup::AbstractMainThreadFor(TaskCategory::Other)->Dispatch(event.forget());
     903             : }
     904             : 
     905             : void
     906           0 : ChannelMediaResource::CacheClientNotifyDataEnded(nsresult aStatus)
     907             : {
     908           0 :   MOZ_ASSERT(NS_IsMainThread());
     909           0 :   mCallback->NotifyDataEnded(aStatus);
     910           0 : }
     911             : 
     912             : void
     913           0 : ChannelMediaResource::CacheClientNotifyPrincipalChanged()
     914             : {
     915           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     916             : 
     917           0 :   mCallback->NotifyPrincipalChanged();
     918           0 : }
     919             : 
     920             : void
     921           0 : ChannelMediaResource::CacheClientNotifySuspendedStatusChanged()
     922             : {
     923           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     924           0 :   mCallback->NotifySuspendedStatusChanged();
     925           0 : }
     926             : 
     927             : nsresult
     928           0 : ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
     929             : {
     930           0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     931             : 
     932           0 :   LOG("CacheClientSeek requested for aOffset [%" PRId64 "] for decoder [%p]",
     933             :       aOffset, mCallback.get());
     934             : 
     935           0 :   CloseChannel();
     936             : 
     937           0 :   mOffset = aOffset;
     938             : 
     939             :   // Don't report close of the channel because the channel is not closed for
     940             :   // download ended, but for internal changes in the read position.
     941           0 :   mIgnoreClose = true;
     942             : 
     943           0 :   if (aResume) {
     944           0 :     mSuspendAgent.Resume();
     945             :   }
     946             : 
     947             :   // Don't create a new channel if we are still suspended. The channel will
     948             :   // be recreated when we are resumed.
     949           0 :   if (mSuspendAgent.IsSuspended()) {
     950           0 :     return NS_OK;
     951             :   }
     952             : 
     953           0 :   nsresult rv = RecreateChannel();
     954           0 :   NS_ENSURE_SUCCESS(rv, rv);
     955             : 
     956           0 :   return OpenChannel(nullptr);
     957             : }
     958             : 
     959             : nsresult
     960           0 : ChannelMediaResource::CacheClientSuspend()
     961             : {
     962           0 :   Suspend(false);
     963           0 :   return NS_OK;
     964             : }
     965             : 
     966             : nsresult
     967           0 : ChannelMediaResource::CacheClientResume()
     968             : {
     969           0 :   Resume();
     970           0 :   return NS_OK;
     971             : }
     972             : 
     973             : int64_t
     974           0 : ChannelMediaResource::GetNextCachedData(int64_t aOffset)
     975             : {
     976           0 :   return mCacheStream.GetNextCachedData(aOffset);
     977             : }
     978             : 
     979             : int64_t
     980           0 : ChannelMediaResource::GetCachedDataEnd(int64_t aOffset)
     981             : {
     982           0 :   return mCacheStream.GetCachedDataEnd(aOffset);
     983             : }
     984             : 
     985             : bool
     986           0 : ChannelMediaResource::IsDataCachedToEndOfResource(int64_t aOffset)
     987             : {
     988           0 :   return mCacheStream.IsDataCachedToEndOfStream(aOffset);
     989             : }
     990             : 
     991             : void
     992           0 : ChannelMediaResource::EnsureCacheUpToDate()
     993             : {
     994           0 :   mCacheStream.EnsureCacheUpdate();
     995           0 : }
     996             : 
     997             : bool
     998           0 : ChannelMediaResource::IsSuspendedByCache()
     999             : {
    1000           0 :   return mCacheStream.AreAllStreamsForResourceSuspended();
    1001             : }
    1002             : 
    1003             : bool
    1004           0 : ChannelMediaResource::IsSuspended()
    1005             : {
    1006           0 :   return mSuspendAgent.IsSuspended();
    1007             : }
    1008             : 
    1009             : void
    1010           0 : ChannelMediaResource::SetReadMode(MediaCacheStream::ReadMode aMode)
    1011             : {
    1012           0 :   mCacheStream.SetReadMode(aMode);
    1013           0 : }
    1014             : 
    1015             : void
    1016           0 : ChannelMediaResource::SetPlaybackRate(uint32_t aBytesPerSecond)
    1017             : {
    1018           0 :   mCacheStream.SetPlaybackRate(aBytesPerSecond);
    1019           0 : }
    1020             : 
    1021             : void
    1022           0 : ChannelMediaResource::Pin()
    1023             : {
    1024           0 :   mCacheStream.Pin();
    1025           0 : }
    1026             : 
    1027             : void
    1028           0 : ChannelMediaResource::Unpin()
    1029             : {
    1030           0 :   mCacheStream.Unpin();
    1031           0 : }
    1032             : 
    1033             : double
    1034           0 : ChannelMediaResource::GetDownloadRate(bool* aIsReliable)
    1035             : {
    1036           0 :   MutexAutoLock lock(mLock);
    1037           0 :   return mChannelStatistics.GetRate(aIsReliable);
    1038             : }
    1039             : 
    1040             : int64_t
    1041           0 : ChannelMediaResource::GetLength()
    1042             : {
    1043           0 :   return mCacheStream.GetLength();
    1044             : }
    1045             : 
    1046             : // ChannelSuspendAgent
    1047             : 
    1048             : bool
    1049           0 : ChannelSuspendAgent::Suspend()
    1050             : {
    1051           0 :   SuspendInternal();
    1052           0 :   return (++mSuspendCount == 1);
    1053             : }
    1054             : 
    1055             : void
    1056           0 : ChannelSuspendAgent::SuspendInternal()
    1057             : {
    1058           0 :   if (mChannel) {
    1059           0 :     bool isPending = false;
    1060           0 :     nsresult rv = mChannel->IsPending(&isPending);
    1061           0 :     if (NS_SUCCEEDED(rv) && isPending && !mIsChannelSuspended) {
    1062           0 :       mChannel->Suspend();
    1063           0 :       mIsChannelSuspended = true;
    1064             :     }
    1065             :   }
    1066           0 : }
    1067             : 
    1068             : bool
    1069           0 : ChannelSuspendAgent::Resume()
    1070             : {
    1071           0 :   MOZ_ASSERT(IsSuspended(), "Resume without suspend!");
    1072           0 :   --mSuspendCount;
    1073             : 
    1074           0 :   if (mSuspendCount == 0) {
    1075           0 :     if (mChannel && mIsChannelSuspended) {
    1076           0 :       mChannel->Resume();
    1077           0 :       mIsChannelSuspended = false;
    1078             :     }
    1079           0 :     return true;
    1080             :   }
    1081           0 :   return false;
    1082             : }
    1083             : 
    1084             : void
    1085           0 : ChannelSuspendAgent::UpdateSuspendedStatusIfNeeded()
    1086             : {
    1087           0 :   if (!mIsChannelSuspended && IsSuspended()) {
    1088           0 :     SuspendInternal();
    1089             :   }
    1090           0 : }
    1091             : 
    1092             : void
    1093           0 : ChannelSuspendAgent::NotifyChannelOpened(nsIChannel* aChannel)
    1094             : {
    1095           0 :   MOZ_ASSERT(aChannel);
    1096           0 :   mChannel = aChannel;
    1097           0 : }
    1098             : 
    1099             : void
    1100           0 : ChannelSuspendAgent::NotifyChannelClosing()
    1101             : {
    1102           0 :   MOZ_ASSERT(mChannel);
    1103             :   // Before close the channel, it need to be resumed to make sure its internal
    1104             :   // state is correct. Besides, We need to suspend the channel after recreating.
    1105           0 :   if (mIsChannelSuspended) {
    1106           0 :     mChannel->Resume();
    1107           0 :     mIsChannelSuspended = false;
    1108             :   }
    1109           0 :   mChannel = nullptr;
    1110           0 : }
    1111             : 
    1112             : bool
    1113           0 : ChannelSuspendAgent::IsSuspended()
    1114             : {
    1115           0 :   return (mSuspendCount > 0);
    1116             : }
    1117             : 
    1118             : // FileMediaResource
    1119             : 
    1120             : class FileMediaResource : public BaseMediaResource
    1121             : {
    1122             : public:
    1123           0 :   FileMediaResource(MediaResourceCallback* aCallback,
    1124             :                     nsIChannel* aChannel,
    1125             :                     nsIURI* aURI)
    1126           0 :     : BaseMediaResource(aCallback, aChannel, aURI)
    1127             :     , mSize(-1)
    1128             :     , mLock("FileMediaResource.mLock")
    1129           0 :     , mSizeInitialized(false)
    1130             :   {
    1131           0 :   }
    1132           0 :   ~FileMediaResource()
    1133           0 :   {
    1134           0 :   }
    1135             : 
    1136             :   // Main thread
    1137             :   nsresult Open(nsIStreamListener** aStreamListener) override;
    1138             :   nsresult Close() override;
    1139           0 :   void     Suspend(bool aCloseImmediately) override {}
    1140           0 :   void     Resume() override {}
    1141             :   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
    1142             :   nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
    1143             : 
    1144             :   // These methods are called off the main thread.
    1145             : 
    1146             :   // Other thread
    1147           0 :   void     SetReadMode(MediaCacheStream::ReadMode aMode) override {}
    1148           0 :   void     SetPlaybackRate(uint32_t aBytesPerSecond) override {}
    1149             :   nsresult ReadAt(int64_t aOffset, char* aBuffer,
    1150             :                   uint32_t aCount, uint32_t* aBytes) override;
    1151             :   // (Probably) file-based, caching recommended.
    1152           0 :   bool ShouldCacheReads() override { return true; }
    1153             :   int64_t  Tell() override;
    1154             : 
    1155             :   // Any thread
    1156           0 :   void    Pin() override {}
    1157           0 :   void    Unpin() override {}
    1158           0 :   double  GetDownloadRate(bool* aIsReliable) override
    1159             :   {
    1160             :     // The data's all already here
    1161           0 :     *aIsReliable = true;
    1162           0 :     return 100*1024*1024; // arbitray, use 100MB/s
    1163             :   }
    1164           0 :   int64_t GetLength() override {
    1165           0 :     MutexAutoLock lock(mLock);
    1166             : 
    1167           0 :     EnsureSizeInitialized();
    1168           0 :     return mSizeInitialized ? mSize : 0;
    1169             :   }
    1170           0 :   int64_t GetNextCachedData(int64_t aOffset) override
    1171             :   {
    1172           0 :     MutexAutoLock lock(mLock);
    1173             : 
    1174           0 :     EnsureSizeInitialized();
    1175           0 :     return (aOffset < mSize) ? aOffset : -1;
    1176             :   }
    1177           0 :   int64_t GetCachedDataEnd(int64_t aOffset) override {
    1178           0 :     MutexAutoLock lock(mLock);
    1179             : 
    1180           0 :     EnsureSizeInitialized();
    1181           0 :     return std::max(aOffset, mSize);
    1182             :   }
    1183           0 :   bool    IsDataCachedToEndOfResource(int64_t aOffset) override { return true; }
    1184           0 :   bool    IsSuspendedByCache() override { return true; }
    1185           0 :   bool    IsSuspended() override { return true; }
    1186           0 :   bool    IsTransportSeekable() override { return true; }
    1187             : 
    1188             :   nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
    1189             : 
    1190           0 :   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
    1191             :   {
    1192             :     // Might be useful to track in the future:
    1193             :     // - mInput
    1194           0 :     return BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
    1195             :   }
    1196             : 
    1197           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
    1198             :   {
    1199           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    1200             :   }
    1201             : 
    1202             : protected:
    1203             :   // These Unsafe variants of Read and Seek perform their operations
    1204             :   // without acquiring mLock. The caller must obtain the lock before
    1205             :   // calling. The implmentation of Read, Seek and ReadAt obtains the
    1206             :   // lock before calling these Unsafe variants to read or seek.
    1207             :   nsresult UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
    1208             :   nsresult UnsafeSeek(int32_t aWhence, int64_t aOffset);
    1209             : private:
    1210             :   // Ensures mSize is initialized, if it can be.
    1211             :   // mLock must be held when this is called, and mInput must be non-null.
    1212             :   void EnsureSizeInitialized();
    1213             :   already_AddRefed<MediaByteBuffer> UnsafeMediaReadAt(
    1214             :                         int64_t aOffset, uint32_t aCount);
    1215             : 
    1216             :   // The file size, or -1 if not known. Immutable after Open().
    1217             :   // Can be used from any thread.
    1218             :   int64_t mSize;
    1219             : 
    1220             :   // This lock handles synchronisation between calls to Close() and
    1221             :   // the Read, Seek, etc calls. Close must not be called while a
    1222             :   // Read or Seek is in progress since it resets various internal
    1223             :   // values to null.
    1224             :   // This lock protects mSeekable, mInput, mSize, and mSizeInitialized.
    1225             :   Mutex mLock;
    1226             : 
    1227             :   // Seekable stream interface to file. This can be used from any
    1228             :   // thread.
    1229             :   nsCOMPtr<nsISeekableStream> mSeekable;
    1230             : 
    1231             :   // Input stream for the media data. This can be used from any
    1232             :   // thread.
    1233             :   nsCOMPtr<nsIInputStream>  mInput;
    1234             : 
    1235             :   // Whether we've attempted to initialize mSize. Note that mSize can be -1
    1236             :   // when mSizeInitialized is true if we tried and failed to get the size
    1237             :   // of the file.
    1238             :   bool mSizeInitialized;
    1239             : };
    1240             : 
    1241           0 : void FileMediaResource::EnsureSizeInitialized()
    1242             : {
    1243           0 :   mLock.AssertCurrentThreadOwns();
    1244           0 :   NS_ASSERTION(mInput, "Must have file input stream");
    1245           0 :   if (mSizeInitialized) {
    1246           0 :     return;
    1247             :   }
    1248           0 :   mSizeInitialized = true;
    1249             :   // Get the file size and inform the decoder.
    1250             :   uint64_t size;
    1251           0 :   nsresult res = mInput->Available(&size);
    1252           0 :   if (NS_SUCCEEDED(res) && size <= INT64_MAX) {
    1253           0 :     mSize = (int64_t)size;
    1254           0 :     mCallback->NotifyDataEnded(NS_OK);
    1255             :   }
    1256             : }
    1257             : 
    1258           0 : nsresult FileMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges)
    1259             : {
    1260           0 :   MutexAutoLock lock(mLock);
    1261             : 
    1262           0 :   EnsureSizeInitialized();
    1263           0 :   if (mSize == -1) {
    1264           0 :     return NS_ERROR_FAILURE;
    1265             :   }
    1266           0 :   aRanges += MediaByteRange(0, mSize);
    1267           0 :   return NS_OK;
    1268             : }
    1269             : 
    1270           0 : nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
    1271             : {
    1272           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1273           0 :   MOZ_ASSERT(aStreamListener);
    1274             : 
    1275           0 :   *aStreamListener = nullptr;
    1276           0 :   nsresult rv = NS_OK;
    1277             : 
    1278             :   // The channel is already open. We need a synchronous stream that
    1279             :   // implements nsISeekableStream, so we have to find the underlying
    1280             :   // file and reopen it
    1281           0 :   nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
    1282           0 :   if (fc) {
    1283           0 :     nsCOMPtr<nsIFile> file;
    1284           0 :     rv = fc->GetFile(getter_AddRefs(file));
    1285           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1286             : 
    1287           0 :     rv = NS_NewLocalFileInputStream(
    1288           0 :       getter_AddRefs(mInput), file, -1, -1, nsIFileInputStream::SHARE_DELETE);
    1289           0 :   } else if (IsBlobURI(mURI)) {
    1290           0 :     rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput));
    1291             :   }
    1292             : 
    1293           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1294             : 
    1295           0 :   mSeekable = do_QueryInterface(mInput);
    1296           0 :   if (!mSeekable) {
    1297             :     // XXX The file may just be a .url or similar
    1298             :     // shortcut that points to a Web site. We need to fix this by
    1299             :     // doing an async open and waiting until we locate the real resource,
    1300             :     // then using that (if it's still a file!).
    1301           0 :     return NS_ERROR_FAILURE;
    1302             :   }
    1303             : 
    1304           0 :   return NS_OK;
    1305             : }
    1306             : 
    1307           0 : nsresult FileMediaResource::Close()
    1308             : {
    1309           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1310             : 
    1311             :   // Since mChennel is only accessed by main thread, there is no necessary to
    1312             :   // take the lock.
    1313           0 :   if (mChannel) {
    1314           0 :     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
    1315           0 :     mChannel = nullptr;
    1316             :   }
    1317             : 
    1318           0 :   return NS_OK;
    1319             : }
    1320             : 
    1321           0 : already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal()
    1322             : {
    1323           0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1324             : 
    1325           0 :   nsCOMPtr<nsIPrincipal> principal;
    1326           0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    1327           0 :   if (!secMan || !mChannel)
    1328           0 :     return nullptr;
    1329           0 :   secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
    1330           0 :   return principal.forget();
    1331             : }
    1332             : 
    1333           0 : nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
    1334             : {
    1335           0 :   MutexAutoLock lock(mLock);
    1336             : 
    1337           0 :   EnsureSizeInitialized();
    1338           0 :   if (!aCount) {
    1339           0 :     return NS_OK;
    1340             :   }
    1341           0 :   int64_t offset = 0;
    1342           0 :   nsresult res = mSeekable->Tell(&offset);
    1343           0 :   NS_ENSURE_SUCCESS(res,res);
    1344           0 :   res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
    1345           0 :   NS_ENSURE_SUCCESS(res,res);
    1346           0 :   uint32_t bytesRead = 0;
    1347           0 :   do {
    1348           0 :     uint32_t x = 0;
    1349           0 :     uint32_t bytesToRead = aCount - bytesRead;
    1350           0 :     res = mInput->Read(aBuffer, bytesToRead, &x);
    1351           0 :     bytesRead += x;
    1352           0 :     if (!x) {
    1353           0 :       res = NS_ERROR_FAILURE;
    1354             :     }
    1355           0 :   } while (bytesRead != aCount && res == NS_OK);
    1356             : 
    1357             :   // Reset read head to original position so we don't disturb any other
    1358             :   // reading thread.
    1359           0 :   nsresult seekres = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1360             : 
    1361             :   // If a read failed in the loop above, we want to return its failure code.
    1362           0 :   NS_ENSURE_SUCCESS(res,res);
    1363             : 
    1364             :   // Else we succeed if the reset-seek succeeds.
    1365           0 :   return seekres;
    1366             : }
    1367             : 
    1368           0 : nsresult FileMediaResource::UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
    1369             : {
    1370           0 :   EnsureSizeInitialized();
    1371           0 :   return mInput->Read(aBuffer, aCount, aBytes);
    1372             : }
    1373             : 
    1374           0 : nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer,
    1375             :                                    uint32_t aCount, uint32_t* aBytes)
    1376             : {
    1377           0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
    1378             : 
    1379             :   nsresult rv;
    1380             :   {
    1381           0 :     MutexAutoLock lock(mLock);
    1382           0 :     rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset);
    1383           0 :     if (NS_FAILED(rv)) return rv;
    1384           0 :     rv = UnsafeRead(aBuffer, aCount, aBytes);
    1385             :   }
    1386           0 :   if (NS_SUCCEEDED(rv)) {
    1387           0 :     DispatchBytesConsumed(*aBytes, aOffset);
    1388             :   }
    1389           0 :   return rv;
    1390             : }
    1391             : 
    1392             : already_AddRefed<MediaByteBuffer>
    1393           0 : FileMediaResource::UnsafeMediaReadAt(int64_t aOffset, uint32_t aCount)
    1394             : {
    1395           0 :   RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
    1396           0 :   bool ok = bytes->SetLength(aCount, fallible);
    1397           0 :   NS_ENSURE_TRUE(ok, nullptr);
    1398           0 :   nsresult rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset);
    1399           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1400           0 :   char* curr = reinterpret_cast<char*>(bytes->Elements());
    1401           0 :   const char* start = curr;
    1402           0 :   while (aCount > 0) {
    1403             :     uint32_t bytesRead;
    1404           0 :     rv = UnsafeRead(curr, aCount, &bytesRead);
    1405           0 :     NS_ENSURE_SUCCESS(rv, nullptr);
    1406           0 :     if (!bytesRead) {
    1407           0 :       break;
    1408             :     }
    1409           0 :     aCount -= bytesRead;
    1410           0 :     curr += bytesRead;
    1411             :   }
    1412           0 :   bytes->SetLength(curr - start);
    1413           0 :   return bytes.forget();
    1414             : }
    1415             : 
    1416           0 : nsresult FileMediaResource::UnsafeSeek(int32_t aWhence, int64_t aOffset)
    1417             : {
    1418           0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
    1419             : 
    1420           0 :   if (!mSeekable)
    1421           0 :     return NS_ERROR_FAILURE;
    1422           0 :   EnsureSizeInitialized();
    1423           0 :   return mSeekable->Seek(aWhence, aOffset);
    1424             : }
    1425             : 
    1426           0 : int64_t FileMediaResource::Tell()
    1427             : {
    1428           0 :   MutexAutoLock lock(mLock);
    1429           0 :   EnsureSizeInitialized();
    1430             : 
    1431           0 :   int64_t offset = 0;
    1432             :   // Return mSize as offset (end of stream) in case of error
    1433           0 :   if (!mSeekable || NS_FAILED(mSeekable->Tell(&offset)))
    1434           0 :     return mSize;
    1435           0 :   return offset;
    1436             : }
    1437             : 
    1438             : already_AddRefed<MediaResource>
    1439           0 : MediaResource::Create(MediaResourceCallback* aCallback,
    1440             :                       nsIChannel* aChannel, bool aIsPrivateBrowsing)
    1441             : {
    1442           0 :   NS_ASSERTION(NS_IsMainThread(),
    1443             :                "MediaResource::Open called on non-main thread");
    1444             : 
    1445             :   // If the channel was redirected, we want the post-redirect URI;
    1446             :   // but if the URI scheme was expanded, say from chrome: to jar:file:,
    1447             :   // we want the original URI.
    1448           0 :   nsCOMPtr<nsIURI> uri;
    1449           0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
    1450           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1451             : 
    1452           0 :   nsAutoCString contentTypeString;
    1453           0 :   aChannel->GetContentType(contentTypeString);
    1454           0 :   Maybe<MediaContainerType> containerType = MakeMediaContainerType(contentTypeString);
    1455           0 :   if (!containerType) {
    1456           0 :     return nullptr;
    1457             :   }
    1458             : 
    1459           0 :   RefPtr<MediaResource> resource;
    1460             : 
    1461             :   // Let's try to create a FileMediaResource in case the channel is a nsIFile
    1462           0 :   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
    1463           0 :   if (fc) {
    1464           0 :     resource = new FileMediaResource(aCallback, aChannel, uri);
    1465             :   }
    1466             : 
    1467             :   // If the URL is blobURL with a seekable inputStream, we can still use a
    1468             :   // FileMediaResource. This basically means that the blobURL and its Blob have
    1469             :   // been created in the current process.
    1470             : 
    1471           0 :   if (!resource) {
    1472           0 :     nsCOMPtr<nsIInputStream> stream;
    1473           0 :     nsCOMPtr<nsISeekableStream> seekableStream;
    1474           0 :     if (IsBlobURI(uri) &&
    1475           0 :         NS_SUCCEEDED(NS_GetStreamForBlobURI(uri, getter_AddRefs(stream))) &&
    1476           0 :         (seekableStream = do_QueryInterface(stream))) {
    1477           0 :       resource = new FileMediaResource(aCallback, aChannel, uri);
    1478             :     }
    1479             :   }
    1480             : 
    1481           0 :   if (!resource) {
    1482             :     resource =
    1483           0 :       new ChannelMediaResource(aCallback, aChannel, uri, aIsPrivateBrowsing);
    1484             :   }
    1485             : 
    1486           0 :   return resource.forget();
    1487             : }
    1488             : 
    1489           0 : void BaseMediaResource::SetLoadInBackground(bool aLoadInBackground) {
    1490           0 :   if (aLoadInBackground == mLoadInBackground) {
    1491           0 :     return;
    1492             :   }
    1493           0 :   mLoadInBackground = aLoadInBackground;
    1494           0 :   if (!mChannel) {
    1495             :     // No channel, resource is probably already loaded.
    1496           0 :     return;
    1497             :   }
    1498             : 
    1499           0 :   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
    1500           0 :   if (!owner) {
    1501           0 :     NS_WARNING("Null owner in MediaResource::SetLoadInBackground()");
    1502           0 :     return;
    1503             :   }
    1504           0 :   dom::HTMLMediaElement* element = owner->GetMediaElement();
    1505           0 :   if (!element) {
    1506           0 :     NS_WARNING("Null element in MediaResource::SetLoadInBackground()");
    1507           0 :     return;
    1508             :   }
    1509             : 
    1510           0 :   bool isPending = false;
    1511           0 :   if (NS_SUCCEEDED(mChannel->IsPending(&isPending)) &&
    1512             :       isPending) {
    1513             :     nsLoadFlags loadFlags;
    1514           0 :     DebugOnly<nsresult> rv = mChannel->GetLoadFlags(&loadFlags);
    1515           0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");
    1516             : 
    1517           0 :     if (aLoadInBackground) {
    1518           0 :       loadFlags |= nsIRequest::LOAD_BACKGROUND;
    1519             :     } else {
    1520           0 :       loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
    1521             :     }
    1522           0 :     ModifyLoadFlags(loadFlags);
    1523             :   }
    1524             : }
    1525             : 
    1526           0 : void BaseMediaResource::ModifyLoadFlags(nsLoadFlags aFlags)
    1527             : {
    1528           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1529           0 :   nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1530           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv), "GetLoadGroup() failed!");
    1531             : 
    1532             :   nsresult status;
    1533           0 :   mChannel->GetStatus(&status);
    1534             : 
    1535           0 :   bool inLoadGroup = false;
    1536           0 :   if (loadGroup) {
    1537           0 :     rv = loadGroup->RemoveRequest(mChannel, nullptr, status);
    1538           0 :     if (NS_SUCCEEDED(rv)) {
    1539           0 :       inLoadGroup = true;
    1540             :     }
    1541             :   }
    1542             : 
    1543           0 :   rv = mChannel->SetLoadFlags(aFlags);
    1544           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv), "SetLoadFlags() failed!");
    1545             : 
    1546           0 :   if (inLoadGroup) {
    1547           0 :     rv = loadGroup->AddRequest(mChannel, nullptr);
    1548           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv), "AddRequest() failed!");
    1549             :   }
    1550           0 : }
    1551             : 
    1552           0 : void BaseMediaResource::DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset)
    1553             : {
    1554           0 :   if (aNumBytes <= 0) {
    1555           0 :     return;
    1556             :   }
    1557           0 :   mCallback->NotifyBytesConsumed(aNumBytes, aOffset);
    1558             : }
    1559             : 
    1560             : nsresult
    1561           0 : MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
    1562             : {
    1563           0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
    1564             : 
    1565             :   // We purposefuly don't check that we may attempt to read past
    1566             :   // mResource->GetLength() as the resource's length may change over time.
    1567             : 
    1568           0 :   nsresult rv = ReadAt(mOffset, aBuffer, aCount, aBytes);
    1569           0 :   if (NS_FAILED(rv)) {
    1570           0 :     return rv;
    1571             :   }
    1572           0 :   mOffset += *aBytes;
    1573           0 :   return NS_OK;
    1574             : }
    1575             : 
    1576             : static nsCString
    1577           0 : ResultName(nsresult aResult)
    1578             : {
    1579           0 :   nsCString name;
    1580           0 :   GetErrorName(aResult, name);
    1581           0 :   return name;
    1582             : }
    1583             : 
    1584             : nsresult
    1585           0 : MediaResourceIndex::ReadAt(int64_t aOffset,
    1586             :                            char* aBuffer,
    1587             :                            uint32_t aCount,
    1588             :                            uint32_t* aBytes)
    1589             : {
    1590           0 :   if (mCacheBlockSize == 0) {
    1591           0 :     return UncachedReadAt(aOffset, aBuffer, aCount, aBytes);
    1592             :   }
    1593             : 
    1594           0 :   *aBytes = 0;
    1595             : 
    1596           0 :   if (aCount == 0) {
    1597           0 :     return NS_OK;
    1598             :   }
    1599             : 
    1600           0 :   const int64_t endOffset = aOffset + aCount;
    1601           0 :   const int64_t lastBlockOffset = CacheOffsetContaining(endOffset - 1);
    1602             : 
    1603           0 :   if (mCachedBytes != 0 && mCachedOffset + mCachedBytes >= aOffset &&
    1604           0 :       mCachedOffset < endOffset) {
    1605             :     // There is data in the cache that is not completely before aOffset and not
    1606             :     // completely after endOffset, so it could be usable (with potential top-up).
    1607           0 :     if (aOffset < mCachedOffset) {
    1608             :       // We need to read before the cached data.
    1609           0 :       const uint32_t toRead = uint32_t(mCachedOffset - aOffset);
    1610           0 :       MOZ_ASSERT(toRead > 0);
    1611           0 :       MOZ_ASSERT(toRead < aCount);
    1612           0 :       uint32_t read = 0;
    1613           0 :       nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read);
    1614           0 :       if (NS_FAILED(rv)) {
    1615           0 :         ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1616             :              ") uncached read before cache -> %s, %" PRIu32,
    1617             :              aCount,
    1618             :              aOffset,
    1619             :              ResultName(rv).get(),
    1620             :              *aBytes);
    1621           0 :         return rv;
    1622             :       }
    1623           0 :       *aBytes = read;
    1624           0 :       if (read < toRead) {
    1625             :         // Could not read everything we wanted, we're done.
    1626           0 :         ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1627             :              ") uncached read before cache, incomplete -> OK, %" PRIu32,
    1628             :              aCount,
    1629             :              aOffset,
    1630             :              *aBytes);
    1631           0 :         return NS_OK;
    1632             :       }
    1633           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1634             :            ") uncached read before cache: %" PRIu32 ", remaining: %" PRIu32
    1635             :            "@%" PRId64 "...",
    1636             :            aCount,
    1637             :            aOffset,
    1638             :            read,
    1639             :            aCount - read,
    1640             :            aOffset + read);
    1641           0 :       aOffset += read;
    1642           0 :       aBuffer += read;
    1643           0 :       aCount -= read;
    1644             :       // We should have reached the cache.
    1645           0 :       MOZ_ASSERT(aOffset == mCachedOffset);
    1646             :     }
    1647           0 :     MOZ_ASSERT(aOffset >= mCachedOffset);
    1648             : 
    1649             :     // We've reached our cache.
    1650             :     const uint32_t toCopy =
    1651           0 :       std::min(aCount, uint32_t(mCachedOffset + mCachedBytes - aOffset));
    1652             :     // Note that we could in fact be just after the last byte of the cache, in
    1653             :     // which case we can't actually read from it! (But we will top-up next.)
    1654           0 :     if (toCopy != 0) {
    1655           0 :       memcpy(aBuffer, &mCachedBlock[IndexInCache(aOffset)], toCopy);
    1656           0 :       *aBytes += toCopy;
    1657           0 :       aCount -= toCopy;
    1658           0 :       if (aCount == 0) {
    1659             :         // All done!
    1660           0 :         ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied everything (%" PRIu32
    1661             :              ") from cache(%" PRIu32 "@%" PRId64 ") :-D -> OK, %" PRIu32,
    1662             :              aCount,
    1663             :              aOffset,
    1664             :              toCopy,
    1665             :              mCachedBytes,
    1666             :              mCachedOffset,
    1667             :              *aBytes);
    1668           0 :         return NS_OK;
    1669             :       }
    1670           0 :       aOffset += toCopy;
    1671           0 :       aBuffer += toCopy;
    1672           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied %" PRIu32
    1673             :            " from cache(%" PRIu32 "@%" PRId64 ") :-), remaining: %" PRIu32
    1674             :            "@%" PRId64 "...",
    1675             :            aCount + toCopy,
    1676             :            aOffset - toCopy,
    1677             :            toCopy,
    1678             :            mCachedBytes,
    1679             :            mCachedOffset,
    1680             :            aCount,
    1681             :            aOffset);
    1682             :     }
    1683             : 
    1684           0 :     if (aOffset - 1 >= lastBlockOffset) {
    1685             :       // We were already reading cached data from the last block, we need more
    1686             :       // from it -> try to top-up, read what we can, and we'll be done.
    1687           0 :       MOZ_ASSERT(aOffset == mCachedOffset + mCachedBytes);
    1688           0 :       MOZ_ASSERT(endOffset <= lastBlockOffset + mCacheBlockSize);
    1689           0 :       return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
    1690             :     }
    1691             : 
    1692             :     // We were not in the last block (but we may just have crossed the line now)
    1693           0 :     MOZ_ASSERT(aOffset <= lastBlockOffset);
    1694             :     // Continue below...
    1695           0 :   } else if (aOffset >= lastBlockOffset) {
    1696             :     // There was nothing we could get from the cache.
    1697             :     // But we're already in the last block -> Cache or read what we can.
    1698             :     // Make sure to invalidate the cache first.
    1699           0 :     mCachedBytes = 0;
    1700           0 :     return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
    1701             :   }
    1702             : 
    1703             :   // If we're here, either there was nothing usable in the cache, or we've just
    1704             :   // read what was in the cache but there's still more to read.
    1705             : 
    1706           0 :   if (aOffset < lastBlockOffset) {
    1707             :     // We need to read before the last block.
    1708             :     // Start with an uncached read up to the last block.
    1709           0 :     const uint32_t toRead = uint32_t(lastBlockOffset - aOffset);
    1710           0 :     MOZ_ASSERT(toRead > 0);
    1711           0 :     MOZ_ASSERT(toRead < aCount);
    1712           0 :     uint32_t read = 0;
    1713           0 :     nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read);
    1714           0 :     if (NS_FAILED(rv)) {
    1715           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1716             :            ") uncached read before last block failed -> %s, %" PRIu32,
    1717             :            aCount,
    1718             :            aOffset,
    1719             :            ResultName(rv).get(),
    1720             :            *aBytes);
    1721           0 :       return rv;
    1722             :     }
    1723           0 :     if (read == 0) {
    1724           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1725             :            ") uncached read 0 before last block -> OK, %" PRIu32,
    1726             :            aCount,
    1727             :            aOffset,
    1728             :            *aBytes);
    1729           0 :       return NS_OK;
    1730             :     }
    1731           0 :     *aBytes += read;
    1732           0 :     if (read < toRead) {
    1733             :       // Could not read everything we wanted, we're done.
    1734           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1735             :            ") uncached read before last block, incomplete -> OK, %" PRIu32,
    1736             :            aCount,
    1737             :            aOffset,
    1738             :            *aBytes);
    1739           0 :       return NS_OK;
    1740             :     }
    1741           0 :     ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") read %" PRIu32
    1742             :          " before last block, remaining: %" PRIu32 "@%" PRId64 "...",
    1743             :          aCount,
    1744             :          aOffset,
    1745             :          read,
    1746             :          aCount - read,
    1747             :          aOffset + read);
    1748           0 :     aOffset += read;
    1749           0 :     aBuffer += read;
    1750           0 :     aCount -= read;
    1751             :   }
    1752             : 
    1753             :   // We should just have reached the start of the last block.
    1754           0 :   MOZ_ASSERT(aOffset == lastBlockOffset);
    1755           0 :   MOZ_ASSERT(aCount <= mCacheBlockSize);
    1756             :   // Make sure to invalidate the cache first.
    1757           0 :   mCachedBytes = 0;
    1758           0 :   return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
    1759             : }
    1760             : 
    1761             : nsresult
    1762           0 : MediaResourceIndex::CacheOrReadAt(int64_t aOffset,
    1763             :                                   char* aBuffer,
    1764             :                                   uint32_t aCount,
    1765             :                                   uint32_t* aBytes)
    1766             : {
    1767             :   // We should be here because there is more data to read.
    1768           0 :   MOZ_ASSERT(aCount > 0);
    1769             :   // We should be in the last block, so we shouldn't try to read past it.
    1770           0 :   MOZ_ASSERT(IndexInCache(aOffset) + aCount <= mCacheBlockSize);
    1771             : 
    1772           0 :   const int64_t length = GetLength();
    1773             :   // If length is unknown (-1), look at resource-cached data.
    1774             :   // If length is known and equal or greater than requested, also look at
    1775             :   // resource-cached data.
    1776             :   // Otherwise, if length is known but same, or less than(!?), requested, don't
    1777             :   // attempt to access resource-cached data, as we're not expecting it to ever
    1778             :   // be greater than the length.
    1779           0 :   if (length < 0 || length >= aOffset + aCount) {
    1780             :     // Is there cached data covering at least the requested range?
    1781           0 :     const int64_t cachedDataEnd = mResource->GetCachedDataEnd(aOffset);
    1782           0 :     if (cachedDataEnd >= aOffset + aCount) {
    1783             :       // Try to read as much resource-cached data as can fill our local cache.
    1784             :       // Assume we can read as much as is cached without blocking.
    1785           0 :       const uint32_t cacheIndex = IndexInCache(aOffset);
    1786             :       const uint32_t toRead =
    1787           0 :         uint32_t(std::min(cachedDataEnd - aOffset,
    1788           0 :                           int64_t(mCacheBlockSize - cacheIndex)));
    1789           0 :       MOZ_ASSERT(toRead >= aCount);
    1790           0 :       uint32_t read = 0;
    1791             :       // We would like `toRead` if possible, but ok with at least `aCount`.
    1792           0 :       nsresult rv = UncachedRangedReadAt(
    1793           0 :         aOffset, &mCachedBlock[cacheIndex], aCount, toRead - aCount, &read);
    1794           0 :       if (NS_SUCCEEDED(rv)) {
    1795           0 :         if (read == 0) {
    1796           0 :           ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
    1797             :                "..%" PRIu32 "@%" PRId64
    1798             :                ") to top-up succeeded but read nothing -> OK anyway",
    1799             :                aCount,
    1800             :                aOffset,
    1801             :                aCount,
    1802             :                toRead,
    1803             :                aOffset);
    1804             :           // Couldn't actually read anything, but didn't error out, so count
    1805             :           // that as success.
    1806           0 :           return NS_OK;
    1807             :         }
    1808           0 :         if (mCachedOffset + mCachedBytes == aOffset) {
    1809             :           // We were topping-up the cache, just update its size.
    1810           0 :           ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
    1811             :                "..%" PRIu32 "@%" PRId64 ") to top-up succeeded to read %" PRIu32
    1812             :                "...",
    1813             :                aCount,
    1814             :                aOffset,
    1815             :                aCount,
    1816             :                toRead,
    1817             :                aOffset,
    1818             :                read);
    1819           0 :           mCachedBytes += read;
    1820             :         } else {
    1821             :           // We were filling the cache from scratch, save new cache information.
    1822           0 :           ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
    1823             :                "..%" PRIu32 "@%" PRId64
    1824             :                ") to fill cache succeeded to read %" PRIu32 "...",
    1825             :                aCount,
    1826             :                aOffset,
    1827             :                aCount,
    1828             :                toRead,
    1829             :                aOffset,
    1830             :                read);
    1831           0 :           mCachedOffset = aOffset;
    1832           0 :           mCachedBytes = read;
    1833             :         }
    1834             :         // Copy relevant part into output.
    1835           0 :         uint32_t toCopy = std::min(aCount, read);
    1836           0 :         memcpy(aBuffer, &mCachedBlock[cacheIndex], toCopy);
    1837           0 :         *aBytes += toCopy;
    1838           0 :         ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - copied %" PRIu32 "@%" PRId64
    1839             :              " -> OK, %" PRIu32,
    1840             :              aCount,
    1841             :              aOffset,
    1842             :              toCopy,
    1843             :              aOffset,
    1844             :              *aBytes);
    1845             :         // We may not have read all that was requested, but we got everything
    1846             :         // we could get, so we're done.
    1847           0 :         return NS_OK;
    1848             :       }
    1849           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
    1850             :            "..%" PRIu32 "@%" PRId64
    1851             :            ") failed: %s, will fallback to blocking read...",
    1852             :            aCount,
    1853             :            aOffset,
    1854             :            aCount,
    1855             :            toRead,
    1856             :            aOffset,
    1857             :            ResultName(rv).get());
    1858             :       // Failure during reading. Note that this may be due to the cache
    1859             :       // changing between `GetCachedDataEnd` and `ReadAt`, so it's not
    1860             :       // totally unexpected, just hopefully rare; but we do need to handle it.
    1861             : 
    1862             :       // Invalidate part of cache that may have been partially overridden.
    1863           0 :       if (mCachedOffset + mCachedBytes == aOffset) {
    1864             :         // We were topping-up the cache, just keep the old untouched data.
    1865             :         // (i.e., nothing to do here.)
    1866             :       } else {
    1867             :         // We were filling the cache from scratch, invalidate cache.
    1868           0 :         mCachedBytes = 0;
    1869             :       }
    1870             :     } else {
    1871           0 :       ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1872             :            ") - no cached data, will fallback to blocking read...",
    1873             :            aCount,
    1874             :            aOffset);
    1875           0 :     }
    1876             :   } else {
    1877           0 :     ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - length is %" PRId64
    1878             :          " (%s), will fallback to blocking read as the caller requested...",
    1879             :          aCount,
    1880             :          aOffset,
    1881             :          length,
    1882             :          length < 0 ? "unknown" : "too short!");
    1883             :   }
    1884           0 :   uint32_t read = 0;
    1885           0 :   nsresult rv = UncachedReadAt(aOffset, aBuffer, aCount, &read);
    1886           0 :   if (NS_SUCCEEDED(rv)) {
    1887           0 :     *aBytes += read;
    1888           0 :     ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - fallback uncached read got %" PRIu32
    1889             :          " bytes -> %s, %" PRIu32,
    1890             :          aCount,
    1891             :          aOffset,
    1892             :          read,
    1893             :          ResultName(rv).get(),
    1894             :          *aBytes);
    1895             :   } else {
    1896           0 :     ILOG("ReadAt(%" PRIu32 "@%" PRId64
    1897             :          ") - fallback uncached read failed -> %s, %" PRIu32,
    1898             :          aCount,
    1899             :          aOffset,
    1900             :          ResultName(rv).get(),
    1901             :          *aBytes);
    1902             :   }
    1903           0 :   return rv;
    1904             : }
    1905             : 
    1906             : nsresult
    1907           0 : MediaResourceIndex::UncachedReadAt(int64_t aOffset,
    1908             :                                    char* aBuffer,
    1909             :                                    uint32_t aCount,
    1910             :                                    uint32_t* aBytes) const
    1911             : {
    1912           0 :   *aBytes = 0;
    1913           0 :   if (aCount != 0) {
    1914             :     for (;;) {
    1915           0 :       uint32_t bytesRead = 0;
    1916           0 :       nsresult rv = mResource->ReadAt(aOffset, aBuffer, aCount, &bytesRead);
    1917           0 :       if (NS_FAILED(rv)) {
    1918           0 :         return rv;
    1919             :       }
    1920           0 :       if (bytesRead == 0) {
    1921           0 :         break;
    1922             :       }
    1923           0 :       *aBytes += bytesRead;
    1924           0 :       aCount -= bytesRead;
    1925           0 :       if (aCount == 0) {
    1926           0 :         break;
    1927             :       }
    1928           0 :       aOffset += bytesRead;
    1929           0 :       aBuffer += bytesRead;
    1930           0 :     }
    1931             :   }
    1932           0 :   return NS_OK;
    1933             : }
    1934             : 
    1935             : nsresult
    1936           0 : MediaResourceIndex::UncachedRangedReadAt(int64_t aOffset,
    1937             :                                          char* aBuffer,
    1938             :                                          uint32_t aRequestedCount,
    1939             :                                          uint32_t aExtraCount,
    1940             :                                          uint32_t* aBytes) const
    1941             : {
    1942           0 :   *aBytes = 0;
    1943           0 :   uint32_t count = aRequestedCount + aExtraCount;
    1944           0 :   if (count != 0) {
    1945             :     for (;;) {
    1946           0 :       uint32_t bytesRead = 0;
    1947           0 :       nsresult rv = mResource->ReadAt(aOffset, aBuffer, count, &bytesRead);
    1948           0 :       if (NS_FAILED(rv)) {
    1949           0 :         return rv;
    1950             :       }
    1951           0 :       if (bytesRead == 0) {
    1952           0 :         break;
    1953             :       }
    1954           0 :       *aBytes += bytesRead;
    1955           0 :       count -= bytesRead;
    1956           0 :       if (count <= aExtraCount) {
    1957             :         // We have read at least aRequestedCount, don't loop anymore.
    1958           0 :         break;
    1959             :       }
    1960           0 :       aOffset += bytesRead;
    1961           0 :       aBuffer += bytesRead;
    1962           0 :     }
    1963             :   }
    1964           0 :   return NS_OK;
    1965             : }
    1966             : 
    1967             : nsresult
    1968           0 : MediaResourceIndex::Seek(int32_t aWhence, int64_t aOffset)
    1969             : {
    1970           0 :   switch (aWhence) {
    1971             :     case SEEK_SET:
    1972           0 :       break;
    1973             :     case SEEK_CUR:
    1974           0 :       aOffset += mOffset;
    1975           0 :       break;
    1976             :     case SEEK_END:
    1977             :     {
    1978           0 :       int64_t length = mResource->GetLength();
    1979           0 :       if (length == -1 || length - aOffset < 0) {
    1980           0 :         return NS_ERROR_FAILURE;
    1981             :       }
    1982           0 :       aOffset = mResource->GetLength() - aOffset;
    1983             :     }
    1984           0 :       break;
    1985             :     default:
    1986           0 :       return NS_ERROR_FAILURE;
    1987             :   }
    1988             : 
    1989           0 :   mOffset = aOffset;
    1990             : 
    1991           0 :   return NS_OK;
    1992             : }
    1993             : 
    1994             : } // namespace mozilla
    1995             : 
    1996             : // avoid redefined macro in unified build
    1997             : #undef LOG
    1998             : #undef ILOG

Generated by: LCOV version 1.13