--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -460,16 +460,162 @@ void HTMLMediaElement::ReportLoadError(c
NS_LITERAL_CSTRING("Media"),
OwnerDoc(),
nsContentUtils::eDOM_PROPERTIES,
aMsg,
aParams,
aParamCount);
}
+class HTMLMediaElement::ChannelLoader final {
+public:
+ NS_INLINE_DECL_REFCOUNTING(ChannelLoader);
+
+ nsresult Load(HTMLMediaElement* aElement)
+ {
+
+ // determine what security checks need to be performed in AsyncOpen2().
+ nsSecurityFlags securityFlags = aElement->ShouldCheckAllowOrigin()
+ ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS :
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+
+ if (aElement->GetCORSMode() == CORS_USE_CREDENTIALS) {
+ securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+ }
+
+ MOZ_ASSERT(aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
+ nsContentPolicyType contentPolicyType = aElement->IsHTMLElement(nsGkAtoms::audio)
+ ? nsIContentPolicy::TYPE_INTERNAL_AUDIO :
+ nsIContentPolicy::TYPE_INTERNAL_VIDEO;
+
+ nsCOMPtr<nsIDocShell> docShell = aElement->OwnerDoc()->GetDocShell();
+ if (docShell) {
+ nsDocShell* docShellPtr = nsDocShell::Cast(docShell);
+ bool privateBrowsing;
+ docShellPtr->GetUsePrivateBrowsing(&privateBrowsing);
+ if (privateBrowsing) {
+ securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
+ }
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aElement->GetDocumentLoadGroup();
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewChannel(getter_AddRefs(channel),
+ aElement->mLoadingSrc,
+ static_cast<Element*>(aElement),
+ securityFlags,
+ contentPolicyType,
+ loadGroup,
+ nullptr, // aCallbacks
+ nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
+ nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
+ nsIChannel::LOAD_CLASSIFY_URI |
+ nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // This is a workaround and it will be fix in bug 1264230.
+ nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+ if (loadInfo) {
+ NeckoOriginAttributes originAttrs;
+ NS_GetOriginAttributes(channel, originAttrs);
+ loadInfo->SetOriginAttributes(originAttrs);
+ }
+
+ // The listener holds a strong reference to us. This creates a
+ // reference cycle, once we've set mChannel, which is manually broken
+ // in the listener's OnStartRequest method after it is finished with
+ // the element. The cycle will also be broken if we get a shutdown
+ // notification before OnStartRequest fires. Necko guarantees that
+ // OnStartRequest will eventually fire if we don't shut down first.
+ RefPtr<MediaLoadListener> loadListener = new MediaLoadListener(aElement);
+
+ channel->SetNotificationCallbacks(loadListener);
+
+ nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
+ if (hc) {
+ // Use a byte range request from the start of the resource.
+ // This enables us to detect if the stream supports byte range
+ // requests, and therefore seeking, early.
+ hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
+ NS_LITERAL_CSTRING("bytes=0-"),
+ false);
+ aElement->SetRequestHeaders(hc);
+ }
+
+ rv = channel->AsyncOpen2(loadListener);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Else the channel must be open and starting to download. If it encounters
+ // a non-catastrophic failure, it will set a new task to continue loading
+ // another candidate. It's safe to set it as mChannel now.
+ mChannel = channel;
+
+ // loadListener will be unregistered either on shutdown or when
+ // OnStartRequest for the channel we just opened fires.
+ nsContentUtils::RegisterShutdownObserver(loadListener);
+ return NS_OK;
+ }
+
+ void Cancel()
+ {
+ mChannel->Cancel(NS_BINDING_ABORTED);
+ mChannel = nullptr;
+ }
+
+ void Done() {
+ // Decoder successfully created, the decoder now owns the MediaResource
+ // which owns the channel.
+ mChannel = nullptr;
+ }
+
+ nsresult Redirect(nsIChannel* aChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aFlags)
+ {
+ NS_ASSERTION(aChannel == mChannel, "Channels should match!");
+ mChannel = aNewChannel;
+
+ // Handle forwarding of Range header so that the intial detection
+ // of seeking support (via result code 206) works across redirects.
+ nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
+ NS_ENSURE_STATE(http);
+
+ NS_NAMED_LITERAL_CSTRING(rangeHdr, "Range");
+
+ nsAutoCString rangeVal;
+ if (NS_SUCCEEDED(http->GetRequestHeader(rangeHdr, rangeVal))) {
+ NS_ENSURE_STATE(!rangeVal.IsEmpty());
+
+ http = do_QueryInterface(aNewChannel);
+ NS_ENSURE_STATE(http);
+
+ nsresult rv = http->SetRequestHeader(rangeHdr, rangeVal, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+ }
+
+private:
+ ~ChannelLoader()
+ {
+ MOZ_ASSERT(!mChannel);
+ }
+ // Holds a reference to the first channel we open to the media resource.
+ // Once the decoder is created, control over the channel passes to the
+ // decoder, and we null out this reference. We must store this in case
+ // we need to cancel the channel before control of it passes to the decoder.
+ nsCOMPtr<nsIChannel> mChannel;
+};
+
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcMediaSource)
@@ -673,38 +819,18 @@ NS_IMETHODIMP HTMLMediaElement::GetNetwo
return NS_OK;
}
nsresult
HTMLMediaElement::OnChannelRedirect(nsIChannel* aChannel,
nsIChannel* aNewChannel,
uint32_t aFlags)
{
- NS_ASSERTION(aChannel == mChannel, "Channels should match!");
- mChannel = aNewChannel;
-
- // Handle forwarding of Range header so that the intial detection
- // of seeking support (via result code 206) works across redirects.
- nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
- NS_ENSURE_STATE(http);
-
- NS_NAMED_LITERAL_CSTRING(rangeHdr, "Range");
-
- nsAutoCString rangeVal;
- if (NS_SUCCEEDED(http->GetRequestHeader(rangeHdr, rangeVal))) {
- NS_ENSURE_STATE(!rangeVal.IsEmpty());
-
- http = do_QueryInterface(aNewChannel);
- NS_ENSURE_STATE(http);
-
- nsresult rv = http->SetRequestHeader(rangeHdr, rangeVal, false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- return NS_OK;
+ MOZ_ASSERT(mChannelLoader);
+ return mChannelLoader->Redirect(aChannel, aNewChannel, aFlags);
}
void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
mDecoder->Shutdown();
mDecoder = nullptr;
@@ -1251,19 +1377,19 @@ void HTMLMediaElement::UpdatePreloadActi
}
}
nsresult HTMLMediaElement::LoadResource()
{
NS_ASSERTION(mDelayingLoadEvent,
"Should delay load event (if in document) during load");
- if (mChannel) {
- mChannel->Cancel(NS_BINDING_ABORTED);
- mChannel = nullptr;
+ if (mChannelLoader) {
+ mChannelLoader->Cancel();
+ mChannelLoader = nullptr;
}
// Check if media is allowed for the docshell.
nsCOMPtr<nsIDocShell> docShell = OwnerDoc()->GetDocShell();
if (docShell && !docShell->GetAllowMedia()) {
return NS_ERROR_FAILURE;
}
@@ -1309,97 +1435,22 @@ nsresult HTMLMediaElement::LoadResource(
decoder->Shutdown();
return NS_ERROR_FAILURE;
}
RefPtr<MediaResource> resource =
MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
return FinishDecoderSetup(decoder, resource, nullptr);
}
- // determine what security checks need to be performed in AsyncOpen2().
- nsSecurityFlags securityFlags =
- ShouldCheckAllowOrigin() ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS :
- nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
-
- if (GetCORSMode() == CORS_USE_CREDENTIALS) {
- securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
- }
-
- MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
- nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
- nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
-
- nsDocShell* docShellPtr;
- if (docShell) {
- docShellPtr = nsDocShell::Cast(docShell);
- bool privateBrowsing;
- docShellPtr->GetUsePrivateBrowsing(&privateBrowsing);
- if (privateBrowsing) {
- securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
- }
- }
-
- nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
- nsCOMPtr<nsIChannel> channel;
- nsresult rv = NS_NewChannel(getter_AddRefs(channel),
- mLoadingSrc,
- static_cast<Element*>(this),
- securityFlags,
- contentPolicyType,
- loadGroup,
- nullptr, // aCallbacks
- nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
- nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
- nsIChannel::LOAD_CLASSIFY_URI |
- nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
-
- NS_ENSURE_SUCCESS(rv,rv);
-
- // This is a workaround and it will be fix in bug 1264230.
- nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
- if (loadInfo) {
- NeckoOriginAttributes originAttrs;
- NS_GetOriginAttributes(channel, originAttrs);
- loadInfo->SetOriginAttributes(originAttrs);
- }
-
- // The listener holds a strong reference to us. This creates a
- // reference cycle, once we've set mChannel, which is manually broken
- // in the listener's OnStartRequest method after it is finished with
- // the element. The cycle will also be broken if we get a shutdown
- // notification before OnStartRequest fires. Necko guarantees that
- // OnStartRequest will eventually fire if we don't shut down first.
- RefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
-
- channel->SetNotificationCallbacks(loadListener);
-
- nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
- if (hc) {
- // Use a byte range request from the start of the resource.
- // This enables us to detect if the stream supports byte range
- // requests, and therefore seeking, early.
- hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
- NS_LITERAL_CSTRING("bytes=0-"),
- false);
-
- SetRequestHeaders(hc);
- }
-
- rv = channel->AsyncOpen2(loadListener);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Else the channel must be open and starting to download. If it encounters
- // a non-catastrophic failure, it will set a new task to continue loading
- // another candidate. It's safe to set it as mChannel now.
- mChannel = channel;
-
- // loadListener will be unregistered either on shutdown or when
- // OnStartRequest for the channel we just opened fires.
- nsContentUtils::RegisterShutdownObserver(loadListener);
- return NS_OK;
+ RefPtr<ChannelLoader> loader = new ChannelLoader;
+ nsresult rv = loader->Load(this);
+ if (NS_SUCCEEDED(rv)) {
+ mChannelLoader = loader.forget();
+ }
+ return rv;
}
nsresult HTMLMediaElement::LoadWithChannel(nsIChannel* aChannel,
nsIStreamListener** aListener)
{
NS_ENSURE_ARG_POINTER(aChannel);
NS_ENSURE_ARG_POINTER(aListener);
@@ -2401,18 +2452,18 @@ HTMLMediaElement::~HTMLMediaElement()
if (mCaptureStreamPort) {
mCaptureStreamPort->Destroy();
mCaptureStreamPort = nullptr;
}
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
"Destroyed media element should no longer be in element table");
- if (mChannel) {
- mChannel->Cancel(NS_BINDING_ABORTED);
+ if (mChannelLoader) {
+ mChannelLoader->Cancel();
}
WakeLockRelease();
}
void HTMLMediaElement::StopSuspendingAfterFirstFrame()
{
mAllowSuspendAfterFirstFrame = false;
@@ -3057,18 +3108,20 @@ nsresult HTMLMediaElement::InitializeDec
LOG(LogLevel::Debug, ("%p Created decoder %p for type %s", this, decoder.get(), mimeType.get()));
RefPtr<MediaResource> resource =
MediaResource::Create(decoder->GetResourceCallback(), aChannel);
if (!resource)
return NS_ERROR_OUT_OF_MEMORY;
- // stream successfully created, the stream now owns the channel.
- mChannel = nullptr;
+ if (mChannelLoader) {
+ mChannelLoader->Done();
+ mChannelLoader = nullptr;
+ }
// We postpone the |FinishDecoderSetup| function call until we get
// |OnConnected| signal from MediaStreamController which is held by
// RtspMediaResource.
if (DecoderTraits::DecoderWaitsForOnConnected(mimeType)) {
decoder->SetResource(resource);
SetDecoder(decoder);
if (aListener) {
@@ -3126,19 +3179,20 @@ nsresult HTMLMediaElement::FinishDecoder
}
#ifdef MOZ_EME
if (mMediaKeys) {
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
}
#endif
- // Decoder successfully created, the decoder now owns the MediaResource
- // which owns the channel.
- mChannel = nullptr;
+ if (mChannelLoader) {
+ mChannelLoader->Done();
+ mChannelLoader = nullptr;
+ }
AddMediaElementToURITable();
// We may want to suspend the new stream now.
// This will also do an AddRemoveSelfReference.
NotifyOwnerDocumentActivityChangedInternal();
UpdateAudioChannelPlayingState();