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:
JW Wang 2016-06-24 10:41:43 +08:00
parent 1377c9112b
commit 7c52f8c397
2 changed files with 168 additions and 117 deletions

View File

@ -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();

View File

@ -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;