Bug 1301055 - part4 : special error dispatching stragedy for Fennec. r=jwwang

On Fennec, we would use external app (Android view) to play the media whih is
unsupported by gecko. We implement some special logic in error sink.

(1) dispatch "OpenMediaWithExternalApp" event
we use this event to start the android video player

(2) doesn't dispatch "error" event
some JS players won't let user to trigger play() again after receving the "error".
So we don't dispatch that event if we want to play the unsupported media more than once.

MozReview-Commit-ID: 7fZK5hdvaOG
This commit is contained in:
Alastor Wu
2016-11-24 10:24:08 +08:00
parent d9b15acaaa
commit 273d74cd0e
2 changed files with 79 additions and 53 deletions

View File

@@ -715,18 +715,23 @@ public:
}
mError = new MediaError(mOwner, aErrorCode, aErrorDetails);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("error"));
if (mOwner->ReadyState() == HAVE_NOTHING &&
aErrorCode == MEDIA_ERR_ABORTED) {
// https://html.spec.whatwg.org/multipage/embedded-content.html#media-data-processing-steps-list
// "If the media data fetching process is aborted by the user"
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
} else if (aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
if (CanOwnerPlayUnsupportedTypeMedia()) {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
OpenUnsupportedMediaForOwner();
} else {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("error"));
if (mOwner->ReadyState() == HAVE_NOTHING &&
aErrorCode == MEDIA_ERR_ABORTED) {
// https://html.spec.whatwg.org/multipage/embedded-content.html#media-data-processing-steps-list
// "If the media data fetching process is aborted by the user"
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
} else if (aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
} else {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
}
}
}
@@ -735,6 +740,13 @@ public:
mError = nullptr;
}
void NotifyPlayStarted()
{
if (CanOwnerPlayUnsupportedTypeMedia()) {
OpenUnsupportedMediaForOwner();
}
}
RefPtr<MediaError> mError;
private:
@@ -746,6 +758,42 @@ private:
aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED);
}
bool CanOwnerPlayUnsupportedTypeMedia() const
{
#if defined(MOZ_WIDGET_ANDROID)
// On Fennec, we will user an external app to open unsupported media types.
if (!Preferences::GetBool("media.openUnsupportedTypeWithExternalApp")) {
return false;
}
if (!mError) {
return false;
}
uint16_t errorCode = mError->Code();
if (errorCode != MEDIA_ERR_SRC_NOT_SUPPORTED) {
return false;
}
// If media doesn't start playing, we don't need to open it.
if (mOwner->Paused()) {
return false;
}
return true;
#endif
return false;
}
void OpenUnsupportedMediaForOwner() const
{
nsContentUtils::DispatchTrustedEvent(mOwner->OwnerDoc(),
static_cast<nsIContent*>(mOwner),
NS_LITERAL_STRING("OpenMediaWithExternalApp"),
true,
true);
}
// Media elememt's life cycle would be longer than error sink, so we use the
// raw pointer and this class would only be referenced by media element.
HTMLMediaElement* mOwner;
@@ -3100,6 +3148,8 @@ HTMLMediaElement::Play(ErrorResult& aRv)
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
OpenUnsupportedMediaWithExternalAppIfNeeded();
}
nsresult
@@ -3146,10 +3196,6 @@ HTMLMediaElement::PlayInternal()
UpdateSrcMediaStreamPlaying();
UpdateAudioChannelPlayingState();
// The check here is to handle the case that the media element starts playing
// after it loaded fail. eg. preload the data before playing.
OpenUnsupportedMediaWithExtenalAppIfNeeded();
// We should check audio channel playing state before dispatching any events,
// because we don't want to dispatch events for blocked media. For blocked
// media, the event would be pending until media is resumed.
@@ -3192,7 +3238,13 @@ NS_IMETHODIMP HTMLMediaElement::Play()
return NS_OK;
}
return PlayInternal();
nsresult rv = PlayInternal();
if (NS_FAILED(rv)) {
return rv;
}
OpenUnsupportedMediaWithExternalAppIfNeeded();
return NS_OK;
}
HTMLMediaElement::WakeLockBoolWrapper&
@@ -5689,6 +5741,14 @@ HTMLMediaElement::GetError() const
return mErrorSink->mError;
}
void
HTMLMediaElement::OpenUnsupportedMediaWithExternalAppIfNeeded() const
{
// Error sink would check the error state and other conditions to decide
// whether we can open unsupported type media with an external app.
mErrorSink->NotifyPlayStarted();
}
void HTMLMediaElement::GetCurrentSpec(nsCString& aString)
{
if (mLoadingSrc) {
@@ -6598,41 +6658,6 @@ HTMLMediaElement::MaybeNotifyMediaResumed(SuspendTypes aSuspend)
}));
}
bool
HTMLMediaElement::HaveFailedWithSourceNotSupportedError() const
{
const MediaError* error = mErrorSink->mError;
if (!error) {
return false;
}
uint16_t errorCode = error->Code();
return (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE &&
errorCode == MEDIA_ERR_SRC_NOT_SUPPORTED);
}
void
HTMLMediaElement::OpenUnsupportedMediaWithExtenalAppIfNeeded()
{
if (!Preferences::GetBool("media.openUnsupportedTypeWithExternalApp")) {
return;
}
if (!HaveFailedWithSourceNotSupportedError()) {
return;
}
// If media doesn't start playing, we don't need to open it.
if (mPaused) {
return;
}
nsContentUtils::DispatchTrustedEvent(OwnerDoc(), static_cast<nsIContent*>(this),
NS_LITERAL_STRING("OpenMediaWithExternalApp"),
true,
true);
}
bool
HTMLMediaElement::ShouldElementBePaused()
{