Bug 1894522 - Implement default seek handlers r=alwu,media-playback-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D219016
This commit is contained in:
@@ -63,7 +63,7 @@ interface MediaController : EventTarget {
|
|||||||
[ChromeOnly,Exposed=Window,HeaderFile="mozilla/dom/MediaControlService.h"]
|
[ChromeOnly,Exposed=Window,HeaderFile="mozilla/dom/MediaControlService.h"]
|
||||||
namespace MediaControlService {
|
namespace MediaControlService {
|
||||||
// This is used to generate fake media control keys event in testing.
|
// This is used to generate fake media control keys event in testing.
|
||||||
undefined generateMediaControlKey(MediaControlKey aKey);
|
undefined generateMediaControlKey(MediaControlKey aKey, optional double aSeekValue = 0.0);
|
||||||
|
|
||||||
// This is used to get the media metadata from the current main controller in
|
// This is used to get the media metadata from the current main controller in
|
||||||
// testing.
|
// testing.
|
||||||
|
|||||||
@@ -480,19 +480,43 @@ class HTMLMediaElement::MediaControlKeyListener final
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMediaKey(MediaControlKey aKey) override {
|
void HandleMediaKey(MediaControlKey aKey,
|
||||||
|
Maybe<SeekDetails> aDetails) override {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(IsStarted());
|
MOZ_ASSERT(IsStarted());
|
||||||
MEDIACONTROL_LOG("HandleEvent '%s'", GetEnumString(aKey).get());
|
MEDIACONTROL_LOG("HandleEvent '%s'", GetEnumString(aKey).get());
|
||||||
if (aKey == MediaControlKey::Play) {
|
switch (aKey) {
|
||||||
|
case MediaControlKey::Play:
|
||||||
Owner()->Play();
|
Owner()->Play();
|
||||||
} else if (aKey == MediaControlKey::Pause) {
|
break;
|
||||||
|
case MediaControlKey::Pause:
|
||||||
Owner()->Pause();
|
Owner()->Pause();
|
||||||
} else {
|
break;
|
||||||
MOZ_ASSERT(aKey == MediaControlKey::Stop,
|
case MediaControlKey::Stop:
|
||||||
"Not supported key for media element!");
|
|
||||||
Owner()->Pause();
|
Owner()->Pause();
|
||||||
StopIfNeeded();
|
StopIfNeeded();
|
||||||
|
break;
|
||||||
|
case MediaControlKey::Seekto:
|
||||||
|
MOZ_ASSERT(aDetails->mAbsolute);
|
||||||
|
if (aDetails->mAbsolute->mFastSeek) {
|
||||||
|
Owner()->FastSeek(aDetails->mAbsolute->mSeekTime, IgnoreErrors());
|
||||||
|
} else {
|
||||||
|
Owner()->SetCurrentTime(aDetails->mAbsolute->mSeekTime);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MediaControlKey::Seekforward:
|
||||||
|
MOZ_ASSERT(aDetails->mRelativeSeekOffset);
|
||||||
|
Owner()->SetCurrentTime(Owner()->CurrentTime() +
|
||||||
|
aDetails->mRelativeSeekOffset.value());
|
||||||
|
break;
|
||||||
|
case MediaControlKey::Seekbackward:
|
||||||
|
MOZ_ASSERT(aDetails->mRelativeSeekOffset);
|
||||||
|
Owner()->SetCurrentTime(Owner()->CurrentTime() -
|
||||||
|
aDetails->mRelativeSeekOffset.value());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"Unsupported media control key for media element!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -351,27 +351,30 @@ void ContentMediaController::RemoveReceiver(
|
|||||||
mReceivers.RemoveElement(aListener);
|
mReceivers.RemoveElement(aListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentMediaController::HandleMediaKey(MediaControlKey aKey) {
|
void ContentMediaController::HandleMediaKey(MediaControlKey aKey,
|
||||||
|
Maybe<SeekDetails> aDetails) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (mReceivers.IsEmpty()) {
|
if (mReceivers.IsEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG("Handle '%s' event, receiver num=%zu", GetEnumString(aKey).get(),
|
LOG("Handle '%s' event, receiver num=%zu", GetEnumString(aKey).get(),
|
||||||
mReceivers.Length());
|
mReceivers.Length());
|
||||||
// We have default handlers for play, pause and stop.
|
// We have default handlers for these actions
|
||||||
// https://w3c.github.io/mediasession/#ref-for-dom-mediasessionaction-play%E2%91%A3
|
// https://w3c.github.io/mediasession/#ref-for-dom-mediasessionaction-play%E2%91%A3
|
||||||
switch (aKey) {
|
switch (aKey) {
|
||||||
case MediaControlKey::Pause:
|
case MediaControlKey::Pause:
|
||||||
PauseOrStopMedia();
|
PauseOrStopMedia();
|
||||||
return;
|
return;
|
||||||
case MediaControlKey::Play:
|
case MediaControlKey::Play:
|
||||||
[[fallthrough]];
|
|
||||||
case MediaControlKey::Stop:
|
case MediaControlKey::Stop:
|
||||||
|
case MediaControlKey::Seekto:
|
||||||
|
case MediaControlKey::Seekforward:
|
||||||
|
case MediaControlKey::Seekbackward:
|
||||||
// When receiving `Stop`, the amount of receiver would vary during the
|
// When receiving `Stop`, the amount of receiver would vary during the
|
||||||
// iteration, so we use the backward iteration to avoid accessing the
|
// iteration, so we use the backward iteration to avoid accessing the
|
||||||
// index which is over the array length.
|
// index which is over the array length.
|
||||||
for (auto& receiver : Reversed(mReceivers)) {
|
for (auto& receiver : Reversed(mReceivers)) {
|
||||||
receiver->HandleMediaKey(aKey);
|
receiver->HandleMediaKey(aKey, aDetails);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ class ContentMediaControlKeyReceiver {
|
|||||||
static ContentMediaControlKeyReceiver* Get(BrowsingContext* aBC);
|
static ContentMediaControlKeyReceiver* Get(BrowsingContext* aBC);
|
||||||
|
|
||||||
// Use this method to handle the event from `ContentMediaAgent`.
|
// Use this method to handle the event from `ContentMediaAgent`.
|
||||||
virtual void HandleMediaKey(MediaControlKey aKey) = 0;
|
virtual void HandleMediaKey(MediaControlKey aKey,
|
||||||
|
Maybe<SeekDetails> aDetails = Nothing()) = 0;
|
||||||
|
|
||||||
virtual bool IsPlaying() const = 0;
|
virtual bool IsPlaying() const = 0;
|
||||||
};
|
};
|
||||||
@@ -94,7 +95,8 @@ class ContentMediaController final : public ContentMediaAgent,
|
|||||||
void RemoveReceiver(ContentMediaControlKeyReceiver* aListener) override;
|
void RemoveReceiver(ContentMediaControlKeyReceiver* aListener) override;
|
||||||
|
|
||||||
// ContentMediaControlKeyReceiver method
|
// ContentMediaControlKeyReceiver method
|
||||||
void HandleMediaKey(MediaControlKey aKey) override;
|
void HandleMediaKey(MediaControlKey aKey,
|
||||||
|
Maybe<SeekDetails> aDetails = Nothing()) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~ContentMediaController() = default;
|
~ContentMediaController() = default;
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ MediaSession* ContentPlaybackController::GetMediaSession() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ContentPlaybackController::NotifyContentMediaControlKeyReceiver(
|
void ContentPlaybackController::NotifyContentMediaControlKeyReceiver(
|
||||||
MediaControlKey aKey) {
|
MediaControlKey aKey, Maybe<SeekDetails> aDetails) {
|
||||||
if (RefPtr<ContentMediaControlKeyReceiver> receiver =
|
if (RefPtr<ContentMediaControlKeyReceiver> receiver =
|
||||||
ContentMediaControlKeyReceiver::Get(mBC)) {
|
ContentMediaControlKeyReceiver::Get(mBC)) {
|
||||||
LOG("Handle '%s' in default behavior for BC %" PRIu64,
|
LOG("Handle '%s' in default behavior for BC %" PRIu64,
|
||||||
GetEnumString(aKey).get(), mBC->Id());
|
GetEnumString(aKey).get(), mBC->Id());
|
||||||
receiver->HandleMediaKey(aKey);
|
receiver->HandleMediaKey(aKey, aDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +124,12 @@ void ContentPlaybackController::SeekBackward(double aSeekOffset) {
|
|||||||
MediaSessionActionDetails details;
|
MediaSessionActionDetails details;
|
||||||
details.mAction = MediaSessionAction::Seekbackward;
|
details.mAction = MediaSessionAction::Seekbackward;
|
||||||
details.mSeekOffset.Construct(aSeekOffset);
|
details.mSeekOffset.Construct(aSeekOffset);
|
||||||
|
RefPtr<MediaSession> session = GetMediaSession();
|
||||||
if (IsMediaSessionActionSupported(details.mAction)) {
|
if (IsMediaSessionActionSupported(details.mAction)) {
|
||||||
NotifyMediaSession(details);
|
NotifyMediaSession(details);
|
||||||
|
} else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
|
||||||
|
NotifyContentMediaControlKeyReceiver(MediaControlKey::Seekbackward,
|
||||||
|
Some(SeekDetails(aSeekOffset)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +137,12 @@ void ContentPlaybackController::SeekForward(double aSeekOffset) {
|
|||||||
MediaSessionActionDetails details;
|
MediaSessionActionDetails details;
|
||||||
details.mAction = MediaSessionAction::Seekforward;
|
details.mAction = MediaSessionAction::Seekforward;
|
||||||
details.mSeekOffset.Construct(aSeekOffset);
|
details.mSeekOffset.Construct(aSeekOffset);
|
||||||
|
RefPtr<MediaSession> session = GetMediaSession();
|
||||||
if (IsMediaSessionActionSupported(details.mAction)) {
|
if (IsMediaSessionActionSupported(details.mAction)) {
|
||||||
NotifyMediaSession(details);
|
NotifyMediaSession(details);
|
||||||
|
} else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
|
||||||
|
NotifyContentMediaControlKeyReceiver(MediaControlKey::Seekforward,
|
||||||
|
Some(SeekDetails(aSeekOffset)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +171,15 @@ void ContentPlaybackController::SeekTo(double aSeekTime, bool aFastSeek) {
|
|||||||
MediaSessionActionDetails details;
|
MediaSessionActionDetails details;
|
||||||
details.mAction = MediaSessionAction::Seekto;
|
details.mAction = MediaSessionAction::Seekto;
|
||||||
details.mSeekTime.Construct(aSeekTime);
|
details.mSeekTime.Construct(aSeekTime);
|
||||||
|
RefPtr<MediaSession> session = GetMediaSession();
|
||||||
if (aFastSeek) {
|
if (aFastSeek) {
|
||||||
details.mFastSeek.Construct(aFastSeek);
|
details.mFastSeek.Construct(aFastSeek);
|
||||||
}
|
}
|
||||||
if (IsMediaSessionActionSupported(details.mAction)) {
|
if (IsMediaSessionActionSupported(details.mAction)) {
|
||||||
NotifyMediaSession(details);
|
NotifyMediaSession(details);
|
||||||
|
} else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
|
||||||
|
NotifyContentMediaControlKeyReceiver(
|
||||||
|
MediaControlKey::Seekto, Some(SeekDetails(aSeekTime, aFastSeek)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ class MOZ_STACK_CLASS ContentPlaybackController {
|
|||||||
void SeekTo(double aSeekTime, bool aFastSeek);
|
void SeekTo(double aSeekTime, bool aFastSeek);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void NotifyContentMediaControlKeyReceiver(MediaControlKey aKey);
|
void NotifyContentMediaControlKeyReceiver(
|
||||||
|
MediaControlKey aKey, Maybe<SeekDetails> aDetails = Nothing());
|
||||||
void NotifyMediaSession(MediaSessionAction aAction);
|
void NotifyMediaSession(MediaSessionAction aAction);
|
||||||
void NotifyMediaSession(const MediaSessionActionDetails& aDetails);
|
void NotifyMediaSession(const MediaSessionActionDetails& aDetails);
|
||||||
void NotifyMediaSessionWhenActionIsSupported(MediaSessionAction aAction);
|
void NotifyMediaSessionWhenActionIsSupported(MediaSessionAction aAction);
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ RefPtr<MediaControlService> MediaControlService::GetService() {
|
|||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void MediaControlService::GenerateMediaControlKey(const GlobalObject& global,
|
void MediaControlService::GenerateMediaControlKey(const GlobalObject& global,
|
||||||
MediaControlKey aKey) {
|
MediaControlKey aKey,
|
||||||
|
double aSeekTime) {
|
||||||
RefPtr<MediaControlService> service = MediaControlService::GetService();
|
RefPtr<MediaControlService> service = MediaControlService::GetService();
|
||||||
if (service) {
|
if (service) {
|
||||||
service->GenerateTestMediaControlKey(aKey);
|
service->GenerateTestMediaControlKey(aKey, aSeekTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,20 +246,21 @@ MediaController* MediaControlService::GetMainController() const {
|
|||||||
return mControllerManager->GetMainController();
|
return mControllerManager->GetMainController();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaControlService::GenerateTestMediaControlKey(MediaControlKey aKey) {
|
void MediaControlService::GenerateTestMediaControlKey(MediaControlKey aKey,
|
||||||
|
double aSeekValue) {
|
||||||
if (!StaticPrefs::media_mediacontrol_testingevents_enabled()) {
|
if (!StaticPrefs::media_mediacontrol_testingevents_enabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Generate seek details when necessary
|
// Generate seek details when necessary
|
||||||
switch (aKey) {
|
switch (aKey) {
|
||||||
case MediaControlKey::Seekto:
|
case MediaControlKey::Seekto:
|
||||||
mMediaKeysHandler->OnActionPerformed(
|
mMediaKeysHandler->OnActionPerformed(MediaControlAction(
|
||||||
MediaControlAction(aKey, SeekDetails(0.0, false /* fast seek */)));
|
aKey, SeekDetails(aSeekValue, false /* fast seek */)));
|
||||||
break;
|
break;
|
||||||
case MediaControlKey::Seekbackward:
|
case MediaControlKey::Seekbackward:
|
||||||
case MediaControlKey::Seekforward:
|
case MediaControlKey::Seekforward:
|
||||||
mMediaKeysHandler->OnActionPerformed(
|
mMediaKeysHandler->OnActionPerformed(
|
||||||
MediaControlAction(aKey, SeekDetails(0.0)));
|
MediaControlAction(aKey, SeekDetails(aSeekValue)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mMediaKeysHandler->OnActionPerformed(MediaControlAction(aKey));
|
mMediaKeysHandler->OnActionPerformed(MediaControlAction(aKey));
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class MediaControlService final : public nsIObserver {
|
|||||||
|
|
||||||
// Currently these following static methods are only being used in testing.
|
// Currently these following static methods are only being used in testing.
|
||||||
static void GenerateMediaControlKey(const GlobalObject& global,
|
static void GenerateMediaControlKey(const GlobalObject& global,
|
||||||
MediaControlKey aKey);
|
MediaControlKey aKey, double aSeekTime);
|
||||||
static void GetCurrentActiveMediaMetadata(const GlobalObject& aGlobal,
|
static void GetCurrentActiveMediaMetadata(const GlobalObject& aGlobal,
|
||||||
MediaMetadataInit& aMetadata);
|
MediaMetadataInit& aMetadata);
|
||||||
static MediaSessionPlaybackState GetCurrentMediaSessionPlaybackState(
|
static MediaSessionPlaybackState GetCurrentMediaSessionPlaybackState(
|
||||||
@@ -70,7 +70,7 @@ class MediaControlService final : public nsIObserver {
|
|||||||
* generate fake media control key events, get the media metadata and playback
|
* generate fake media control key events, get the media metadata and playback
|
||||||
* state from the main controller.
|
* state from the main controller.
|
||||||
*/
|
*/
|
||||||
void GenerateTestMediaControlKey(MediaControlKey aKey);
|
void GenerateTestMediaControlKey(MediaControlKey aKey, double aSeekValue);
|
||||||
MediaMetadataBase GetMainControllerMediaMetadata() const;
|
MediaMetadataBase GetMainControllerMediaMetadata() const;
|
||||||
MediaSessionPlaybackState GetMainControllerPlaybackState() const;
|
MediaSessionPlaybackState GetMainControllerPlaybackState() const;
|
||||||
|
|
||||||
|
|||||||
@@ -75,9 +75,10 @@ void MediaController::GetMetadata(MediaMetadataInit& aMetadata,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const MediaControlKey sDefaultSupportedKeys[] = {
|
static const MediaControlKey sDefaultSupportedKeys[] = {
|
||||||
MediaControlKey::Focus, MediaControlKey::Play, MediaControlKey::Pause,
|
MediaControlKey::Focus, MediaControlKey::Play,
|
||||||
MediaControlKey::Playpause, MediaControlKey::Stop,
|
MediaControlKey::Pause, MediaControlKey::Playpause,
|
||||||
};
|
MediaControlKey::Stop, MediaControlKey::Seekto,
|
||||||
|
MediaControlKey::Seekforward, MediaControlKey::Seekbackward};
|
||||||
|
|
||||||
static void GetDefaultSupportedKeys(nsTArray<MediaControlKey>& aKeys) {
|
static void GetDefaultSupportedKeys(nsTArray<MediaControlKey>& aKeys) {
|
||||||
for (const auto& key : sDefaultSupportedKeys) {
|
for (const auto& key : sDefaultSupportedKeys) {
|
||||||
@@ -183,13 +184,23 @@ bool MediaController::IsActive() const { return mIsActive; };
|
|||||||
|
|
||||||
bool MediaController::ShouldPropagateActionToAllContexts(
|
bool MediaController::ShouldPropagateActionToAllContexts(
|
||||||
const MediaControlAction& aAction) const {
|
const MediaControlAction& aAction) const {
|
||||||
// These three actions have default action handler for each frame, so we
|
// These actions have default action handler for each frame, so we
|
||||||
// need to propagate to all contexts. We would handle default handlers in
|
// need to propagate to all contexts. We would handle default handlers in
|
||||||
// `ContentMediaController::HandleMediaKey`.
|
// `ContentMediaController::HandleMediaKey`.
|
||||||
return aAction.mKey.isSome() &&
|
if (aAction.mKey.isSome()) {
|
||||||
(aAction.mKey.value() == MediaControlKey::Play ||
|
switch (aAction.mKey.value()) {
|
||||||
aAction.mKey.value() == MediaControlKey::Pause ||
|
case MediaControlKey::Play:
|
||||||
aAction.mKey.value() == MediaControlKey::Stop);
|
case MediaControlKey::Pause:
|
||||||
|
case MediaControlKey::Stop:
|
||||||
|
case MediaControlKey::Seekto:
|
||||||
|
case MediaControlKey::Seekforward:
|
||||||
|
case MediaControlKey::Seekbackward:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaController::UpdateMediaControlActionToContentMediaIfNeeded(
|
void MediaController::UpdateMediaControlActionToContentMediaIfNeeded(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const videoId = "video";
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This test is used to check the scenario when we should use the customized
|
* This test is used to check the scenario when we should use the customized
|
||||||
* action handler and the the default action handler (play/pause/stop).
|
* action handler and the the default action handler (play/pause/stop/seekXXX).
|
||||||
* If a frame (DOM Window, it could be main frame or an iframe) has active media
|
* If a frame (DOM Window, it could be main frame or an iframe) has active media
|
||||||
* session, then it should use the customized action handler it it has one.
|
* session, then it should use the customized action handler it it has one.
|
||||||
* Otherwise, the default action handler should be used.
|
* Otherwise, the default action handler should be used.
|
||||||
@@ -47,6 +47,24 @@ add_task(async function triggerDefaultActionHandler() {
|
|||||||
info(`default action handler should pause media`);
|
info(`default action handler should pause media`);
|
||||||
await checkOrWaitUntilMediaPauses(tab, { videoId });
|
await checkOrWaitUntilMediaPauses(tab, { videoId });
|
||||||
|
|
||||||
|
info(`test 'seekto' action`);
|
||||||
|
await simulateMediaAction(tab, "seekto", 2.0);
|
||||||
|
|
||||||
|
info(`default action handler should set currentTime`);
|
||||||
|
await checkOrWaitUntilMediaSeek(tab, { videoId }, 2.0);
|
||||||
|
|
||||||
|
info(`test 'seekforward' action`);
|
||||||
|
await simulateMediaAction(tab, "seekforward", 1.0);
|
||||||
|
|
||||||
|
info(`default action handler should set currentTime`);
|
||||||
|
await checkOrWaitUntilMediaSeek(tab, { videoId }, 3.0);
|
||||||
|
|
||||||
|
info(`test 'seekbackward' action`);
|
||||||
|
await simulateMediaAction(tab, "seekbackward", 1.0);
|
||||||
|
|
||||||
|
info(`default action handler should set currentTime`);
|
||||||
|
await checkOrWaitUntilMediaSeek(tab, { videoId }, 2.0);
|
||||||
|
|
||||||
info(`test 'play' action`);
|
info(`test 'play' action`);
|
||||||
await simulateMediaAction(tab, "play");
|
await simulateMediaAction(tab, "play");
|
||||||
|
|
||||||
@@ -136,7 +154,7 @@ add_task(
|
|||||||
await pauseAllMedia(tab);
|
await pauseAllMedia(tab);
|
||||||
|
|
||||||
info(
|
info(
|
||||||
`press '${action}' would trigger default andler on main frame because it doesn't set action handler`
|
`press '${action}' would trigger default handler on main frame because it doesn't set action handler`
|
||||||
);
|
);
|
||||||
await simulateMediaAction(tab, action);
|
await simulateMediaAction(tab, action);
|
||||||
await checkOrWaitUntilMediaPlays(tab, { videoId });
|
await checkOrWaitUntilMediaPlays(tab, { videoId });
|
||||||
@@ -147,7 +165,7 @@ add_task(
|
|||||||
await checkOrWaitUntilMediaPlays(tab, { frameId });
|
await checkOrWaitUntilMediaPlays(tab, { frameId });
|
||||||
} else {
|
} else {
|
||||||
info(
|
info(
|
||||||
`press '${action}' would trigger default andler on main frame because it doesn't set action handler`
|
`press '${action}' would trigger default handler on main frame because it doesn't set action handler`
|
||||||
);
|
);
|
||||||
await simulateMediaAction(tab, action);
|
await simulateMediaAction(tab, action);
|
||||||
await checkOrWaitUntilMediaPauses(tab, { videoId });
|
await checkOrWaitUntilMediaPauses(tab, { videoId });
|
||||||
@@ -313,6 +331,30 @@ function checkOrWaitUntilMediaPlays(tab, { videoId, frameId }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkOrWaitUntilMediaSeek(tab, { videoId }, expectedTime) {
|
||||||
|
return SpecialPowers.spawn(
|
||||||
|
tab.linkedBrowser,
|
||||||
|
[videoId, expectedTime],
|
||||||
|
(videoId, expectedTime) => {
|
||||||
|
return new Promise(r => {
|
||||||
|
const video = content.document.getElementById(videoId);
|
||||||
|
ok(video.paused, "video is paused");
|
||||||
|
if (video.currentTime == expectedTime) {
|
||||||
|
ok(true, `media has been seeked`);
|
||||||
|
r();
|
||||||
|
} else {
|
||||||
|
info(`wait until media seeked`);
|
||||||
|
video.ontimeupdate = () => {
|
||||||
|
video.ontimeupdate = null;
|
||||||
|
is(video.currentTime, expectedTime, `correct time set`);
|
||||||
|
r();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function setActionHandler(tab, action, frameId = null) {
|
function setActionHandler(tab, action, frameId = null) {
|
||||||
return SpecialPowers.spawn(
|
return SpecialPowers.spawn(
|
||||||
tab.linkedBrowser,
|
tab.linkedBrowser,
|
||||||
@@ -366,12 +408,12 @@ async function waitUntilActionHandlerIsTriggered(tab, action, frameId = null) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function simulateMediaAction(tab, action) {
|
async function simulateMediaAction(tab, action, seekValue = 0.0) {
|
||||||
const controller = tab.linkedBrowser.browsingContext.mediaController;
|
const controller = tab.linkedBrowser.browsingContext.mediaController;
|
||||||
if (!controller.isActive) {
|
if (!controller.isActive) {
|
||||||
await new Promise(r => (controller.onactivated = r));
|
await new Promise(r => (controller.onactivated = r));
|
||||||
}
|
}
|
||||||
MediaControlService.generateMediaControlKey(action);
|
MediaControlService.generateMediaControlKey(action, seekValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadIframe(tab, iframeId, url) {
|
function loadIframe(tab, iframeId, url) {
|
||||||
|
|||||||
@@ -2,7 +2,16 @@ const PAGE_NON_AUTOPLAY =
|
|||||||
"https://example.com/browser/dom/media/mediacontrol/tests/browser/file_non_autoplay.html";
|
"https://example.com/browser/dom/media/mediacontrol/tests/browser/file_non_autoplay.html";
|
||||||
|
|
||||||
const testVideoId = "video";
|
const testVideoId = "video";
|
||||||
const sDefaultSupportedKeys = ["focus", "play", "pause", "playpause", "stop"];
|
const sDefaultSupportedKeys = [
|
||||||
|
"focus",
|
||||||
|
"play",
|
||||||
|
"pause",
|
||||||
|
"playpause",
|
||||||
|
"stop",
|
||||||
|
"seekto",
|
||||||
|
"seekforward",
|
||||||
|
"seekbackward",
|
||||||
|
];
|
||||||
|
|
||||||
add_task(async function setupTestingPref() {
|
add_task(async function setupTestingPref() {
|
||||||
await SpecialPowers.pushPrefEnv({
|
await SpecialPowers.pushPrefEnv({
|
||||||
@@ -80,12 +89,7 @@ add_task(async function testSettingActionsWhichAreNotDefaultKeys() {
|
|||||||
await playMedia(tab, testVideoId);
|
await playMedia(tab, testVideoId);
|
||||||
|
|
||||||
info(`create media session but not set any action handler`);
|
info(`create media session but not set any action handler`);
|
||||||
let nonDefaultActions = [
|
let nonDefaultActions = ["previoustrack", "nexttrack"];
|
||||||
"seekbackward",
|
|
||||||
"seekforward",
|
|
||||||
"previoustrack",
|
|
||||||
"nexttrack",
|
|
||||||
];
|
|
||||||
await setMediaSessionSupportedAction(tab, nonDefaultActions);
|
await setMediaSessionSupportedAction(tab, nonDefaultActions);
|
||||||
|
|
||||||
info(
|
info(
|
||||||
|
|||||||
2
tools/@types/lib.gecko.dom.d.ts
vendored
2
tools/@types/lib.gecko.dom.d.ts
vendored
@@ -24275,7 +24275,7 @@ declare namespace L10nOverlays {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare namespace MediaControlService {
|
declare namespace MediaControlService {
|
||||||
function generateMediaControlKey(aKey: MediaControlKey): void;
|
function generateMediaControlKey(aKey: MediaControlKey, aSeekValue?: double): void;
|
||||||
function getCurrentActiveMediaMetadata(): MediaMetadataInit;
|
function getCurrentActiveMediaMetadata(): MediaMetadataInit;
|
||||||
function getCurrentMediaSessionPlaybackState(): MediaSessionPlaybackState;
|
function getCurrentMediaSessionPlaybackState(): MediaSessionPlaybackState;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user