mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1235183. Part 3 - move mChannel to ChannelLoader so it will be easier to create the channel asynchronously. r=cpearce
MozReview-Commit-ID: 5n8P0L7m0Iw --HG-- extra : rebase_source : d15417c822467d04367cb19e40b9fdb8db822ae8
This commit is contained in:
parent
1377c9112b
commit
7c52f8c397
@ -465,6 +465,152 @@ void HTMLMediaElement::ReportLoadError(const char* aMsg,
|
||||
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)
|
||||
|
||||
@ -678,28 +824,8 @@ 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()
|
||||
@ -1256,9 +1382,9 @@ 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.
|
||||
@ -1314,87 +1440,12 @@ nsresult HTMLMediaElement::LoadResource()
|
||||
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;
|
||||
RefPtr<ChannelLoader> loader = new ChannelLoader;
|
||||
nsresult rv = loader->Load(this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mChannelLoader = loader.forget();
|
||||
}
|
||||
|
||||
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;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult HTMLMediaElement::LoadWithChannel(nsIChannel* aChannel,
|
||||
@ -2406,8 +2457,8 @@ HTMLMediaElement::~HTMLMediaElement()
|
||||
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();
|
||||
@ -3062,8 +3113,10 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* 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
|
||||
@ -3131,9 +3184,10 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
||||
}
|
||||
#endif
|
||||
|
||||
// Decoder successfully created, the decoder now owns the MediaResource
|
||||
// which owns the channel.
|
||||
mChannel = nullptr;
|
||||
if (mChannelLoader) {
|
||||
mChannelLoader->Done();
|
||||
mChannelLoader = nullptr;
|
||||
}
|
||||
|
||||
AddMediaElementToURITable();
|
||||
|
||||
|
@ -732,6 +732,7 @@ public:
|
||||
protected:
|
||||
virtual ~HTMLMediaElement();
|
||||
|
||||
class ChannelLoader;
|
||||
class MediaLoadListener;
|
||||
class MediaStreamTracksAvailableCallback;
|
||||
class MediaStreamTrackListener;
|
||||
@ -1255,11 +1256,7 @@ protected:
|
||||
// that resolved to a MediaSource.
|
||||
RefPtr<MediaSource> mMediaSource;
|
||||
|
||||
// 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;
|
||||
RefPtr<ChannelLoader> mChannelLoader;
|
||||
|
||||
// Error attribute
|
||||
RefPtr<MediaError> mError;
|
||||
|
Loading…
Reference in New Issue
Block a user