LCOV - code coverage report
Current view: directory - content/media - MediaResource.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 547 0 0.0 %
Date: 2012-06-02 Functions: 91 0 0.0 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Chris Double <chris.double@double.co.nz>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "MediaResource.h"
      40                 : 
      41                 : #include "mozilla/Mutex.h"
      42                 : #include "nsDebug.h"
      43                 : #include "nsMediaDecoder.h"
      44                 : #include "nsNetUtil.h"
      45                 : #include "nsThreadUtils.h"
      46                 : #include "nsIFile.h"
      47                 : #include "nsIFileChannel.h"
      48                 : #include "nsIHttpChannel.h"
      49                 : #include "nsISeekableStream.h"
      50                 : #include "nsIInputStream.h"
      51                 : #include "nsIOutputStream.h"
      52                 : #include "nsIRequestObserver.h"
      53                 : #include "nsIStreamListener.h"
      54                 : #include "nsIScriptSecurityManager.h"
      55                 : #include "nsCrossSiteListenerProxy.h"
      56                 : #include "nsHTMLMediaElement.h"
      57                 : #include "nsIDocument.h"
      58                 : #include "nsDOMError.h"
      59                 : #include "nsICachingChannel.h"
      60                 : #include "nsURILoader.h"
      61                 : #include "nsIAsyncVerifyRedirectCallback.h"
      62                 : #include "mozilla/Util.h" // for DebugOnly
      63                 : #include "nsContentUtils.h"
      64                 : 
      65                 : static const PRUint32 HTTP_OK_CODE = 200;
      66                 : static const PRUint32 HTTP_PARTIAL_RESPONSE_CODE = 206;
      67                 : 
      68                 : using namespace mozilla;
      69                 : 
      70               0 : ChannelMediaResource::ChannelMediaResource(nsMediaDecoder* aDecoder,
      71                 :     nsIChannel* aChannel, nsIURI* aURI)
      72                 :   : MediaResource(aDecoder, aChannel, aURI),
      73                 :     mOffset(0), mSuspendCount(0),
      74                 :     mReopenOnError(false), mIgnoreClose(false),
      75                 :     mCacheStream(this),
      76                 :     mLock("ChannelMediaResource.mLock"),
      77               0 :     mIgnoreResume(false)
      78                 : {
      79               0 : }
      80                 : 
      81               0 : ChannelMediaResource::~ChannelMediaResource()
      82                 : {
      83               0 :   if (mListener) {
      84                 :     // Kill its reference to us since we're going away
      85               0 :     mListener->Revoke();
      86                 :   }
      87               0 : }
      88                 : 
      89                 : // ChannelMediaResource::Listener just observes the channel and
      90                 : // forwards notifications to the ChannelMediaResource. We use multiple
      91                 : // listener objects so that when we open a new stream for a seek we can
      92                 : // disconnect the old listener from the ChannelMediaResource and hook up
      93                 : // a new listener, so notifications from the old channel are discarded
      94                 : // and don't confuse us.
      95               0 : NS_IMPL_ISUPPORTS4(ChannelMediaResource::Listener,
      96                 :                    nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
      97                 :                    nsIInterfaceRequestor)
      98                 : 
      99                 : nsresult
     100               0 : ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest,
     101                 :                                                nsISupports* aContext)
     102                 : {
     103               0 :   if (!mResource)
     104               0 :     return NS_OK;
     105               0 :   return mResource->OnStartRequest(aRequest);
     106                 : }
     107                 : 
     108                 : nsresult
     109               0 : ChannelMediaResource::Listener::OnStopRequest(nsIRequest* aRequest,
     110                 :                                               nsISupports* aContext,
     111                 :                                               nsresult aStatus)
     112                 : {
     113               0 :   if (!mResource)
     114               0 :     return NS_OK;
     115               0 :   return mResource->OnStopRequest(aRequest, aStatus);
     116                 : }
     117                 : 
     118                 : nsresult
     119               0 : ChannelMediaResource::Listener::OnDataAvailable(nsIRequest* aRequest,
     120                 :                                                 nsISupports* aContext,
     121                 :                                                 nsIInputStream* aStream,
     122                 :                                                 PRUint32 aOffset,
     123                 :                                                 PRUint32 aCount)
     124                 : {
     125               0 :   if (!mResource)
     126               0 :     return NS_OK;
     127               0 :   return mResource->OnDataAvailable(aRequest, aStream, aCount);
     128                 : }
     129                 : 
     130                 : nsresult
     131               0 : ChannelMediaResource::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
     132                 :                                                        nsIChannel* aNewChannel,
     133                 :                                                        PRUint32 aFlags,
     134                 :                                                        nsIAsyncVerifyRedirectCallback* cb)
     135                 : {
     136               0 :   nsresult rv = NS_OK;
     137               0 :   if (mResource)
     138               0 :     rv = mResource->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
     139                 : 
     140               0 :   if (NS_FAILED(rv))
     141               0 :     return rv;
     142                 : 
     143               0 :   cb->OnRedirectVerifyCallback(NS_OK);
     144               0 :   return NS_OK;
     145                 : }
     146                 : 
     147                 : nsresult
     148               0 : ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
     149                 : {
     150               0 :   return QueryInterface(aIID, aResult);
     151                 : }
     152                 : 
     153                 : nsresult
     154               0 : ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
     155                 : {
     156               0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     157                 : 
     158               0 :   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     159               0 :   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     160                 :   nsresult status;
     161               0 :   nsresult rv = aRequest->GetStatus(&status);
     162               0 :   NS_ENSURE_SUCCESS(rv, rv);
     163                 : 
     164               0 :   if (element->ShouldCheckAllowOrigin()) {
     165                 :     // If the request was cancelled by nsCORSListenerProxy due to failing
     166                 :     // the CORS security check, send an error through to the media element.
     167               0 :     if (status == NS_ERROR_DOM_BAD_URI) {
     168               0 :       mDecoder->NetworkError();
     169               0 :       return NS_ERROR_DOM_BAD_URI;
     170                 :     }
     171                 :   }
     172                 : 
     173               0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
     174               0 :   bool seekable = false;
     175               0 :   if (hc) {
     176               0 :     PRUint32 responseStatus = 0;
     177               0 :     hc->GetResponseStatus(&responseStatus);
     178               0 :     bool succeeded = false;
     179               0 :     hc->GetRequestSucceeded(&succeeded);
     180                 : 
     181               0 :     if (!succeeded && NS_SUCCEEDED(status)) {
     182                 :       // HTTP-level error (e.g. 4xx); treat this as a fatal network-level error.
     183                 :       // We might get this on a seek.
     184                 :       // (Note that lower-level errors indicated by NS_FAILED(status) are
     185                 :       // handled in OnStopRequest.)
     186                 :       // A 416 error should treated as EOF here... it's possible
     187                 :       // that we don't get Content-Length, we read N bytes, then we
     188                 :       // suspend and resume, the resume reopens the channel and we seek to
     189                 :       // offset N, but there are no more bytes, so we get a 416
     190                 :       // "Requested Range Not Satisfiable".
     191               0 :       if (responseStatus != HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE) {
     192               0 :         mDecoder->NetworkError();
     193                 :       }
     194                 : 
     195                 :       // This disconnects our listener so we don't get any more data. We
     196                 :       // certainly don't want an error page to end up in our cache!
     197               0 :       CloseChannel();
     198               0 :       return NS_OK;
     199                 :     }
     200                 : 
     201               0 :     nsCAutoString ranges;
     202               0 :     hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
     203               0 :                           ranges);
     204               0 :     bool acceptsRanges = ranges.EqualsLiteral("bytes");
     205                 : 
     206               0 :     if (mOffset == 0) {
     207                 :       // Look for duration headers from known Ogg content systems.
     208                 :       // In the case of multiple options for obtaining the duration
     209                 :       // the order of precedence is:
     210                 :       // 1) The Media resource metadata if possible (done by the decoder itself).
     211                 :       // 2) Content-Duration message header.
     212                 :       // 3) X-AMZ-Meta-Content-Duration.
     213                 :       // 4) X-Content-Duration.
     214                 :       // 5) Perform a seek in the decoder to find the value.
     215               0 :       nsCAutoString durationText;
     216               0 :       PRInt32 ec = 0;
     217               0 :       rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("Content-Duration"), durationText);
     218               0 :       if (NS_FAILED(rv)) {
     219               0 :         rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-AMZ-Meta-Content-Duration"), durationText);
     220                 :       }
     221               0 :       if (NS_FAILED(rv)) {
     222               0 :         rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-Content-Duration"), durationText);
     223                 :       }
     224                 : 
     225               0 :       if (NS_SUCCEEDED(rv)) {
     226               0 :         double duration = durationText.ToDouble(&ec);
     227               0 :         if (ec == NS_OK && duration >= 0) {
     228               0 :           mDecoder->SetDuration(duration);
     229                 :         }
     230                 :       } else {
     231               0 :         mDecoder->SetInfinite(true);
     232                 :       }
     233                 :     }
     234                 : 
     235               0 :     if (mOffset > 0 && responseStatus == HTTP_OK_CODE) {
     236                 :       // If we get an OK response but we were seeking, we have to assume
     237                 :       // that seeking doesn't work. We also need to tell the cache that
     238                 :       // it's getting data for the start of the stream.
     239               0 :       mCacheStream.NotifyDataStarted(0);
     240               0 :       mOffset = 0;
     241                 : 
     242                 :       // The server claimed it supported range requests.  It lied.
     243               0 :       acceptsRanges = false;
     244               0 :     } else if (mOffset == 0 &&
     245                 :                (responseStatus == HTTP_OK_CODE ||
     246                 :                 responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
     247                 :       // We weren't seeking and got a valid response status,
     248                 :       // set the length of the content.
     249               0 :       PRInt32 cl = -1;
     250               0 :       hc->GetContentLength(&cl);
     251               0 :       if (cl >= 0) {
     252               0 :         mCacheStream.NotifyDataLength(cl);
     253                 :       }
     254                 :     }
     255                 :     // XXX we probably should examine the Content-Range header in case
     256                 :     // the server gave us a range which is not quite what we asked for
     257                 : 
     258                 :     // If we get an HTTP_OK_CODE response to our byte range request,
     259                 :     // and the server isn't sending Accept-Ranges:bytes then we don't
     260                 :     // support seeking.
     261                 :     seekable =
     262               0 :       responseStatus == HTTP_PARTIAL_RESPONSE_CODE || acceptsRanges;
     263                 : 
     264               0 :     if (seekable) {
     265               0 :       mDecoder->SetInfinite(false);
     266                 :     }
     267                 :   }
     268               0 :   mDecoder->SetSeekable(seekable);
     269               0 :   mCacheStream.SetSeekable(seekable);
     270                 : 
     271               0 :   nsCOMPtr<nsICachingChannel> cc = do_QueryInterface(aRequest);
     272               0 :   if (cc) {
     273               0 :     bool fromCache = false;
     274               0 :     rv = cc->IsFromCache(&fromCache);
     275               0 :     if (NS_SUCCEEDED(rv) && !fromCache) {
     276               0 :       cc->SetCacheAsFile(true);
     277                 :     }
     278                 :   }
     279                 : 
     280                 :   {
     281               0 :     MutexAutoLock lock(mLock);
     282               0 :     mChannelStatistics.Start(TimeStamp::Now());
     283                 :   }
     284                 : 
     285               0 :   mReopenOnError = false;
     286               0 :   mIgnoreClose = false;
     287               0 :   if (mSuspendCount > 0) {
     288                 :     // Re-suspend the channel if it needs to be suspended
     289                 :     // No need to call PossiblySuspend here since the channel is
     290                 :     // definitely in the right state for us in OnStartRequest.
     291               0 :     mChannel->Suspend();
     292               0 :     mIgnoreResume = false;
     293                 :   }
     294                 : 
     295                 :   // Fires an initial progress event and sets up the stall counter so stall events
     296                 :   // fire if no download occurs within the required time frame.
     297               0 :   mDecoder->Progress(false);
     298                 : 
     299               0 :   return NS_OK;
     300                 : }
     301                 : 
     302                 : nsresult
     303               0 : ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
     304                 : {
     305               0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     306               0 :   NS_ASSERTION(mSuspendCount == 0,
     307                 :                "How can OnStopRequest fire while we're suspended?");
     308                 : 
     309                 :   {
     310               0 :     MutexAutoLock lock(mLock);
     311               0 :     mChannelStatistics.Stop(TimeStamp::Now());
     312                 :   }
     313                 : 
     314                 :   // Note that aStatus might have succeeded --- this might be a normal close
     315                 :   // --- even in situations where the server cut us off because we were
     316                 :   // suspended. So we need to "reopen on error" in that case too. The only
     317                 :   // cases where we don't need to reopen are when *we* closed the stream.
     318                 :   // But don't reopen if we need to seek and we don't think we can... that would
     319                 :   // cause us to just re-read the stream, which would be really bad.
     320               0 :   if (mReopenOnError &&
     321                 :       aStatus != NS_ERROR_PARSED_DATA_CACHED && aStatus != NS_BINDING_ABORTED &&
     322               0 :       (mOffset == 0 || mCacheStream.IsSeekable())) {
     323                 :     // If the stream did close normally, then if the server is seekable we'll
     324                 :     // just seek to the end of the resource and get an HTTP 416 error because
     325                 :     // there's nothing there, so this isn't bad.
     326               0 :     nsresult rv = CacheClientSeek(mOffset, false);
     327               0 :     if (NS_SUCCEEDED(rv))
     328               0 :       return rv;
     329                 :     // If the reopen/reseek fails, just fall through and treat this
     330                 :     // error as fatal.
     331                 :   }
     332                 : 
     333               0 :   if (!mIgnoreClose) {
     334               0 :     mCacheStream.NotifyDataEnded(aStatus);
     335                 : 
     336                 :     // Move this request back into the foreground.  This is necessary for
     337                 :     // requests owned by video documents to ensure the load group fires
     338                 :     // OnStopRequest when restoring from session history.
     339                 :     nsLoadFlags loadFlags;
     340               0 :     DebugOnly<nsresult> rv = mChannel->GetLoadFlags(&loadFlags);
     341               0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");
     342                 : 
     343               0 :     if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
     344               0 :       ModifyLoadFlags(loadFlags & ~nsIRequest::LOAD_BACKGROUND);
     345                 :     }
     346                 :   }
     347                 : 
     348               0 :   return NS_OK;
     349                 : }
     350                 : 
     351                 : nsresult
     352               0 : ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
     353                 :                                         PRUint32 aFlags)
     354                 : {
     355               0 :   mChannel = aNew;
     356               0 :   SetupChannelHeaders();
     357               0 :   return NS_OK;
     358                 : }
     359                 : 
     360               0 : struct CopySegmentClosure {
     361                 :   nsCOMPtr<nsIPrincipal> mPrincipal;
     362                 :   ChannelMediaResource*  mResource;
     363                 : };
     364                 : 
     365                 : NS_METHOD
     366               0 : ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream,
     367                 :                                          void *aClosure,
     368                 :                                          const char *aFromSegment,
     369                 :                                          PRUint32 aToOffset,
     370                 :                                          PRUint32 aCount,
     371                 :                                          PRUint32 *aWriteCount)
     372                 : {
     373               0 :   CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
     374                 : 
     375               0 :   closure->mResource->mDecoder->NotifyDataArrived(aFromSegment, aCount, closure->mResource->mOffset);
     376                 : 
     377                 :   // Keep track of where we're up to
     378               0 :   closure->mResource->mOffset += aCount;
     379                 :   closure->mResource->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
     380               0 :                                                       closure->mPrincipal);
     381               0 :   *aWriteCount = aCount;
     382               0 :   return NS_OK;
     383                 : }
     384                 : 
     385                 : nsresult
     386               0 : ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
     387                 :                                       nsIInputStream* aStream,
     388                 :                                       PRUint32 aCount)
     389                 : {
     390               0 :   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
     391                 : 
     392                 :   {
     393               0 :     MutexAutoLock lock(mLock);
     394               0 :     mChannelStatistics.AddBytes(aCount);
     395                 :   }
     396                 : 
     397               0 :   CopySegmentClosure closure;
     398               0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     399               0 :   if (secMan && mChannel) {
     400               0 :     secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
     401                 :   }
     402               0 :   closure.mResource = this;
     403                 : 
     404               0 :   PRUint32 count = aCount;
     405               0 :   while (count > 0) {
     406                 :     PRUint32 read;
     407                 :     nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
     408               0 :                                         &read);
     409               0 :     if (NS_FAILED(rv))
     410               0 :       return rv;
     411               0 :     NS_ASSERTION(read > 0, "Read 0 bytes while data was available?");
     412               0 :     count -= read;
     413                 :   }
     414                 : 
     415               0 :   return NS_OK;
     416                 : }
     417                 : 
     418               0 : nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
     419                 : {
     420               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     421                 : 
     422               0 :   nsresult rv = mCacheStream.Init();
     423               0 :   if (NS_FAILED(rv))
     424               0 :     return rv;
     425               0 :   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
     426                 : 
     427               0 :   if (!mChannel) {
     428                 :     // When we're a clone, the decoder might ask us to Open even though
     429                 :     // we haven't established an mChannel (because we might not need one)
     430               0 :     NS_ASSERTION(!aStreamListener,
     431                 :                  "Should have already been given a channel if we're to return a stream listener");
     432               0 :     return NS_OK;
     433                 :   }
     434                 : 
     435               0 :   return OpenChannel(aStreamListener);
     436                 : }
     437                 : 
     438               0 : nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
     439                 : {
     440               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     441               0 :   NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
     442               0 :   NS_ASSERTION(!mListener, "Listener should have been removed by now");
     443                 : 
     444               0 :   if (aStreamListener) {
     445               0 :     *aStreamListener = nsnull;
     446                 :   }
     447                 : 
     448               0 :   mListener = new Listener(this);
     449               0 :   NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
     450                 : 
     451               0 :   if (aStreamListener) {
     452               0 :     *aStreamListener = mListener;
     453               0 :     NS_ADDREF(*aStreamListener);
     454                 :   } else {
     455               0 :     mChannel->SetNotificationCallbacks(mListener.get());
     456                 : 
     457               0 :     nsCOMPtr<nsIStreamListener> listener = mListener.get();
     458                 : 
     459                 :     // Ensure that if we're loading cross domain, that the server is sending
     460                 :     // an authorizing Access-Control header.
     461               0 :     nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     462               0 :     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     463               0 :     if (element->ShouldCheckAllowOrigin()) {
     464                 :       nsresult rv;
     465                 :       nsCORSListenerProxy* crossSiteListener =
     466                 :         new nsCORSListenerProxy(mListener,
     467               0 :                                 element->NodePrincipal(),
     468                 :                                 mChannel,
     469                 :                                 false,
     470               0 :                                 &rv);
     471               0 :       listener = crossSiteListener;
     472               0 :       NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
     473               0 :       NS_ENSURE_SUCCESS(rv, rv);
     474                 :     } else {
     475               0 :       nsresult rv = nsContentUtils::GetSecurityManager()->
     476                 :         CheckLoadURIWithPrincipal(element->NodePrincipal(),
     477                 :                                   mURI,
     478               0 :                                   nsIScriptSecurityManager::STANDARD);
     479               0 :       NS_ENSURE_SUCCESS(rv, rv);
     480                 :     }
     481                 : 
     482               0 :     SetupChannelHeaders();
     483                 : 
     484               0 :     nsresult rv = mChannel->AsyncOpen(listener, nsnull);
     485               0 :     NS_ENSURE_SUCCESS(rv, rv);
     486                 :   }
     487                 : 
     488               0 :   return NS_OK;
     489                 : }
     490                 : 
     491               0 : void ChannelMediaResource::SetupChannelHeaders()
     492                 : {
     493                 :   // Always use a byte range request even if we're reading from the start
     494                 :   // of the resource.
     495                 :   // This enables us to detect if the stream supports byte range
     496                 :   // requests, and therefore seeking, early.
     497               0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     498               0 :   if (hc) {
     499               0 :     nsCAutoString rangeString("bytes=");
     500               0 :     rangeString.AppendInt(mOffset);
     501               0 :     rangeString.Append("-");
     502               0 :     hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
     503                 : 
     504                 :     // Send Accept header for video and audio types only (Bug 489071)
     505               0 :     NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     506               0 :     nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     507               0 :     if (!element) {
     508                 :       return;
     509                 :     }
     510               0 :     element->SetRequestHeaders(hc);
     511                 :   } else {
     512               0 :     NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
     513                 :   }
     514                 : }
     515                 : 
     516               0 : nsresult ChannelMediaResource::Close()
     517                 : {
     518               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     519                 : 
     520               0 :   mCacheStream.Close();
     521               0 :   CloseChannel();
     522               0 :   return NS_OK;
     523                 : }
     524                 : 
     525               0 : already_AddRefed<nsIPrincipal> ChannelMediaResource::GetCurrentPrincipal()
     526                 : {
     527               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     528                 : 
     529               0 :   nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal();
     530               0 :   return principal.forget();
     531                 : }
     532                 : 
     533               0 : bool ChannelMediaResource::CanClone()
     534                 : {
     535               0 :   return mCacheStream.IsAvailableForSharing();
     536                 : }
     537                 : 
     538               0 : MediaResource* ChannelMediaResource::CloneData(nsMediaDecoder* aDecoder)
     539                 : {
     540               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     541               0 :   NS_ASSERTION(mCacheStream.IsAvailableForSharing(), "Stream can't be cloned");
     542                 : 
     543               0 :   ChannelMediaResource* resource = new ChannelMediaResource(aDecoder, nsnull, mURI);
     544               0 :   if (resource) {
     545                 :     // Initially the clone is treated as suspended by the cache, because
     546                 :     // we don't have a channel. If the cache needs to read data from the clone
     547                 :     // it will call CacheClientResume (or CacheClientSeek with aResume true)
     548                 :     // which will recreate the channel. This way, if all of the media data
     549                 :     // is already in the cache we don't create an unneccesary HTTP channel
     550                 :     // and perform a useless HTTP transaction.
     551               0 :     resource->mSuspendCount = 1;
     552               0 :     resource->mCacheStream.InitAsClone(&mCacheStream);
     553               0 :     resource->mChannelStatistics = mChannelStatistics;
     554               0 :     resource->mChannelStatistics.Stop(TimeStamp::Now());
     555                 :   }
     556               0 :   return resource;
     557                 : }
     558                 : 
     559               0 : void ChannelMediaResource::CloseChannel()
     560                 : {
     561               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     562                 : 
     563                 :   {
     564               0 :     MutexAutoLock lock(mLock);
     565               0 :     mChannelStatistics.Stop(TimeStamp::Now());
     566                 :   }
     567                 : 
     568               0 :   if (mListener) {
     569               0 :     mListener->Revoke();
     570               0 :     mListener = nsnull;
     571                 :   }
     572                 : 
     573               0 :   if (mChannel) {
     574               0 :     if (mSuspendCount > 0) {
     575                 :       // Resume the channel before we cancel it
     576               0 :       PossiblyResume();
     577                 :     }
     578                 :     // The status we use here won't be passed to the decoder, since
     579                 :     // we've already revoked the listener. It can however be passed
     580                 :     // to DocumentViewerImpl::LoadComplete if our channel is the one
     581                 :     // that kicked off creation of a video document. We don't want that
     582                 :     // document load to think there was an error.
     583                 :     // NS_ERROR_PARSED_DATA_CACHED is the best thing we have for that
     584                 :     // at the moment.
     585               0 :     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     586               0 :     mChannel = nsnull;
     587                 :   }
     588               0 : }
     589                 : 
     590               0 : nsresult ChannelMediaResource::ReadFromCache(char* aBuffer,
     591                 :                                              PRInt64 aOffset,
     592                 :                                              PRUint32 aCount)
     593                 : {
     594               0 :   return mCacheStream.ReadFromCache(aBuffer, aOffset, aCount);
     595                 : }
     596                 : 
     597               0 : nsresult ChannelMediaResource::Read(char* aBuffer,
     598                 :                                     PRUint32 aCount,
     599                 :                                     PRUint32* aBytes)
     600                 : {
     601               0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     602                 : 
     603               0 :   return mCacheStream.Read(aBuffer, aCount, aBytes);
     604                 : }
     605                 : 
     606               0 : nsresult ChannelMediaResource::Seek(PRInt32 aWhence, PRInt64 aOffset)
     607                 : {
     608               0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     609                 : 
     610               0 :   return mCacheStream.Seek(aWhence, aOffset);
     611                 : }
     612                 : 
     613               0 : PRInt64 ChannelMediaResource::Tell()
     614                 : {
     615               0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     616                 : 
     617               0 :   return mCacheStream.Tell();
     618                 : }
     619                 : 
     620               0 : nsresult ChannelMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
     621                 : {
     622               0 :   return mCacheStream.GetCachedRanges(aRanges);
     623                 : }
     624                 : 
     625               0 : void ChannelMediaResource::Suspend(bool aCloseImmediately)
     626                 : {
     627               0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     628                 : 
     629               0 :   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     630               0 :   if (!element) {
     631                 :     // Shutting down; do nothing.
     632               0 :     return;
     633                 :   }
     634                 : 
     635               0 :   if (mChannel) {
     636               0 :     if (aCloseImmediately && mCacheStream.IsSeekable()) {
     637                 :       // Kill off our channel right now, but don't tell anyone about it.
     638               0 :       mIgnoreClose = true;
     639               0 :       CloseChannel();
     640               0 :       element->DownloadSuspended();
     641               0 :     } else if (mSuspendCount == 0) {
     642                 :       {
     643               0 :         MutexAutoLock lock(mLock);
     644               0 :         mChannelStatistics.Stop(TimeStamp::Now());
     645                 :       }
     646               0 :       PossiblySuspend();
     647               0 :       element->DownloadSuspended();
     648                 :     }
     649                 :   }
     650                 : 
     651               0 :   ++mSuspendCount;
     652                 : }
     653                 : 
     654               0 : void ChannelMediaResource::Resume()
     655                 : {
     656               0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     657               0 :   NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     658                 : 
     659               0 :   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     660               0 :   if (!element) {
     661                 :     // Shutting down; do nothing.
     662               0 :     return;
     663                 :   }
     664                 : 
     665               0 :   NS_ASSERTION(mSuspendCount > 0, "Resume without previous Suspend!");
     666               0 :   --mSuspendCount;
     667               0 :   if (mSuspendCount == 0) {
     668               0 :     if (mChannel) {
     669                 :       // Just wake up our existing channel
     670                 :       {
     671               0 :         MutexAutoLock lock(mLock);
     672               0 :         mChannelStatistics.Start(TimeStamp::Now());
     673                 :       }
     674                 :       // if an error occurs after Resume, assume it's because the server
     675                 :       // timed out the connection and we should reopen it.
     676               0 :       mReopenOnError = true;
     677               0 :       PossiblyResume();
     678               0 :       element->DownloadResumed();
     679                 :     } else {
     680               0 :       PRInt64 totalLength = mCacheStream.GetLength();
     681                 :       // If mOffset is at the end of the stream, then we shouldn't try to
     682                 :       // seek to it. The seek will fail and be wasted anyway. We can leave
     683                 :       // the channel dead; if the media cache wants to read some other data
     684                 :       // in the future, it will call CacheClientSeek itself which will reopen the
     685                 :       // channel.
     686               0 :       if (totalLength < 0 || mOffset < totalLength) {
     687                 :         // There is (or may be) data to read at mOffset, so start reading it.
     688                 :         // Need to recreate the channel.
     689               0 :         CacheClientSeek(mOffset, false);
     690                 :       }
     691               0 :       element->DownloadResumed();
     692                 :     }
     693                 :   }
     694                 : }
     695                 : 
     696                 : nsresult
     697               0 : ChannelMediaResource::RecreateChannel()
     698                 : {
     699                 :   nsLoadFlags loadFlags =
     700                 :     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
     701               0 :     (mLoadInBackground ? nsIRequest::LOAD_BACKGROUND : 0);
     702                 : 
     703               0 :   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     704               0 :   if (!element) {
     705                 :     // The decoder is being shut down, so don't bother opening a new channel
     706               0 :     return NS_OK;
     707                 :   }
     708               0 :   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
     709               0 :   NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
     710                 : 
     711               0 :   return NS_NewChannel(getter_AddRefs(mChannel),
     712                 :                        mURI,
     713                 :                        nsnull,
     714                 :                        loadGroup,
     715                 :                        nsnull,
     716               0 :                        loadFlags);
     717                 : }
     718                 : 
     719                 : void
     720               0 : ChannelMediaResource::DoNotifyDataReceived()
     721                 : {
     722               0 :   mDataReceivedEvent.Revoke();
     723               0 :   mDecoder->NotifyBytesDownloaded();
     724               0 : }
     725                 : 
     726                 : void
     727               0 : ChannelMediaResource::CacheClientNotifyDataReceived()
     728                 : {
     729               0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     730                 :   // NOTE: this can be called with the media cache lock held, so don't
     731                 :   // block or do anything which might try to acquire a lock!
     732                 : 
     733               0 :   if (mDataReceivedEvent.IsPending())
     734               0 :     return;
     735                 : 
     736                 :   mDataReceivedEvent =
     737               0 :     NS_NewNonOwningRunnableMethod(this, &ChannelMediaResource::DoNotifyDataReceived);
     738               0 :   NS_DispatchToMainThread(mDataReceivedEvent.get(), NS_DISPATCH_NORMAL);
     739                 : }
     740                 : 
     741               0 : class DataEnded : public nsRunnable {
     742                 : public:
     743               0 :   DataEnded(nsMediaDecoder* aDecoder, nsresult aStatus) :
     744               0 :     mDecoder(aDecoder), mStatus(aStatus) {}
     745               0 :   NS_IMETHOD Run() {
     746               0 :     mDecoder->NotifyDownloadEnded(mStatus);
     747               0 :     return NS_OK;
     748                 :   }
     749                 : private:
     750                 :   nsRefPtr<nsMediaDecoder> mDecoder;
     751                 :   nsresult                 mStatus;
     752                 : };
     753                 : 
     754                 : void
     755               0 : ChannelMediaResource::CacheClientNotifyDataEnded(nsresult aStatus)
     756                 : {
     757               0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     758                 :   // NOTE: this can be called with the media cache lock held, so don't
     759                 :   // block or do anything which might try to acquire a lock!
     760                 : 
     761               0 :   nsCOMPtr<nsIRunnable> event = new DataEnded(mDecoder, aStatus);
     762               0 :   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
     763               0 : }
     764                 : 
     765                 : nsresult
     766               0 : ChannelMediaResource::CacheClientSeek(PRInt64 aOffset, bool aResume)
     767                 : {
     768               0 :   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     769                 : 
     770               0 :   CloseChannel();
     771                 : 
     772               0 :   if (aResume) {
     773               0 :     NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     774                 :     // No need to mess with the channel, since we're making a new one
     775               0 :     --mSuspendCount;
     776                 :   }
     777                 : 
     778               0 :   nsresult rv = RecreateChannel();
     779               0 :   if (NS_FAILED(rv))
     780               0 :     return rv;
     781                 : 
     782               0 :   mOffset = aOffset;
     783               0 :   return OpenChannel(nsnull);
     784                 : }
     785                 : 
     786                 : nsresult
     787               0 : ChannelMediaResource::CacheClientSuspend()
     788                 : {
     789               0 :   Suspend(false);
     790                 : 
     791               0 :   mDecoder->NotifySuspendedStatusChanged();
     792               0 :   return NS_OK;
     793                 : }
     794                 : 
     795                 : nsresult
     796               0 : ChannelMediaResource::CacheClientResume()
     797                 : {
     798               0 :   Resume();
     799                 : 
     800               0 :   mDecoder->NotifySuspendedStatusChanged();
     801               0 :   return NS_OK;
     802                 : }
     803                 : 
     804                 : PRInt64
     805               0 : ChannelMediaResource::GetNextCachedData(PRInt64 aOffset)
     806                 : {
     807               0 :   return mCacheStream.GetNextCachedData(aOffset);
     808                 : }
     809                 : 
     810                 : PRInt64
     811               0 : ChannelMediaResource::GetCachedDataEnd(PRInt64 aOffset)
     812                 : {
     813               0 :   return mCacheStream.GetCachedDataEnd(aOffset);
     814                 : }
     815                 : 
     816                 : bool
     817               0 : ChannelMediaResource::IsDataCachedToEndOfResource(PRInt64 aOffset)
     818                 : {
     819               0 :   return mCacheStream.IsDataCachedToEndOfStream(aOffset);
     820                 : }
     821                 : 
     822                 : void
     823               0 : ChannelMediaResource::EnsureCacheUpToDate()
     824                 : {
     825               0 :   mCacheStream.EnsureCacheUpdate();
     826               0 : }
     827                 : 
     828                 : bool
     829               0 : ChannelMediaResource::IsSuspendedByCache(MediaResource** aActiveResource)
     830                 : {
     831               0 :   return mCacheStream.AreAllStreamsForResourceSuspended(aActiveResource);
     832                 : }
     833                 : 
     834                 : bool
     835               0 : ChannelMediaResource::IsSuspended()
     836                 : {
     837               0 :   MutexAutoLock lock(mLock);
     838               0 :   return mSuspendCount > 0;
     839                 : }
     840                 : 
     841                 : void
     842               0 : ChannelMediaResource::SetReadMode(nsMediaCacheStream::ReadMode aMode)
     843                 : {
     844               0 :   mCacheStream.SetReadMode(aMode);
     845               0 : }
     846                 : 
     847                 : void
     848               0 : ChannelMediaResource::SetPlaybackRate(PRUint32 aBytesPerSecond)
     849                 : {
     850               0 :   mCacheStream.SetPlaybackRate(aBytesPerSecond);
     851               0 : }
     852                 : 
     853                 : void
     854               0 : ChannelMediaResource::Pin()
     855                 : {
     856               0 :   mCacheStream.Pin();
     857               0 : }
     858                 : 
     859                 : void
     860               0 : ChannelMediaResource::Unpin()
     861                 : {
     862               0 :   mCacheStream.Unpin();
     863               0 : }
     864                 : 
     865                 : double
     866               0 : ChannelMediaResource::GetDownloadRate(bool* aIsReliable)
     867                 : {
     868               0 :   MutexAutoLock lock(mLock);
     869               0 :   return mChannelStatistics.GetRate(TimeStamp::Now(), aIsReliable);
     870                 : }
     871                 : 
     872                 : PRInt64
     873               0 : ChannelMediaResource::GetLength()
     874                 : {
     875               0 :   return mCacheStream.GetLength();
     876                 : }
     877                 : 
     878                 : void
     879               0 : ChannelMediaResource::PossiblySuspend()
     880                 : {
     881               0 :   bool isPending = false;
     882               0 :   nsresult rv = mChannel->IsPending(&isPending);
     883               0 :   if (NS_SUCCEEDED(rv) && isPending) {
     884               0 :     mChannel->Suspend();
     885               0 :     mIgnoreResume = false;
     886                 :   } else {
     887               0 :     mIgnoreResume = true;
     888                 :   }
     889               0 : }
     890                 : 
     891                 : void
     892               0 : ChannelMediaResource::PossiblyResume()
     893                 : {
     894               0 :   if (!mIgnoreResume) {
     895               0 :     mChannel->Resume();
     896                 :   } else {
     897               0 :     mIgnoreResume = false;
     898                 :   }
     899               0 : }
     900                 : 
     901                 : class FileMediaResource : public MediaResource
     902                 : {
     903                 : public:
     904               0 :   FileMediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
     905                 :     MediaResource(aDecoder, aChannel, aURI), mSize(-1),
     906               0 :     mLock("FileMediaResource.mLock")
     907                 :   {
     908               0 :   }
     909               0 :   ~FileMediaResource()
     910               0 :   {
     911               0 :   }
     912                 : 
     913                 :   // Main thread
     914                 :   virtual nsresult Open(nsIStreamListener** aStreamListener);
     915                 :   virtual nsresult Close();
     916               0 :   virtual void     Suspend(bool aCloseImmediately) {}
     917               0 :   virtual void     Resume() {}
     918                 :   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
     919                 :   virtual bool     CanClone();
     920                 :   virtual MediaResource* CloneData(nsMediaDecoder* aDecoder);
     921                 :   virtual nsresult ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount);
     922                 : 
     923                 :   // These methods are called off the main thread.
     924                 : 
     925                 :   // Other thread
     926               0 :   virtual void     SetReadMode(nsMediaCacheStream::ReadMode aMode) {}
     927               0 :   virtual void     SetPlaybackRate(PRUint32 aBytesPerSecond) {}
     928                 :   virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
     929                 :   virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
     930                 :   virtual PRInt64  Tell();
     931                 : 
     932                 :   // Any thread
     933               0 :   virtual void    Pin() {}
     934               0 :   virtual void    Unpin() {}
     935               0 :   virtual double  GetDownloadRate(bool* aIsReliable)
     936                 :   {
     937                 :     // The data's all already here
     938               0 :     *aIsReliable = true;
     939               0 :     return 100*1024*1024; // arbitray, use 100MB/s
     940                 :   }
     941               0 :   virtual PRInt64 GetLength() { return mSize; }
     942               0 :   virtual PRInt64 GetNextCachedData(PRInt64 aOffset)
     943                 :   {
     944               0 :     return (aOffset < mSize) ? aOffset : -1;
     945                 :   }
     946               0 :   virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset) { return NS_MAX(aOffset, mSize); }
     947               0 :   virtual bool    IsDataCachedToEndOfResource(PRInt64 aOffset) { return true; }
     948               0 :   virtual bool    IsSuspendedByCache(MediaResource** aActiveResource)
     949                 :   {
     950               0 :     if (aActiveResource) {
     951               0 :       *aActiveResource = nsnull;
     952                 :     }
     953               0 :     return false;
     954                 :   }
     955               0 :   virtual bool    IsSuspended() { return false; }
     956                 : 
     957                 :   nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
     958                 : 
     959                 : private:
     960                 :   // The file size, or -1 if not known. Immutable after Open().
     961                 :   PRInt64 mSize;
     962                 : 
     963                 :   // This lock handles synchronisation between calls to Close() and
     964                 :   // the Read, Seek, etc calls. Close must not be called while a
     965                 :   // Read or Seek is in progress since it resets various internal
     966                 :   // values to null.
     967                 :   // This lock protects mSeekable and mInput.
     968                 :   Mutex mLock;
     969                 : 
     970                 :   // Seekable stream interface to file. This can be used from any
     971                 :   // thread.
     972                 :   nsCOMPtr<nsISeekableStream> mSeekable;
     973                 : 
     974                 :   // Input stream for the media data. This can be used from any
     975                 :   // thread.
     976                 :   nsCOMPtr<nsIInputStream>  mInput;
     977                 : };
     978                 : 
     979                 : class LoadedEvent : public nsRunnable
     980                 : {
     981                 : public:
     982               0 :   LoadedEvent(nsMediaDecoder* aDecoder) :
     983               0 :     mDecoder(aDecoder)
     984                 :   {
     985               0 :     MOZ_COUNT_CTOR(LoadedEvent);
     986               0 :   }
     987               0 :   ~LoadedEvent()
     988               0 :   {
     989               0 :     MOZ_COUNT_DTOR(LoadedEvent);
     990               0 :   }
     991                 : 
     992               0 :   NS_IMETHOD Run() {
     993               0 :     mDecoder->NotifyDownloadEnded(NS_OK);
     994               0 :     return NS_OK;
     995                 :   }
     996                 : 
     997                 : private:
     998                 :   nsRefPtr<nsMediaDecoder> mDecoder;
     999                 : };
    1000                 : 
    1001               0 : nsresult FileMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
    1002                 : {
    1003               0 :   if (mSize == -1) {
    1004               0 :     return NS_ERROR_FAILURE;
    1005                 :   }
    1006               0 :   aRanges.AppendElement(MediaByteRange(0, mSize));
    1007               0 :   return NS_OK;
    1008                 : }
    1009                 : 
    1010               0 : nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
    1011                 : {
    1012               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1013                 : 
    1014               0 :   if (aStreamListener) {
    1015               0 :     *aStreamListener = nsnull;
    1016                 :   }
    1017                 : 
    1018               0 :   nsresult rv = NS_OK;
    1019               0 :   if (aStreamListener) {
    1020                 :     // The channel is already open. We need a synchronous stream that
    1021                 :     // implements nsISeekableStream, so we have to find the underlying
    1022                 :     // file and reopen it
    1023               0 :     nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
    1024               0 :     if (!fc)
    1025               0 :       return NS_ERROR_UNEXPECTED;
    1026                 : 
    1027               0 :     nsCOMPtr<nsIFile> file;
    1028               0 :     rv = fc->GetFile(getter_AddRefs(file));
    1029               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1030                 : 
    1031               0 :     rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
    1032                 :   } else {
    1033                 :     // Ensure that we never load a local file from some page on a
    1034                 :     // web server.
    1035               0 :     nsHTMLMediaElement* element = mDecoder->GetMediaElement();
    1036               0 :     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
    1037                 : 
    1038               0 :     rv = nsContentUtils::GetSecurityManager()->
    1039                 :            CheckLoadURIWithPrincipal(element->NodePrincipal(),
    1040                 :                                      mURI,
    1041               0 :                                      nsIScriptSecurityManager::STANDARD);
    1042               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1043                 : 
    1044               0 :     rv = mChannel->Open(getter_AddRefs(mInput));
    1045                 :   }
    1046               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1047                 : 
    1048               0 :   mSeekable = do_QueryInterface(mInput);
    1049               0 :   if (!mSeekable) {
    1050                 :     // XXX The file may just be a .url or similar
    1051                 :     // shortcut that points to a Web site. We need to fix this by
    1052                 :     // doing an async open and waiting until we locate the real resource,
    1053                 :     // then using that (if it's still a file!).
    1054               0 :     return NS_ERROR_FAILURE;
    1055                 :   }
    1056                 : 
    1057                 :   // Get the file size and inform the decoder. Only files up to 4GB are
    1058                 :   // supported here.
    1059                 :   PRUint32 size;
    1060               0 :   rv = mInput->Available(&size);
    1061               0 :   if (NS_SUCCEEDED(rv)) {
    1062               0 :     mSize = size;
    1063                 :   }
    1064                 : 
    1065               0 :   nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder);
    1066               0 :   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1067               0 :   return NS_OK;
    1068                 : }
    1069                 : 
    1070               0 : nsresult FileMediaResource::Close()
    1071                 : {
    1072               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1073                 : 
    1074               0 :   MutexAutoLock lock(mLock);
    1075               0 :   if (mChannel) {
    1076               0 :     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
    1077               0 :     mChannel = nsnull;
    1078               0 :     mInput = nsnull;
    1079               0 :     mSeekable = nsnull;
    1080                 :   }
    1081                 : 
    1082               0 :   return NS_OK;
    1083                 : }
    1084                 : 
    1085               0 : already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal()
    1086                 : {
    1087               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1088                 : 
    1089               0 :   nsCOMPtr<nsIPrincipal> principal;
    1090               0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    1091               0 :   if (!secMan || !mChannel)
    1092               0 :     return nsnull;
    1093               0 :   secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
    1094               0 :   return principal.forget();
    1095                 : }
    1096                 : 
    1097               0 : bool FileMediaResource::CanClone()
    1098                 : {
    1099               0 :   return true;
    1100                 : }
    1101                 : 
    1102               0 : MediaResource* FileMediaResource::CloneData(nsMediaDecoder* aDecoder)
    1103                 : {
    1104               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1105                 : 
    1106               0 :   nsHTMLMediaElement* element = aDecoder->GetMediaElement();
    1107               0 :   if (!element) {
    1108                 :     // The decoder is being shut down, so we can't clone
    1109               0 :     return nsnull;
    1110                 :   }
    1111               0 :   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
    1112               0 :   NS_ENSURE_TRUE(loadGroup, nsnull);
    1113                 : 
    1114               0 :   nsCOMPtr<nsIChannel> channel;
    1115                 :   nsresult rv =
    1116               0 :     NS_NewChannel(getter_AddRefs(channel), mURI, nsnull, loadGroup, nsnull, 0);
    1117               0 :   if (NS_FAILED(rv))
    1118               0 :     return nsnull;
    1119                 : 
    1120               0 :   return new FileMediaResource(aDecoder, channel, mURI);
    1121                 : }
    1122                 : 
    1123               0 : nsresult FileMediaResource::ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount)
    1124                 : {
    1125               0 :   MutexAutoLock lock(mLock);
    1126               0 :   if (!mInput || !mSeekable)
    1127               0 :     return NS_ERROR_FAILURE;
    1128               0 :   PRInt64 offset = 0;
    1129               0 :   nsresult res = mSeekable->Tell(&offset);
    1130               0 :   NS_ENSURE_SUCCESS(res,res);
    1131               0 :   res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
    1132               0 :   NS_ENSURE_SUCCESS(res,res);
    1133               0 :   PRUint32 bytesRead = 0;
    1134               0 :   do {
    1135               0 :     PRUint32 x = 0;
    1136               0 :     PRUint32 bytesToRead = aCount - bytesRead;
    1137               0 :     res = mInput->Read(aBuffer, bytesToRead, &x);
    1138               0 :     bytesRead += x;
    1139                 :   } while (bytesRead != aCount && res == NS_OK);
    1140                 : 
    1141                 :   // Reset read head to original position so we don't disturb any other
    1142                 :   // reading thread.
    1143               0 :   nsresult seekres = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1144                 : 
    1145                 :   // If a read failed in the loop above, we want to return its failure code.
    1146               0 :   NS_ENSURE_SUCCESS(res,res);
    1147                 : 
    1148                 :   // Else we succeed if the reset-seek succeeds.
    1149               0 :   return seekres;
    1150                 : }
    1151                 : 
    1152               0 : nsresult FileMediaResource::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
    1153                 : {
    1154               0 :   MutexAutoLock lock(mLock);
    1155               0 :   if (!mInput)
    1156               0 :     return NS_ERROR_FAILURE;
    1157               0 :   return mInput->Read(aBuffer, aCount, aBytes);
    1158                 : }
    1159                 : 
    1160               0 : nsresult FileMediaResource::Seek(PRInt32 aWhence, PRInt64 aOffset)
    1161                 : {
    1162               0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
    1163                 : 
    1164               0 :   MutexAutoLock lock(mLock);
    1165               0 :   if (!mSeekable)
    1166               0 :     return NS_ERROR_FAILURE;
    1167               0 :   return mSeekable->Seek(aWhence, aOffset);
    1168                 : }
    1169                 : 
    1170               0 : PRInt64 FileMediaResource::Tell()
    1171                 : {
    1172               0 :   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
    1173                 : 
    1174               0 :   MutexAutoLock lock(mLock);
    1175               0 :   if (!mSeekable)
    1176               0 :     return 0;
    1177                 : 
    1178               0 :   PRInt64 offset = 0;
    1179               0 :   mSeekable->Tell(&offset);
    1180               0 :   return offset;
    1181                 : }
    1182                 : 
    1183                 : MediaResource*
    1184               0 : MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
    1185                 : {
    1186               0 :   NS_ASSERTION(NS_IsMainThread(),
    1187                 :                      "MediaResource::Open called on non-main thread");
    1188                 : 
    1189                 :   // If the channel was redirected, we want the post-redirect URI;
    1190                 :   // but if the URI scheme was expanded, say from chrome: to jar:file:,
    1191                 :   // we want the original URI.
    1192               0 :   nsCOMPtr<nsIURI> uri;
    1193               0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
    1194               0 :   NS_ENSURE_SUCCESS(rv, nsnull);
    1195                 : 
    1196               0 :   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
    1197               0 :   if (fc) {
    1198               0 :     return new FileMediaResource(aDecoder, aChannel, uri);
    1199                 :   }
    1200               0 :   return new ChannelMediaResource(aDecoder, aChannel, uri);
    1201                 : }
    1202                 : 
    1203               0 : void MediaResource::MoveLoadsToBackground() {
    1204               0 :   NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
    1205               0 :   mLoadInBackground = true;
    1206               0 :   if (!mChannel) {
    1207                 :     // No channel, resource is probably already loaded.
    1208               0 :     return;
    1209                 :   }
    1210                 : 
    1211                 :   nsresult rv;
    1212               0 :   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
    1213               0 :   if (!element) {
    1214               0 :     NS_WARNING("Null element in MediaResource::MoveLoadsToBackground()");
    1215               0 :     return;
    1216                 :   }
    1217                 : 
    1218               0 :   bool isPending = false;
    1219               0 :   if (NS_SUCCEEDED(mChannel->IsPending(&isPending)) &&
    1220                 :       isPending) {
    1221                 :     nsLoadFlags loadFlags;
    1222               0 :     rv = mChannel->GetLoadFlags(&loadFlags);
    1223               0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");
    1224                 : 
    1225               0 :     loadFlags |= nsIRequest::LOAD_BACKGROUND;
    1226               0 :     ModifyLoadFlags(loadFlags);
    1227                 :   }
    1228                 : }
    1229                 : 
    1230               0 : void MediaResource::ModifyLoadFlags(nsLoadFlags aFlags)
    1231                 : {
    1232               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1233               0 :   nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1234               0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadGroup() failed!");
    1235                 : 
    1236                 :   nsresult status;
    1237               0 :   mChannel->GetStatus(&status);
    1238                 : 
    1239                 :   // Note: if (NS_FAILED(status)), the channel won't be in the load group.
    1240               0 :   if (loadGroup &&
    1241               0 :       NS_SUCCEEDED(status)) {
    1242               0 :     rv = loadGroup->RemoveRequest(mChannel, nsnull, status);
    1243               0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveRequest() failed!");
    1244                 :   }
    1245                 : 
    1246               0 :   rv = mChannel->SetLoadFlags(aFlags);
    1247               0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "SetLoadFlags() failed!");
    1248                 : 
    1249               0 :   if (loadGroup &&
    1250               0 :       NS_SUCCEEDED(status)) {
    1251               0 :     rv = loadGroup->AddRequest(mChannel, nsnull);
    1252               0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "AddRequest() failed!");
    1253                 :   }
    1254               0 : }

Generated by: LCOV version 1.7