зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1627999 - part7 : add test. r=bryce
This patch will do : - add test cases - introduce the test-only notification `media-displayed-metadata-changed` when the event source updates its metadata The advantage of doing so : - increase test coverage Differential Revision: https://phabricator.services.mozilla.com/D72501
This commit is contained in:
Родитель
fa7fe1ff9d
Коммит
eac28634c9
|
@ -126,6 +126,12 @@ void MediaControlKeysManager::SetMediaMetadata(
|
|||
NS_ConvertUTF16toUTF8(mMetadata.mTitle).get(),
|
||||
NS_ConvertUTF16toUTF8(mMetadata.mArtist).get(),
|
||||
NS_ConvertUTF16toUTF8(mMetadata.mAlbum).get());
|
||||
if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
|
||||
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
|
||||
obs->NotifyObservers(nullptr, "media-displayed-metadata-changed",
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
tags = mediacontrol
|
||||
support-files =
|
||||
file_autoplay.html
|
||||
file_main_frame_with_multiple_child_session_frames.html
|
||||
file_media_session_page.html
|
||||
file_muted_autoplay.html
|
||||
file_non_autoplay.html
|
||||
file_non_eligible_media.html
|
||||
|
@ -13,6 +15,7 @@ support-files =
|
|||
../../../../toolkit/content/tests/browser/silentAudioTrack.webm
|
||||
|
||||
[browser_audio_focus_management.js]
|
||||
[browser_media_control_audio_focus_within_a_page.js]
|
||||
[browser_media_control_captured_audio.js]
|
||||
[browser_media_control_metadata.js]
|
||||
[browser_media_control_keys_event.js]
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
/* eslint-disable no-undef */
|
||||
const mainPageURL =
|
||||
"https://example.com/browser/dom/media/mediacontrol/tests/file_main_frame_with_multiple_child_session_frames.html";
|
||||
const frameURL =
|
||||
"https://example.com/browser/dom/media/mediacontrol/tests/file_media_session_page.html";
|
||||
|
||||
const frame1 = "frame1";
|
||||
const frame2 = "frame2";
|
||||
|
||||
add_task(async function setupTestingPref() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["media.mediacontrol.testingevents.enabled", true],
|
||||
["dom.media.mediasession.enabled", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This test is used to check the behaviors when we play media from different
|
||||
* frames. When a page contains multiple frames, if those frames are using the
|
||||
* media session and set the metadata, then we have to know which frame owns the
|
||||
* audio focus that would be the last tab playing media. When the frame owns
|
||||
* audio focus, it means its metadata would be displayed on the virtual control
|
||||
* interface if it has a media session.
|
||||
*/
|
||||
add_task(async function testAudioFocusChangesAmongMultipleFrames() {
|
||||
/**
|
||||
* Play the media from the main frame, so it would own the audio focus and
|
||||
* its metadata should be shown on the virtual control interface. As the main
|
||||
* frame doesn't use media session, the current metadata would be the default
|
||||
* metadata.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await playAndWaitUntilMetadataChanged(tab);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Play media for frame1, so the audio focus switches to frame1 because it's
|
||||
* the last tab playing media and frame1's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadata = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Play media for frame2, so the audio focus switches to frame2 because it's
|
||||
* the last tab playing media and frame2's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame2, frameURL);
|
||||
metadata = await setMetadataAndGetReturnResult(tab, frame2);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame2);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function testAudioFocusChangesAfterPausingAudioFocusOwner() {
|
||||
/**
|
||||
* Play the media from the main frame, so it would own the audio focus and
|
||||
* its metadata should be shown on the virtual control interface. As the main
|
||||
* frame doesn't use media session, the current metadata would be the default
|
||||
* metadata.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await playAndWaitUntilMetadataChanged(tab);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Play media for frame1, so the audio focus switches to frame1 because it's
|
||||
* the last tab playing media and frame1's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadata = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Pause media for frame1, so the audio focus switches back to the main frame
|
||||
* which is still playing media.
|
||||
*/
|
||||
await pauseAndWaitUntilMetadataChangedFrom(tab, frame1);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function testAudioFocusUnchangesAfterPausingAudioFocusOwner() {
|
||||
/**
|
||||
* Play the media from the main frame, so it would own the audio focus and
|
||||
* its metadata should be shown on the virtual control interface. As the main
|
||||
* frame doesn't use media session, the current metadata would be the default
|
||||
* metadata.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await playAndWaitUntilMetadataChanged(tab);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Play media for frame1, so the audio focus switches to frame1 because it's
|
||||
* the last tab playing media and frame1's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadata = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Pause main frame's media first. When pausing frame1's media, there are not
|
||||
* other frames playing media, so frame1 still owns the audio focus and its
|
||||
* metadata should be displayed.
|
||||
*/
|
||||
await pauseMediaFrom(tab);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function testSwitchAudioFocusToMainFrameAfterRemovingAudioFocusOwner() {
|
||||
/**
|
||||
* Play the media from the main frame, so it would own the audio focus and
|
||||
* its metadata should be displayed on the virtual control interface. As the
|
||||
* main frame doesn't use media session, the current metadata would be the
|
||||
* default metadata.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await playAndWaitUntilMetadataChanged(tab);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Play media for frame1, so the audio focus switches to frame1 because it's
|
||||
* the last tab playing media and frame1's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadata = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Remove frame1, the audio focus would switch to the main frame which
|
||||
* metadata should be displayed.
|
||||
*/
|
||||
await Promise.all([
|
||||
waitUntilDisplayedMetadataChanged(),
|
||||
removeFrame(tab, frame1),
|
||||
]);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function testSwitchAudioFocusToIframeAfterRemovingAudioFocusOwner() {
|
||||
/**
|
||||
* Play media for frame1, so frame1 owns the audio focus and frame1's metadata
|
||||
* should be displayed.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadataFrame1 = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadataFrame1);
|
||||
|
||||
/**
|
||||
* Play media for frame2, so the audio focus switches to frame2 because it's
|
||||
* the last tab playing media and frame2's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame2, frameURL);
|
||||
let metadataFrame2 = await setMetadataAndGetReturnResult(tab, frame2);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame2);
|
||||
isCurrentMetadataEqualTo(metadataFrame2);
|
||||
|
||||
/**
|
||||
* Remove frame2, the audio focus would switch to frame1 which metadata should
|
||||
* be displayed.
|
||||
*/
|
||||
await Promise.all([
|
||||
waitUntilDisplayedMetadataChanged(),
|
||||
removeFrame(tab, frame2),
|
||||
]);
|
||||
isCurrentMetadataEqualTo(metadataFrame1);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function testNoAudioFocusAfterRemovingAudioFocusOwner() {
|
||||
/**
|
||||
* Play the media from the main frame, so it would own the audio focus and
|
||||
* its metadata should be shown on the virtual control interface. As the main
|
||||
* frame doesn't use media session, the current metadata would be the default
|
||||
* metadata.
|
||||
*/
|
||||
const tab = await createTabAndLoad(mainPageURL);
|
||||
await playAndWaitUntilMetadataChanged(tab);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Play media for frame1, so the audio focus switches to frame1 because it's
|
||||
* the last tab playing media and frame1's metadata should be displayed.
|
||||
*/
|
||||
await loadPageForFrame(tab, frame1, frameURL);
|
||||
let metadata = await setMetadataAndGetReturnResult(tab, frame1);
|
||||
await playAndWaitUntilMetadataChanged(tab, frame1);
|
||||
isCurrentMetadataEqualTo(metadata);
|
||||
|
||||
/**
|
||||
* Pause media in main frame and then remove frame1. As the frame which owns
|
||||
* the audio focus is removed and no frame is still playing media, the current
|
||||
* metadata would be the default metadata.
|
||||
*/
|
||||
await pauseMediaFrom(tab);
|
||||
await Promise.all([
|
||||
waitUntilDisplayedMetadataChanged(),
|
||||
removeFrame(tab, frame1),
|
||||
]);
|
||||
await isUsingDefaultMetadata(tab);
|
||||
|
||||
/**
|
||||
* Remove tab and end test.
|
||||
*/
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* The following are helper functions.
|
||||
*/
|
||||
function loadPageForFrame(tab, frameId, pageUrl) {
|
||||
info(`start to load page for ${frameId}`);
|
||||
return SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[frameId, pageUrl],
|
||||
async (id, url) => {
|
||||
const iframe = content.document.getElementById(id);
|
||||
if (!iframe) {
|
||||
ok(false, `can not get iframe '${id}'`);
|
||||
}
|
||||
iframe.src = url;
|
||||
await new Promise(r => (iframe.onload = r));
|
||||
// Set the document title that would be used as the value for properties
|
||||
// in frame's medadata.
|
||||
iframe.contentDocument.title = id;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function playMediaFrom(tab, frameId = undefined) {
|
||||
return SpecialPowers.spawn(tab.linkedBrowser, [frameId], id => {
|
||||
if (id == undefined) {
|
||||
info(`start to play media from main frame`);
|
||||
const video = content.document.getElementById("video");
|
||||
if (!video) {
|
||||
ok(false, `can't get the media element!`);
|
||||
}
|
||||
return video.play();
|
||||
}
|
||||
|
||||
info(`start to play media from ${id}`);
|
||||
const iframe = content.document.getElementById(id);
|
||||
if (!iframe) {
|
||||
ok(false, `can not get ${id}`);
|
||||
}
|
||||
iframe.contentWindow.postMessage("play", "*");
|
||||
return new Promise(r => {
|
||||
content.onmessage = event => {
|
||||
is(event.data, "played", `media started playing in ${id}`);
|
||||
r();
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function playAndWaitUntilMetadataChanged(tab, frameId = undefined) {
|
||||
const metadataChanged = waitUntilDisplayedMetadataChanged();
|
||||
return Promise.all([metadataChanged, playMediaFrom(tab, frameId)]);
|
||||
}
|
||||
|
||||
function pauseMediaFrom(tab, frameId = undefined) {
|
||||
return SpecialPowers.spawn(tab.linkedBrowser, [frameId], id => {
|
||||
if (id == undefined) {
|
||||
info(`start to pause media from in frame`);
|
||||
const video = content.document.getElementById("video");
|
||||
if (!video) {
|
||||
ok(false, `can't get the media element!`);
|
||||
}
|
||||
return video.pause();
|
||||
}
|
||||
|
||||
info(`start to pause media in ${id}`);
|
||||
const iframe = content.document.getElementById(id);
|
||||
if (!iframe) {
|
||||
ok(false, `can not get ${id}`);
|
||||
}
|
||||
iframe.contentWindow.postMessage("pause", "*");
|
||||
return new Promise(r => {
|
||||
content.onmessage = event => {
|
||||
is(event.data, "paused", `media paused in ${id}`);
|
||||
r();
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pauseAndWaitUntilMetadataChangedFrom(tab, frameId = undefined) {
|
||||
const metadataChanged = waitUntilDisplayedMetadataChanged();
|
||||
return Promise.all([metadataChanged, pauseMediaFrom(tab, frameId)]);
|
||||
}
|
||||
|
||||
function setMetadataAndGetReturnResult(tab, frameId) {
|
||||
info(`start to set metadata for ${frameId}`);
|
||||
return SpecialPowers.spawn(tab.linkedBrowser, [frameId], id => {
|
||||
const iframe = content.document.getElementById(id);
|
||||
if (!iframe) {
|
||||
ok(false, `can not get ${id}`);
|
||||
}
|
||||
iframe.contentWindow.postMessage("setMetadata", "*");
|
||||
info(`wait until we get metadata for ${id}`);
|
||||
return new Promise(r => {
|
||||
content.onmessage = event => {
|
||||
ok(
|
||||
event.data.title && event.data.artist && event.data.album,
|
||||
"correct return format"
|
||||
);
|
||||
r(event.data);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeFrame(tab, frameId) {
|
||||
info(`remove ${frameId}`);
|
||||
return SpecialPowers.spawn(tab.linkedBrowser, [frameId], id => {
|
||||
const iframe = content.document.getElementById(id);
|
||||
if (!iframe) {
|
||||
ok(false, `can not get ${id}`);
|
||||
}
|
||||
content.document.body.removeChild(iframe);
|
||||
});
|
||||
}
|
|
@ -335,30 +335,6 @@ add_task(async function testMetadataAfterTabNavigation() {
|
|||
/**
|
||||
* The following are helper functions.
|
||||
*/
|
||||
async function isUsingDefaultMetadata(tab, options = {}) {
|
||||
let metadata = ChromeUtils.getCurrentActiveMediaMetadata();
|
||||
if (options.isPrivateBrowsing) {
|
||||
is(
|
||||
metadata.title,
|
||||
"Firefox is playing media",
|
||||
"Using generic title to not expose sensitive information"
|
||||
);
|
||||
} else {
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [metadata.title], title => {
|
||||
is(
|
||||
title,
|
||||
content.document.title,
|
||||
"Using website title as a default title"
|
||||
);
|
||||
});
|
||||
}
|
||||
is(metadata.artwork.length, 1, "Default metada contains one artwork");
|
||||
ok(
|
||||
metadata.artwork[0].src.includes(defaultFaviconName),
|
||||
"Using default favicon as a default art work"
|
||||
);
|
||||
}
|
||||
|
||||
function setMediaMetadata(tab, metadata) {
|
||||
const promise = SpecialPowers.spawn(tab.linkedBrowser, [metadata], data => {
|
||||
content.navigator.mediaSession.metadata = new content.MediaMetadata(data);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Media control page with multiple iframes which contain media session</title>
|
||||
</head>
|
||||
<body>
|
||||
<video id="video" src="gizmo.mp4" loop></video>
|
||||
<iframe id="frame1"></iframe>
|
||||
<iframe id="frame2"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<video id="video" src="gizmo.mp4" loop></video>
|
||||
<script type="text/javascript">
|
||||
|
||||
const video = document.getElementById("video");
|
||||
const w = window.opener || window.parent;
|
||||
|
||||
window.onmessage = async event => {
|
||||
if (event.data == "play") {
|
||||
await video.play();
|
||||
w.postMessage("played", "*");
|
||||
} else if (event.data == "pause") {
|
||||
video.pause();
|
||||
w.postMessage("paused", "*");
|
||||
} else if (event.data == "setMetadata") {
|
||||
const metadata = {
|
||||
title: document.title,
|
||||
artist: document.title,
|
||||
album: document.title,
|
||||
artwork: [{ src: document.title, sizes: "128x128", type: "image/jpeg" }],
|
||||
};
|
||||
navigator.mediaSession.metadata = new window.MediaMetadata(metadata);
|
||||
w.postMessage(metadata, "*");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -214,6 +214,35 @@ function isCurrentMetadataEqualTo(metadata) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given tab is using the default metadata. If the tab is being
|
||||
* used in the private browsing mode, `isPrivateBrowsing` should be definded in
|
||||
* the `options`.
|
||||
*/
|
||||
async function isUsingDefaultMetadata(tab, options = {}) {
|
||||
let metadata = ChromeUtils.getCurrentActiveMediaMetadata();
|
||||
if (options.isPrivateBrowsing) {
|
||||
is(
|
||||
metadata.title,
|
||||
"Firefox is playing media",
|
||||
"Using generic title to not expose sensitive information"
|
||||
);
|
||||
} else {
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [metadata.title], title => {
|
||||
is(
|
||||
title,
|
||||
content.document.title,
|
||||
"Using website title as a default title"
|
||||
);
|
||||
});
|
||||
}
|
||||
is(metadata.artwork.length, 1, "Default metada contains one artwork");
|
||||
ok(
|
||||
metadata.artwork[0].src.includes("defaultFavicon.svg"),
|
||||
"Using default favicon as a default art work"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the main media controller changes its playback state, we would
|
||||
* observe that by listening for `media-displayed-playback-changed`
|
||||
|
@ -226,6 +255,18 @@ function waitUntilDisplayedPlaybackChanged() {
|
|||
return BrowserUtils.promiseObserved("media-displayed-playback-changed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the metadata that would be displayed on the virtual control
|
||||
* interface changes. we would observe that by listening for
|
||||
* `media-displayed-metadata-changed` notification.
|
||||
*
|
||||
* @return {Promise}
|
||||
* Resolve when observing `media-displayed-metadata-changed`
|
||||
*/
|
||||
function waitUntilDisplayedMetadataChanged() {
|
||||
return BrowserUtils.promiseObserved("media-displayed-metadata-changed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the main media controller has been changed, we would observe that
|
||||
* by listening for the `main-media-controller-changed` notification.
|
||||
|
@ -238,9 +279,12 @@ function waitUntilMainMediaControllerChanged() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Wait until the main session controller changes its metadata, we would observe
|
||||
* that by listening for `media-session-controller-metadata-changed`
|
||||
* notification.
|
||||
* Wait until any media controller updates its metadata even if it's not the
|
||||
* main controller. The difference between this function and
|
||||
* `waitUntilDisplayedMetadataChanged()` is that the changed metadata might come
|
||||
* from non-main controller so it won't be show on the virtual control
|
||||
* interface. we would observe that by listening for
|
||||
* `media-session-controller-metadata-changed` notification.
|
||||
*
|
||||
* @return {Promise}
|
||||
* Resolve when observing `media-session-controller-metadata-changed`
|
||||
|
|
Загрузка…
Ссылка в новой задаче