dom/media/BaseMediaResource.cpp
author Mike Hommey <mh+mozilla@glandium.org>
Fri, 11 Jan 2019 16:01:15 +0000
changeset 453570 daf50f25895db073e44d50fecf2e4f6fe873865d
parent 452451 9c9f8232272eda52a244059234f6f3ca9fc2afdd
child 474648 c4781d18e0f5d7f276c91a809db4cfc831c7a24d
permissions -rw-r--r--
Bug 1519307 - Add a new project to build useful parts of breakpad independently. r=froydnj With `ac_add_options --enable-project=tools/crashreporter` in a mozconfig, `./mach build` builds minidump_stackwalk, dump_syms and fileid. One caveat is that due to limitation in how the build system works currently, it's cumbersome to keep dump_syms as a host program for Gecko, and to make it a target program for this project. For now, keep it as a host program. We're not going to use it on automation, but it's still convenient to have for quick local builds (I've had to resort to awful hacks downstream). Differential Revision: https://phabricator.services.mozilla.com/D16299

#include "BaseMediaResource.h"

#include "ChannelMediaResource.h"
#include "CloneableWithRangeMediaResource.h"
#include "FileMediaResource.h"
#include "MediaContainerType.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/InputStreamLengthHelper.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsICloneableInputStream.h"
#include "nsIFile.h"
#include "nsIFileChannel.h"
#include "nsIInputStream.h"
#include "nsISeekableStream.h"
#include "nsNetUtil.h"

namespace mozilla {

already_AddRefed<BaseMediaResource> BaseMediaResource::Create(
    MediaResourceCallback* aCallback, nsIChannel* aChannel,
    bool aIsPrivateBrowsing) {
  NS_ASSERTION(NS_IsMainThread(),
               "MediaResource::Open called on non-main thread");

  // If the channel was redirected, we want the post-redirect URI;
  // but if the URI scheme was expanded, say from chrome: to jar:file:,
  // we want the original URI.
  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
  NS_ENSURE_SUCCESS(rv, nullptr);

  nsAutoCString contentTypeString;
  aChannel->GetContentType(contentTypeString);
  Maybe<MediaContainerType> containerType =
      MakeMediaContainerType(contentTypeString);
  if (!containerType) {
    return nullptr;
  }

  // Let's try to create a FileMediaResource in case the channel is a nsIFile
  nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
  if (fc) {
    RefPtr<BaseMediaResource> resource =
        new FileMediaResource(aCallback, aChannel, uri);
    return resource.forget();
  }

  int64_t streamLength = -1;

  RefPtr<mozilla::dom::BlobImpl> blobImpl;
  if (dom::IsBlobURI(uri) &&
      NS_SUCCEEDED(NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl))) &&
      blobImpl) {
    IgnoredErrorResult rv;

    nsCOMPtr<nsIInputStream> stream;
    blobImpl->CreateInputStream(getter_AddRefs(stream), rv);
    if (NS_WARN_IF(rv.Failed())) {
      return nullptr;
    }

    // If this stream knows its own size synchronously, we can still use
    // FileMediaResource. If the size is known, it means that the reading
    // doesn't require any async operation.  This is required because
    // FileMediaResource doesn't work with nsIAsyncInputStreams.
    int64_t length;
    if (InputStreamLengthHelper::GetSyncLength(stream, &length) &&
        length >= 0) {
      RefPtr<BaseMediaResource> resource =
          new FileMediaResource(aCallback, aChannel, uri, length);
      return resource.forget();
    }

    // Also if the stream doesn't know its own size synchronously, we can still
    // read the length from the blob.
    uint64_t size = blobImpl->GetSize(rv);
    if (NS_WARN_IF(rv.Failed())) {
      return nullptr;
    }

    // Maybe this blob URL can be cloned with a range.
    nsCOMPtr<nsICloneableInputStreamWithRange> cloneableWithRange =
        do_QueryInterface(stream);
    if (cloneableWithRange) {
      RefPtr<BaseMediaResource> resource = new CloneableWithRangeMediaResource(
          aCallback, aChannel, uri, stream, size);
      return resource.forget();
    }

    // We know the size of the stream for blobURLs, let's use it.
    streamLength = size;
  }

  RefPtr<BaseMediaResource> resource = new ChannelMediaResource(
      aCallback, aChannel, uri, streamLength, aIsPrivateBrowsing);
  return resource.forget();
}

void BaseMediaResource::SetLoadInBackground(bool aLoadInBackground) {
  if (aLoadInBackground == mLoadInBackground) {
    return;
  }
  mLoadInBackground = aLoadInBackground;
  if (!mChannel) {
    // No channel, resource is probably already loaded.
    return;
  }

  MediaDecoderOwner* owner = mCallback->GetMediaOwner();
  if (!owner) {
    NS_WARNING("Null owner in MediaResource::SetLoadInBackground()");
    return;
  }
  dom::HTMLMediaElement* element = owner->GetMediaElement();
  if (!element) {
    NS_WARNING("Null element in MediaResource::SetLoadInBackground()");
    return;
  }

  bool isPending = false;
  if (NS_SUCCEEDED(mChannel->IsPending(&isPending)) && isPending) {
    nsLoadFlags loadFlags;
    DebugOnly<nsresult> rv = mChannel->GetLoadFlags(&loadFlags);
    NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");

    if (aLoadInBackground) {
      loadFlags |= nsIRequest::LOAD_BACKGROUND;
    } else {
      loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
    }
    ModifyLoadFlags(loadFlags);
  }
}

void BaseMediaResource::ModifyLoadFlags(nsLoadFlags aFlags) {
  nsCOMPtr<nsILoadGroup> loadGroup;
  nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
  MOZ_ASSERT(NS_SUCCEEDED(rv), "GetLoadGroup() failed!");

  nsresult status;
  mChannel->GetStatus(&status);

  bool inLoadGroup = false;
  if (loadGroup) {
    rv = loadGroup->RemoveRequest(mChannel, nullptr, status);
    if (NS_SUCCEEDED(rv)) {
      inLoadGroup = true;
    }
  }

  rv = mChannel->SetLoadFlags(aFlags);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "SetLoadFlags() failed!");

  if (inLoadGroup) {
    rv = loadGroup->AddRequest(mChannel, nullptr);
    MOZ_ASSERT(NS_SUCCEEDED(rv), "AddRequest() failed!");
  }
}

}  // namespace mozilla