diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 9aed5f82b419..cde458d95cf4 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2804,8 +2804,7 @@ double HTMLMediaElement::CurrentTime() const { void HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv) { LOG(LogLevel::Debug, ("%p FastSeek(%f) called by JS", this, aTime)); - RefPtr tobeDropped = - Seek(aTime, SeekTarget::PrevSyncPoint, IgnoreErrors()); + Seek(aTime, SeekTarget::PrevSyncPoint, IgnoreErrors()); } already_AddRefed HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv) { @@ -2820,14 +2819,23 @@ already_AddRefed HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv) { } } - return Seek(CurrentTime(), SeekTarget::NextFrame, aRv); + Seek(CurrentTime(), SeekTarget::NextFrame, aRv); + if (aRv.Failed()) { + return nullptr; + } + + mSeekDOMPromise = CreateDOMPromise(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return do_AddRef(mSeekDOMPromise); } void HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv) { LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) called by JS", this, aCurrentTime)); - RefPtr tobeDropped = - Seek(aCurrentTime, SeekTarget::Accurate, IgnoreErrors()); + Seek(aCurrentTime, SeekTarget::Accurate, IgnoreErrors()); } /** @@ -2857,9 +2865,8 @@ static bool IsInRanges(TimeRanges& aRanges, double aValue, return false; } -already_AddRefed HTMLMediaElement::Seek(double aTime, - SeekTarget::Type aSeekType, - ErrorResult& aRv) { +void HTMLMediaElement::Seek(double aTime, SeekTarget::Type aSeekType, + ErrorResult& aRv) { // Note: Seek is called both by synchronous code that expects errors thrown in // aRv, as well as asynchronous code that expects a promise. Make sure all // synchronous errors are returned using aRv, not promise rejections. @@ -2871,11 +2878,6 @@ already_AddRefed HTMLMediaElement::Seek(double aTime, // https://html.spec.whatwg.org/multipage/media.html#dom-media-seek mShowPoster = false; - RefPtr promise = CreateDOMPromise(aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - // Detect if user has interacted with element by seeking so that // play will not be blocked when initiated by a script. if (EventStateManager::IsHandlingUserInput()) { @@ -2887,7 +2889,7 @@ already_AddRefed HTMLMediaElement::Seek(double aTime, if (mSrcAttrStream) { // do nothing since media streams have an empty Seekable range. aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; + return; } if (mPlayed && mCurrentPlayRangeStart != -1.0) { @@ -2906,28 +2908,28 @@ already_AddRefed HTMLMediaElement::Seek(double aTime, if (mReadyState == HAVE_NOTHING) { mDefaultPlaybackStartPosition = aTime; aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; + return; } if (!mDecoder) { // mDecoder must always be set in order to reach this point. NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder"); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; + return; } // Clamp the seek target to inside the seekable ranges. media::TimeIntervals seekableIntervals = mDecoder->GetSeekable(); if (seekableIntervals.IsInvalid()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; + return; } RefPtr seekable = new TimeRanges(ToSupports(OwnerDoc()), seekableIntervals); uint32_t length = seekable->Length(); if (length == 0) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; + return; } // If the position we want to seek to is not in a seekable range, we seek @@ -2988,11 +2990,6 @@ already_AddRefed HTMLMediaElement::Seek(double aTime, // We changed whether we're seeking so we need to AddRemoveSelfReference. AddRemoveSelfReference(); - - // Keep the DOM promise. - mSeekDOMPromise = promise; - - return promise.forget(); } double HTMLMediaElement::Duration() const { @@ -5266,6 +5263,24 @@ void HTMLMediaElement::SeekCompleted() { if (IsAudioTrackCurrentlySilent()) { UpdateAudioTrackSilenceRange(mIsAudioTrackAudible); } + + if (mSeekDOMPromise) { + mAbstractMainThread->Dispatch(NS_NewRunnableFunction( + __func__, [promise = std::move(mSeekDOMPromise)] { + promise->MaybeResolveWithUndefined(); + })); + } + MOZ_ASSERT(!mSeekDOMPromise); +} + +void HTMLMediaElement::SeekAborted() { + if (mSeekDOMPromise) { + mAbstractMainThread->Dispatch(NS_NewRunnableFunction( + __func__, [promise = std::move(mSeekDOMPromise)] { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + })); + } + MOZ_ASSERT(!mSeekDOMPromise); } void HTMLMediaElement::NotifySuspendedByCache(bool aSuspendedByCache) { @@ -7235,30 +7250,6 @@ bool HasDebuggerOrTabsPrivilege(JSContext* aCx, JSObject* aObj) { nsContentUtils::CallerHasPermission(aCx, nsGkAtoms::tabs); } -void HTMLMediaElement::AsyncResolveSeekDOMPromiseIfExists() { - MOZ_ASSERT(NS_IsMainThread()); - if (mSeekDOMPromise) { - RefPtr promise = mSeekDOMPromise.forget(); - nsCOMPtr r = NS_NewRunnableFunction( - "dom::HTMLMediaElement::AsyncResolveSeekDOMPromiseIfExists", - [promise]() { promise->MaybeResolveWithUndefined(); }); - mAbstractMainThread->Dispatch(r.forget()); - mSeekDOMPromise = nullptr; - } -} - -void HTMLMediaElement::AsyncRejectSeekDOMPromiseIfExists() { - MOZ_ASSERT(NS_IsMainThread()); - if (mSeekDOMPromise) { - RefPtr promise = mSeekDOMPromise.forget(); - nsCOMPtr r = NS_NewRunnableFunction( - "dom::HTMLMediaElement::AsyncRejectSeekDOMPromiseIfExists", - [promise]() { promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); }); - mAbstractMainThread->Dispatch(r.forget()); - mSeekDOMPromise = nullptr; - } -} - void HTMLMediaElement::ReportCanPlayTelemetry() { LOG(LogLevel::Debug, ("%s", __func__)); diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 1df98b4f37a1..e75b653c1205 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -200,6 +200,10 @@ class HTMLMediaElement : public nsGenericHTMLElement, // when the resource has completed seeking. void SeekCompleted() final; + // Called by the video decoder object, on the main thread, + // when the resource has aborted seeking. + void SeekAborted() final; + // Called by the media stream, on the main thread, when the download // has been suspended by the cache or because the element itself // asked the decoder to suspend the download. @@ -699,17 +703,6 @@ class HTMLMediaElement : public nsGenericHTMLElement, already_AddRefed CreateGMPCrashHelper() override; - // The promise resolving/rejection is queued as a "micro-task" which will be - // handled immediately after the current JS task and before any pending JS - // tasks. - // At the time we are going to resolve/reject a promise, the "seeking" event - // task should already be queued but might yet be processed, so we queue one - // more task to file the promise resolving/rejection micro-tasks - // asynchronously to make sure that the micro-tasks are processed after the - // "seeking" event task. - void AsyncResolveSeekDOMPromiseIfExists() override; - void AsyncRejectSeekDOMPromiseIfExists() override; - nsISerialEventTarget* MainThreadEventTarget() { return mMainThreadEventTarget; } @@ -1185,8 +1178,7 @@ class HTMLMediaElement : public nsGenericHTMLElement, // seek target, or PrevSyncPoint if a quicker but less precise seek is // desired, and we'll seek to the sync point (keyframe and/or start of the // next block of audio samples) preceeding seek target. - already_AddRefed Seek(double aTime, SeekTarget::Type aSeekType, - ErrorResult& aRv); + void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv); // Update the audio channel playing state void UpdateAudioChannelPlayingState(bool aForcePlaying = false); diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index d73d5d431ce2..8e3b52c05b80 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -627,7 +627,6 @@ void MediaDecoder::DiscardOngoingSeekIfExists() { MOZ_ASSERT(NS_IsMainThread()); AbstractThread::AutoEnter context(AbstractMainThread()); mSeekRequest.DisconnectIfExists(); - GetOwner()->AsyncRejectSeekDOMPromiseIfExists(); } void MediaDecoder::CallSeek(const SeekTarget& aTarget) { @@ -836,14 +835,14 @@ void MediaDecoder::OnSeekResolved() { mSeekRequest.Complete(); GetOwner()->SeekCompleted(); - GetOwner()->AsyncResolveSeekDOMPromiseIfExists(); } void MediaDecoder::OnSeekRejected() { MOZ_ASSERT(NS_IsMainThread()); mSeekRequest.Complete(); mLogicallySeeking = false; - GetOwner()->AsyncRejectSeekDOMPromiseIfExists(); + + GetOwner()->SeekAborted(); } void MediaDecoder::SeekingStarted() { diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 76874a91c493..d5f2c2fa4753 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -497,7 +497,7 @@ class MediaDecoder : public DecoderDoctorLifeLogger { protected: void NotifyReaderDataArrived(); void DiscardOngoingSeekIfExists(); - virtual void CallSeek(const SeekTarget& aTarget); + void CallSeek(const SeekTarget& aTarget); // Called by MediaResource when the principal of the resource has // changed. Called on main thread only. diff --git a/dom/media/MediaDecoderOwner.h b/dom/media/MediaDecoderOwner.h index 42f8d4b21aeb..48e14c36c5f6 100644 --- a/dom/media/MediaDecoderOwner.h +++ b/dom/media/MediaDecoderOwner.h @@ -94,6 +94,10 @@ class MediaDecoderOwner { // when the resource has completed seeking. virtual void SeekCompleted() = 0; + // Called by the video decoder object, on the main thread, + // when the resource has aborted seeking. + virtual void SeekAborted() = 0; + // Called by the media stream, on the main thread, when the download // has been suspended by the cache or because the element itself // asked the decoder to suspend the download. @@ -144,12 +148,6 @@ class MediaDecoderOwner { // owner's track list. virtual void RemoveMediaTracks() = 0; - // Called by the media decoder to notify the owner to resolve a seek promise. - virtual void AsyncResolveSeekDOMPromiseIfExists() = 0; - - // Called by the media decoder to notify the owner to reject a seek promise. - virtual void AsyncRejectSeekDOMPromiseIfExists() = 0; - // Notified by the decoder that a decryption key is required before emitting // further output. virtual void NotifyWaitingForKey() {} diff --git a/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini b/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini deleted file mode 100644 index 52da55863615..000000000000 --- a/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini +++ /dev/null @@ -1,7 +0,0 @@ -[mediasource-seek-beyond-duration.html] - disabled: - if (os == "android") and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1499003 - if (os == "android") and e10s: bug 1550895 (frequently fails on geckoview) - if (os == "android") and debug: https://bugzilla.mozilla.org/show_bug.cgi?id=1560524 - expected: ERROR - diff --git a/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini b/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini deleted file mode 100644 index f329f6882c0d..000000000000 --- a/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini +++ /dev/null @@ -1,7 +0,0 @@ -[mediasource-seek-during-pending-seek.html] - disabled: - if (os == "android") and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1499003 - expected: ERROR - [Test seeking to a new location during a pending seek.] - disabled: - if (os == "android"): Frequently failing on geckoview (Bug 1563134), Bug 1553057