diff options
Diffstat (limited to 'dom/media/BaseMediaResource.cpp')
-rw-r--r-- | dom/media/BaseMediaResource.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/dom/media/BaseMediaResource.cpp b/dom/media/BaseMediaResource.cpp new file mode 100644 index 0000000000..e5ba50109a --- /dev/null +++ b/dom/media/BaseMediaResource.cpp @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 "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; + } + RefPtr<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; + } + Unused << NS_WARN_IF(NS_FAILED(ModifyLoadFlags(loadFlags))); + } +} + +nsresult BaseMediaResource::ModifyLoadFlags(nsLoadFlags aFlags) { + nsCOMPtr<nsILoadGroup> loadGroup; + nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup)); + MOZ_ASSERT(NS_SUCCEEDED(rv), "GetLoadGroup() failed!"); + + bool inLoadGroup = false; + if (loadGroup) { + nsresult status; + mChannel->GetStatus(&status); + + rv = loadGroup->RemoveRequest(mChannel, nullptr, status); + if (NS_WARN_IF(NS_FAILED(rv))) { + return 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!"); + } + + return NS_OK; +} + +} // namespace mozilla |