From 7eec59aab284c2f9c4dfb2ce9cb130519882144f Mon Sep 17 00:00:00 2001 From: alwu Date: Fri, 6 Mar 2020 17:51:48 +0000 Subject: [PATCH] Bug 1617033 - part7 : modify tests to allow simulating media keys would always happen after controller's playback changes. r=chunmin Whenever pressing the media control keys, it would not only change the media playback state, but also change the playback state of the media controller. Therefore, we want to ensure simulating events always happen after media controller changes its playback state. In addition, for `play/pause` key, it would check controller's playback state to decide if we should file `play` event or `pause` event, so we definitely have to do that only after the controller changes its playback state. Changing a file to non-autoplay to prevent missing the `main-media-controller-playback-changed` notification, because we can control when to start media and ensure we have already create a listener for that event. For non-browser test, we have no way to listen to that notification, which could only be observed in the chrome process, so we do a hack to listen `timeupdate` several times to wait and hope the controller has been created and changed its state in the chrome process. Differential Revision: https://phabricator.services.mozilla.com/D63926 --HG-- extra : moz-landing-system : lando --- .../mediacontrol/MediaControlService.cpp | 6 ++ .../tests/browser_media_control_keys_event.js | 67 ++++++++++++++----- dom/media/mediacontrol/tests/head.js | 4 ++ ...ctive_mediasession_within_page_window.html | 16 ++++- .../file_trigger_actionhanlder_window.html | 16 ++++- 5 files changed, 87 insertions(+), 22 deletions(-) diff --git a/dom/media/mediacontrol/MediaControlService.cpp b/dom/media/mediacontrol/MediaControlService.cpp index cbc5432ccec1..1aa473666e24 100644 --- a/dom/media/mediacontrol/MediaControlService.cpp +++ b/dom/media/mediacontrol/MediaControlService.cpp @@ -190,6 +190,12 @@ void MediaControlService::ControllerManager::ControllerPlaybackStateChanged( PlaybackState aState) { MOZ_ASSERT(NS_IsMainThread()); mSource->SetPlaybackState(aState); + if (StaticPrefs::media_mediacontrol_testingevents_enabled()) { + if (nsCOMPtr obs = services::GetObserverService()) { + obs->NotifyObservers(nullptr, "main-media-controller-playback-changed", + nullptr); + } + } } void MediaControlService::ControllerManager::ControllerMetadataChanged( diff --git a/dom/media/mediacontrol/tests/browser_media_control_keys_event.js b/dom/media/mediacontrol/tests/browser_media_control_keys_event.js index ab41898ded71..5f23583e8ec2 100644 --- a/dom/media/mediacontrol/tests/browser_media_control_keys_event.js +++ b/dom/media/mediacontrol/tests/browser_media_control_keys_event.js @@ -1,7 +1,7 @@ /* eslint-disable no-undef */ -const PAGE_AUTOPLAY = - "https://example.com/browser/dom/media/mediacontrol/tests/file_autoplay.html"; -const testVideoId = "autoplay"; +const PAGE = + "https://example.com/browser/dom/media/mediacontrol/tests/file_non_autoplay.html"; +const testVideoId = "video"; /** * This test is used to generate platform-independent media control keys event @@ -15,39 +15,74 @@ add_task(async function setupTestingPref() { }); add_task(async function testPlayPauseAndStop() { - info(`open autoplay media`); - const tab = await createTabAndLoad(PAGE_AUTOPLAY); - await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId); + info(`open page and start media`); + const tab = await createTabAndLoad(PAGE); + await playMedia(tab); info(`pressing 'pause' key`); ChromeUtils.generateMediaControlKeysTestEvent("pause"); - await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId); + await waitUntilPlaybackStops(tab); info(`pressing 'play' key`); ChromeUtils.generateMediaControlKeysTestEvent("play"); - await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId); + await waitUntilPlaybackStarts(tab); info(`pressing 'stop' key`); ChromeUtils.generateMediaControlKeysTestEvent("stop"); - await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId); + await waitUntilPlaybackStops(tab); info(`remove tab`); await BrowserTestUtils.removeTab(tab); }); add_task(async function testPlayPause() { - info(`open autoplay media`); - const tab = await createTabAndLoad(PAGE_AUTOPLAY); - await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId); + info(`open page and start media`); + const tab = await createTabAndLoad(PAGE); + await playMedia(tab); - info(`pressing 'playPause' key`); + info(`pressing 'playPause' key, media should stop`); ChromeUtils.generateMediaControlKeysTestEvent("playPause"); - await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId); + await waitUntilPlaybackStops(tab); - info(`pressing 'playPause' key`); + info(`pressing 'playPause' key, media should start`); ChromeUtils.generateMediaControlKeysTestEvent("playPause"); - await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId); + await waitUntilPlaybackStarts(tab); info(`remove tab`); await BrowserTestUtils.removeTab(tab); }); + +/** + * The following are helper functions. + */ +function waitUntilPlaybackStarts(tab) { + return Promise.all([ + checkOrWaitUntilMediaStartedPlaying(tab, testVideoId), + waitUntilMainMediaControllerPlaybackChanged(), + ]); +} + +function waitUntilPlaybackStops(tab) { + return Promise.all([ + checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId), + waitUntilMainMediaControllerPlaybackChanged(), + ]); +} + +function playMedia(tab) { + const playPromise = SpecialPowers.spawn( + tab.linkedBrowser, + [testVideoId], + Id => { + const video = content.document.getElementById(Id); + if (!video) { + ok(false, `can't get the media element!`); + } + return video.play(); + } + ); + return Promise.all([ + playPromise, + waitUntilMainMediaControllerPlaybackChanged(), + ]); +} diff --git a/dom/media/mediacontrol/tests/head.js b/dom/media/mediacontrol/tests/head.js index 8d246c0a7aa8..5e87e1005126 100644 --- a/dom/media/mediacontrol/tests/head.js +++ b/dom/media/mediacontrol/tests/head.js @@ -48,3 +48,7 @@ function checkOrWaitUntilMediaStoppedPlaying(tab, elementId) { }); }); } + +function waitUntilMainMediaControllerPlaybackChanged() { + return BrowserUtils.promiseObserved("main-media-controller-playback-changed"); +} diff --git a/dom/media/mediasession/test/file_active_mediasession_within_page_window.html b/dom/media/mediasession/test/file_active_mediasession_within_page_window.html index ddb7d0c91170..5d22f1b1fc1f 100644 --- a/dom/media/mediasession/test/file_active_mediasession_within_page_window.html +++ b/dom/media/mediasession/test/file_active_mediasession_within_page_window.html @@ -47,11 +47,21 @@ nextWindowMessage().then( /** * The following are helper functions */ -function startMediaPlayback() { +async function startMediaPlayback() { info(`wait until media starts playing`); const video = document.getElementById("testVideo"); - video.play(); - return new Promise(r => video.onplaying = r); + await video.play(); + // As we can't observe `main-media-controller-playback-changed` notification, + // which can only be observed in the chrome process. Therefore, we use a + // workaround instead which is to wait for a while to ensure that the + // controller has already been created in the chrome process. + let timeupdatecount = 0; + await new Promise(r => video.ontimeupdate = () => { + if (++timeupdatecount == 3) { + video.ontimeupdate = null; + r(); + } + }); } function createSessionInMainFrame() { diff --git a/dom/media/mediasession/test/file_trigger_actionhanlder_window.html b/dom/media/mediasession/test/file_trigger_actionhanlder_window.html index 89e052fa9f70..9df646c493f4 100644 --- a/dom/media/mediasession/test/file_trigger_actionhanlder_window.html +++ b/dom/media/mediasession/test/file_trigger_actionhanlder_window.html @@ -29,11 +29,21 @@ nextWindowMessage().then( /** * The following are helper functions */ -function startMediaPlayback() { +async function startMediaPlayback() { info(`wait until media starts playing`); const video = document.getElementById("testVideo"); - video.play(); - return new Promise(r => video.onplaying = r); + await video.play(); + // As we can't observe `main-media-controller-playback-changed` notification, + // which can only be observed in the chrome process. Therefore, we use a + // workaround instead which is to wait for a while to ensure that the + // controller has already been created in the chrome process. + let timeupdatecount = 0; + await new Promise(r => video.ontimeupdate = () => { + if (++timeupdatecount == 3) { + video.ontimeupdate = null; + r(); + } + }); } async function createSession({shouldCreateFrom, origin}) {