Feat: basic media control

Fixed dragging undefined tabs
This commit is contained in:
Slowlife01
2025-03-09 09:53:07 +07:00
parent 3df1973ac9
commit e6552c8dda
14 changed files with 638 additions and 3 deletions

View File

@@ -37,6 +37,8 @@ var gZenUIManager = {
window.addEventListener('TabClose', this.onTabClose.bind(this));
this.tabsWrapper.addEventListener('scroll', this.saveScrollbarState.bind(this));
gZenMediaController.init();
},
updateTabsToolbar() {
@@ -768,3 +770,252 @@ var gZenVerticalTabsManager = {
this._tabEdited = null;
},
};
var gZenMediaController = {
_currentMediaController: null,
_currentBrowser: null,
_mediaUpdateInterval: null,
mediaTitle: null,
mediaArtist: null,
mediaControlBar: null,
mediaServiceIcon: null,
mediaServiceTitle: null,
mediaProgressBar: null,
mediaCurrentTime: null,
mediaDuration: null,
mediaFocusButton: null,
supportedKeys: ['playpause', 'previoustrack', 'nexttrack'],
init() {
this.mediaTitle = document.querySelector('#zen-media-title');
this.mediaArtist = document.querySelector('#zen-media-artist');
this.mediaControlBar = document.querySelector('#zen-media-controls-toolbar');
this.mediaServiceIcon = document.querySelector('#zen-media-service-button > image');
this.mediaServiceTitle = document.querySelector('#zen-media-service-title');
this.mediaProgressBar = document.querySelector('#zen-media-progress-bar');
this.mediaCurrentTime = document.querySelector('#zen-media-current-time');
this.mediaDuration = document.querySelector('#zen-media-duration');
this.mediaFocusButton = document.querySelector('#zen-media-focus-button');
},
/**
* Deinitializes a media controller, removing all event listeners and resetting state.
* @param {Object} mediaController - The media controller to deinitialize.
*/
deinitMediaController(mediaController) {
if (!mediaController) return;
mediaController.onpositionstatechange = null;
mediaController.onplaybackstatechange = null;
mediaController.onsupportedkeyschange = null;
mediaController.onmetadatachange = null;
mediaController.ondeactivated = null;
this._currentMediaController = null;
this._currentBrowser = null;
if (this._mediaUpdateInterval) {
clearInterval(this._mediaUpdateInterval);
this._mediaUpdateInterval = null;
}
this.mediaControlBar.setAttribute('hidden', 'true');
this.mediaControlBar.removeAttribute('muted');
this.mediaControlBar.classList.remove('playing');
},
/**
* Sets up the media control UI with metadata and position state.
* @param {Object} metadata - The media metadata (title, artist, etc.).
* @param {Object} positionState - The position state (position, duration).
*/
setupMediaControl(metadata, positionState) {
if (!this.mediaControlBar.classList.contains('playing')) {
this.mediaControlBar.classList.add('playing');
}
this.mediaServiceTitle.textContent = this._currentBrowser._originalURI.displayHost;
this.mediaServiceIcon.src = this._currentBrowser.mIconURL;
this.mediaFocusButton.style.listStyleImage = `url(${this._currentBrowser.mIconURL})`;
this.mediaControlBar.removeAttribute('hidden');
this.mediaTitle.textContent = metadata.title || '';
this.mediaArtist.textContent = metadata.artist || '';
this._currentPosition = positionState.position;
this._currentDuration = positionState.duration;
this.updateMediaPosition();
for (const key of this.supportedKeys) {
const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
button.disabled = !this._currentMediaController.supportedKeys.includes(key);
}
},
/**
* @param {Object} mediaController - The media controller to activate.
* @param {Object} browser - The browser associated with the media controller.
*/
activateMediaControls(mediaController, browser) {
if (this._currentBrowser?.browserId === browser.browserId) return;
else this._currentBrowser = browser;
mediaController.onpositionstatechange = this.onPositionstateChange.bind(this);
mediaController.onplaybackstatechange = this.onPlaybackstateChange.bind(this);
mediaController.onsupportedkeyschange = this.onSupportedKeysChange.bind(this);
mediaController.onmetadatachange = this.onMetadataChange.bind(this);
mediaController.ondeactivated = this.onDeactivated.bind(this);
if (this._currentMediaController === mediaController) return;
else this.deinitMediaController(this._currentMediaController);
this._currentMediaController = mediaController;
const metadata = mediaController.getMetadata();
const positionState = mediaController.getPositionState();
this.setupMediaControl(metadata, positionState);
},
/**
* @param {Event} event - The deactivation event.
*/
onDeactivated(event) {
if (event.target === this._currentMediaController) {
this.deinitMediaController(event.target);
}
},
/**
* Updates playback state and UI based on changes.
* @param {Event} event - The playback state change event.
*/
onPlaybackstateChange(event) {
this.mediaControlBar.classList.toggle('playing', event.target.isPlaying);
},
/**
* Updates supported keys in the UI.
* @param {Event} event - The supported keys change event.
*/
onSupportedKeysChange(event) {
for (const key of this.supportedKeys) {
const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
button.disabled = !event.target.supportedKeys.includes(key);
}
},
/**
* Updates position state and UI when the media position changes.
* @param {Event} event - The position state change event.
*/
onPositionstateChange(event) {
if (event.target !== this._currentMediaController) return;
this._currentPosition = event.position;
this._currentDuration = event.duration;
this.updateMediaPosition();
},
/**
* Updates the media progress bar and time display.
*/
updateMediaPosition() {
if (this._mediaUpdateInterval) {
clearInterval(this._mediaUpdateInterval);
this._mediaUpdateInterval = null;
}
this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(this._currentPosition);
this.mediaDuration.textContent = this.formatSecondsToMinutes(this._currentDuration);
this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
if (this._currentMediaController?.isPlaying) {
this._mediaUpdateInterval = setInterval(() => {
if (this._currentMediaController?.isPlaying) {
this._currentPosition += 1;
if (this._currentPosition > this._currentDuration) {
this._currentPosition = this._currentDuration;
}
this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(this._currentPosition);
this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
} else {
clearInterval(this._mediaUpdateInterval);
this._mediaUpdateInterval = null;
}
}, 1000);
}
},
/**
* Formats seconds into a minutes:seconds string.
* @param {number} seconds - The time in seconds.
* @returns {string} Formatted time string.
*/
formatSecondsToMinutes(seconds) {
const totalSeconds = Math.ceil(seconds);
const minutes = Math.floor(totalSeconds / 60);
const remainingSeconds = totalSeconds % 60;
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
},
/**
* Updates metadata in the UI.
* @param {Event} event - The metadata change event.
*/
onMetadataChange(event) {
const metadata = event.target.getMetadata();
this.mediaTitle.textContent = metadata.title || '';
this.mediaArtist.textContent = metadata.artist || '';
},
onMediaPlayPrev() {
if (this._currentMediaController?.supportedKeys.includes('previoustrack')) {
this._currentMediaController.prevTrack();
}
},
onMediaPlayNext() {
if (this._currentMediaController?.supportedKeys.includes('nexttrack')) {
this._currentMediaController.nextTrack();
}
},
onMediaSeekDrag(event) {
this._currentMediaController?.pause();
const newTime = (event.target.value / 100) * this._currentDuration;
this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(newTime);
},
onMediaSeekComplete(event) {
const newPosition = (event.target.value / 100) * this._currentDuration;
if (this._currentMediaController?.supportedKeys.includes('seekto')) {
this._currentMediaController.seekTo(newPosition);
this._currentMediaController.play();
}
},
onMediaFocus() {
this._currentMediaController?.focus();
},
onMediaMute() {
if (!this.mediaControlBar.hasAttribute('muted')) {
this._currentBrowser.mute();
this.mediaControlBar.setAttribute('muted', '');
} else {
this._currentBrowser.unmute();
this.mediaControlBar.removeAttribute('muted');
}
},
onMediaToggle() {
if (this.mediaControlBar.classList.contains('playing')) {
this._currentMediaController?.pause();
} else {
this._currentMediaController?.play();
}
},
};

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index a0a382643a2f74b6d789f3641ef300eed202d5e9..a962e155f1452362a2a35df89c8f56e1c0d9968c 100644
index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695b0d5af37 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -2,7 +2,7 @@
@@ -73,7 +73,7 @@ index a0a382643a2f74b6d789f3641ef300eed202d5e9..a962e155f1452362a2a35df89c8f56e1
</tabs>
<toolbarbutton id="new-tab-button"
@@ -100,11 +108,12 @@
@@ -100,11 +108,13 @@
#include private-browsing-indicator.inc.xhtml
<toolbarbutton id="content-analysis-indicator"
class="toolbarbutton-1 content-analysis-indicator-icon"/>
@@ -82,6 +82,7 @@ index a0a382643a2f74b6d789f3641ef300eed202d5e9..a962e155f1452362a2a35df89c8f56e1
#include titlebar-items.inc.xhtml
-
+#endif
+#include zen-media-controller.inc.xhtml
+#include zen-sidebar-icons.inc.xhtml
</toolbar>
-
@@ -89,7 +90,7 @@ index a0a382643a2f74b6d789f3641ef300eed202d5e9..a962e155f1452362a2a35df89c8f56e1
<toolbar id="nav-bar"
class="browser-toolbar chromeclass-location"
data-l10n-id="navbar-accessible"
@@ -490,10 +499,12 @@
@@ -490,10 +500,12 @@
consumeanchor="PanelUI-button"
data-l10n-id="appmenu-menu-button-closed2"/>
</toolbaritem>

View File

@@ -26,6 +26,7 @@
<link rel="stylesheet" type="text/css" href="chrome://browser/skin/zen-icons/icons.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-branding.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-welcome.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-media-controls.css" />
</linkset>
# Scripts used all over the browser

View File

@@ -49,6 +49,7 @@
content/browser/zen-styles/zen-rices.css (content/zen-styles/zen-rices.css)
content/browser/zen-styles/zen-branding.css (content/zen-styles/zen-branding.css)
content/browser/zen-styles/zen-welcome.css (content/zen-styles/zen-welcome.css)
content/browser/zen-styles/zen-media-controls.css (content/zen-styles/zen-media-controls.css)
content/browser/zen-styles/zen-panels/bookmarks.css (content/zen-styles/zen-panels/bookmarks.css)
content/browser/zen-styles/zen-panels/extensions.css (content/zen-styles/zen-panels/extensions.css)

View File

@@ -0,0 +1,48 @@
<toolbar id="zen-media-controls-toolbar"
class="browser-toolbar customization-target zen-sidebar-toolbar"
context="toolbar-context-menu"
mode="icons"
hidden="true">
<toolbaritem>
<vbox id="zen-media-main-vbox">
<hbox id="zen-media-service-hbox" class="show-on-hover">
<html:div id="zen-media-service-button">
<image/>
</html:div>
<label id="zen-media-service-title" fadein="true"/>
</hbox>
<vbox id="zen-media-info-vbox" class="show-on-hover">
<label id="zen-media-title" fadein="true"/>
<label id="zen-media-artist" fadein="true"/>
</vbox>
<hbox id="zen-media-progress-hbox" class="show-on-hover">
<label id="zen-media-current-time">0:00</label>
<html:input type="range" id="zen-media-progress-bar"
value="0" min="0" max="100" step="0.1"
oninput="gZenMediaController.onMediaSeekDrag(event);"
onchange="gZenMediaController.onMediaSeekComplete(event);"/>
<label id="zen-media-duration">0:00</label>
</hbox>
<hbox id="zen-media-controls-hbox">
<toolbarbutton id="zen-media-focus-button"
class="toolbarbutton-1"
oncommand="gZenMediaController.onMediaFocus();" />
<toolbarbutton id="zen-media-previoustrack-button"
class="toolbarbutton-1"
oncommand="gZenMediaController.onMediaPlayPrev();" />
<toolbarbutton id="zen-media-playpause-button"
class="toolbarbutton-1"
oncommand="gZenMediaController.onMediaToggle();" />
<toolbarbutton id="zen-media-nexttrack-button"
class="toolbarbutton-1"
oncommand="gZenMediaController.onMediaPlayNext();" />
<toolbarbutton id="zen-media-mute-button"
class="toolbarbutton-1"
oncommand="gZenMediaController.onMediaMute();" />
</hbox>
</vbox>
</toolbaritem>
</toolbar>

View File

@@ -0,0 +1,191 @@
#zen-media-controls-toolbar {
--progress-height: 5px;
--button-spacing: 2px;
display: flex;
justify-content: space-between;
min-width: 0;
padding: 5px;
border-radius: 10px;
background: var(--zen-toolbar-element-bg) !important;
container-type: inline-size;
.toolbarbutton-1 {
border-radius: 5px;
color: white;
}
#zen-media-prev-button,
#zen-media-play-pause-button,
#zen-media-next-button {
margin: 0;
}
image.toolbarbutton-icon {
padding: 5px;
width: 26px;
height: 26px;
}
#zen-media-progress-bar {
appearance: none;
width: 100%;
height: var(--progress-height);
margin: 0 8px;
border-radius: 2px;
background-color: rgba(255, 255, 255, 0.2);
cursor: pointer;
transition: height 0.15s ease-out;
&::-moz-range-track {
background: var(--zen-colors-border);
border-radius: 999px;
height: var(--progress-height);
}
&::-moz-range-progress {
background: var(--zen-primary-color);
border-radius: 999px;
height: var(--progress-height);
}
&::-moz-range-thumb {
background: var(--zen-primary-color);
border: none;
width: 14px;
height: 14px;
border-radius: 50%;
cursor: pointer;
}
}
&:hover {
.show-on-hover {
max-height: 50px;
opacity: 1;
transform: translateY(0);
}
}
#zen-media-focus-button {
width: 34px;
height: 26px;
align-self: center;
transition: opacity 0.2s ease, transform 0.2s ease;
@container (max-width: 185px) {
width: 0;
height: 0;
opacity: 0;
padding: 0;
transform: translateX(-20px);
}
@container (min-width: 185px) {
opacity: 1;
transform: translateX(0);
}
}
toolbaritem {
flex-grow: 1;
padding: 0;
transition: padding 0.3s ease-out;
}
.show-on-hover {
padding-inline: 4px;
max-height: 0;
opacity: 0;
overflow: hidden;
transform: translateY(5px);
transition: max-height 0.35s ease-in-out,
opacity 0.25s ease-in-out,
transform 0.3s ease-in-out;
}
#zen-media-current-time,
#zen-media-duration {
margin: 0 0 0 1px;
}
}
@keyframes zen-media-controls-show {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
#zen-media-controls-toolbar {
&:not([hidden]) {
animation: zen-media-controls-show 0.2s ease-out;
display: flex;
}
&[hidden] {
display: none;
animation: none;
transition: none;
}
}
#zen-media-service-title,
#zen-media-title,
#zen-media-artist {
white-space: nowrap;
width: 0;
margin-left: 0;
}
#zen-media-service-title {
align-self: center;
font-size: 13px;
margin-bottom: 5px;
margin-left: 6px;
}
#zen-media-title,
#zen-media-artist {
align-self: start;
margin-top: 5px;
}
#zen-media-title {
height: 16px;
font-size: 16px;
font-weight: bold;
}
#zen-media-artist {
height: 5px;
padding-bottom: 20px;
}
#zen-media-main-vbox,
#zen-media-service-hbox,
#zen-media-info-vbox,
#zen-media-progress-hbox {
width: 100%;
}
#zen-media-main-vbox {
height: 100%;
justify-content: space-between;
overflow: hidden;
}
#zen-media-progress-hbox {
flex-grow: 1;
align-items: center;
}
#zen-media-controls-hbox {
align-items: flex-end;
justify-content: space-evenly;
max-width: 100%;
overflow: hidden;
}
#zen-media-service-button image {
width: 20px;
height: 20px;
}

View File

@@ -1150,3 +1150,37 @@ menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
#sidebarRevampSeparator {
display: none !important;
}
#zen-media-playpause-button {
list-style-image: url('media-play.svg') !important;
}
#zen-media-controls-toolbar.playing #zen-media-playpause-button {
list-style-image: url('media-pause.svg') !important;
}
#zen-media-nexttrack-button {
list-style-image: url('media-next.svg') !important;
}
#zen-media-previoustrack-button {
list-style-image: url('media-previous.svg') !important;
}
#zen-media-controls-toolbar[muted] #zen-media-mute-button {
list-style-image: url('media-mute.svg') !important;
}
#zen-media-mute-button {
list-style-image: url('media-unmute.svg') !important;
}
#zen-media-close-button {
list-style-image: url('close.svg') !important;
}
#zen-media-controls-toolbar:hover {
#zen-media-focus-button {
list-style-image: url('screen.svg') !important;
}
}

View File

@@ -66,9 +66,11 @@
skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/lin/manage.svg)
skin/classic/browser/zen-icons/media-loop.svg (../shared/zen-icons/lin/media-loop.svg)
skin/classic/browser/zen-icons/media-mute.svg (../shared/zen-icons/lin/media-mute.svg)
skin/classic/browser/zen-icons/media-next.svg (../shared/zen-icons/lin/media-next.svg)
skin/classic/browser/zen-icons/media-pause.svg (../shared/zen-icons/lin/media-pause.svg)
skin/classic/browser/zen-icons/media-pip.svg (../shared/zen-icons/lin/media-pip.svg)
skin/classic/browser/zen-icons/media-play.svg (../shared/zen-icons/lin/media-play.svg)
skin/classic/browser/zen-icons/media-previous.svg (../shared/zen-icons/lin/media-previous.svg)
skin/classic/browser/zen-icons/media-speed.svg (../shared/zen-icons/lin/media-speed.svg)
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)
@@ -198,9 +200,11 @@
skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/lin/manage.svg)
skin/classic/browser/zen-icons/media-loop.svg (../shared/zen-icons/lin/media-loop.svg)
skin/classic/browser/zen-icons/media-mute.svg (../shared/zen-icons/lin/media-mute.svg)
skin/classic/browser/zen-icons/media-next.svg (../shared/zen-icons/lin/media-next.svg)
skin/classic/browser/zen-icons/media-pause.svg (../shared/zen-icons/lin/media-pause.svg)
skin/classic/browser/zen-icons/media-pip.svg (../shared/zen-icons/lin/media-pip.svg)
skin/classic/browser/zen-icons/media-play.svg (../shared/zen-icons/lin/media-play.svg)
skin/classic/browser/zen-icons/media-previous.svg (../shared/zen-icons/lin/media-previous.svg)
skin/classic/browser/zen-icons/media-speed.svg (../shared/zen-icons/lin/media-speed.svg)
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)
@@ -330,9 +334,11 @@
skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/lin/manage.svg)
skin/classic/browser/zen-icons/media-loop.svg (../shared/zen-icons/lin/media-loop.svg)
skin/classic/browser/zen-icons/media-mute.svg (../shared/zen-icons/lin/media-mute.svg)
skin/classic/browser/zen-icons/media-next.svg (../shared/zen-icons/lin/media-next.svg)
skin/classic/browser/zen-icons/media-pause.svg (../shared/zen-icons/lin/media-pause.svg)
skin/classic/browser/zen-icons/media-pip.svg (../shared/zen-icons/lin/media-pip.svg)
skin/classic/browser/zen-icons/media-play.svg (../shared/zen-icons/lin/media-play.svg)
skin/classic/browser/zen-icons/media-previous.svg (../shared/zen-icons/lin/media-previous.svg)
skin/classic/browser/zen-icons/media-speed.svg (../shared/zen-icons/lin/media-speed.svg)
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)

View File

@@ -0,0 +1,8 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M13.0502 2.74989C13.0502 2.44613 12.804 2.19989 12.5002 2.19989C12.1965 2.19989 11.9502 2.44613 11.9502 2.74989V7.2825C11.9046 7.18802 11.8295 7.10851 11.7334 7.05776L2.73338 2.30776C2.5784 2.22596 2.3919 2.23127 2.24182 2.32176C2.09175 2.41225 2 2.57471 2 2.74995V12.25C2 12.4252 2.09175 12.5877 2.24182 12.6781C2.3919 12.7686 2.5784 12.7739 2.73338 12.6921L11.7334 7.94214C11.8295 7.89139 11.9046 7.81188 11.9502 7.7174V12.2499C11.9502 12.5536 12.1965 12.7999 12.5002 12.7999C12.804 12.7999 13.0502 12.5536 13.0502 12.2499V2.74989ZM3 11.4207V3.5792L10.4288 7.49995L3 11.4207Z"
fill="currentColor"
/>
</svg>

After

Width:  |  Height:  |  Size: 774 B

View File

@@ -0,0 +1,8 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M1.94976 2.74989C1.94976 2.44613 2.196 2.19989 2.49976 2.19989C2.80351 2.19989 3.04976 2.44613 3.04976 2.74989V7.2825C3.0954 7.18802 3.17046 7.10851 3.26662 7.05776L12.2666 2.30776C12.4216 2.22596 12.6081 2.23127 12.7582 2.32176C12.9083 2.41225 13 2.57471 13 2.74995V12.25C13 12.4252 12.9083 12.5877 12.7582 12.6781C12.6081 12.7686 12.4216 12.7739 12.2666 12.6921L3.26662 7.94214C3.17046 7.89139 3.0954 7.81188 3.04976 7.7174V12.2499C3.04976 12.5536 2.80351 12.7999 2.49976 12.7999C2.196 12.7999 1.94976 12.5536 1.94976 12.2499V2.74989ZM4.57122 7.49995L12 11.4207V3.5792L4.57122 7.49995Z"
fill="currentColor"
/>
</svg>

After

Width:  |  Height:  |  Size: 784 B

View File

@@ -0,0 +1,27 @@
diff --git a/dom/chrome-webidl/MediaController.webidl b/dom/chrome-webidl/MediaController.webidl
index 20f416d1c3b41798e0f90bbac5db40ed2a4ab000..06cb4c847fcfba964eeb93089613e293dc10bd87 100644
--- a/dom/chrome-webidl/MediaController.webidl
+++ b/dom/chrome-webidl/MediaController.webidl
@@ -20,6 +20,12 @@ enum MediaControlKey {
"stop",
};
+dictionary MediaControllerPositionState {
+ required double duration;
+ required double playbackRate;
+ required double position;
+};
+
/**
* MediaController is used to control media playback for a tab, and each tab
* would only have one media controller, which can be accessed from the
@@ -36,6 +42,9 @@ interface MediaController : EventTarget {
[Throws]
MediaMetadataInit getMetadata();
+ [Throws]
+ MediaControllerPositionState getPositionState();
+
[Frozen, Cached, Pure]
readonly attribute sequence<MediaControlKey> supportedKeys;

View File

@@ -0,0 +1,30 @@
diff --git a/dom/media/mediacontrol/MediaController.cpp b/dom/media/mediacontrol/MediaController.cpp
index 3f08d24d4ed56bb72ed513ed602b2c8fa48afe7b..690d9abdb0ab8efc019dd606743b82504834faa0 100644
--- a/dom/media/mediacontrol/MediaController.cpp
+++ b/dom/media/mediacontrol/MediaController.cpp
@@ -51,6 +51,25 @@ void MediaController::GetSupportedKeys(
}
}
+void MediaController::GetPositionState(MediaControllerPositionState& aPositionState, ErrorResult& aRv) {
+ if (!IsActive() || mShutdown) {
+ LOG("Cannot get position state: controller is inactive or shut down");
+ aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+ return;
+ }
+
+ Maybe<PositionState> currentPositionState = GetCurrentPositionState();
+ if (!currentPositionState) {
+ LOG("No position state available for controller %" PRId64, Id());
+ aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+ return;
+ }
+
+ aPositionState.mDuration = currentPositionState->mDuration;
+ aPositionState.mPosition = currentPositionState->mLastReportedPlaybackPosition;
+ aPositionState.mPlaybackRate = currentPositionState->mPlaybackRate;
+}
+
void MediaController::GetMetadata(MediaMetadataInit& aMetadata,
ErrorResult& aRv) {
if (!IsActive() || mShutdown) {

View File

@@ -0,0 +1,12 @@
diff --git a/dom/media/mediacontrol/MediaController.h b/dom/media/mediacontrol/MediaController.h
index 8fec9c59e38bc24b9ff6d30ddbaebff67107bc76..5e7f3634f9edef48d6f96b4900f82a7ebbd730be 100644
--- a/dom/media/mediacontrol/MediaController.h
+++ b/dom/media/mediacontrol/MediaController.h
@@ -90,6 +90,7 @@ class MediaController final : public DOMEventTargetHelper,
JS::Handle<JSObject*> aGivenProto) override;
void GetSupportedKeys(nsTArray<MediaControlKey>& aRetVal) const;
void GetMetadata(MediaMetadataInit& aMetadata, ErrorResult& aRv);
+ void GetPositionState(MediaControllerPositionState& aPositionState, ErrorResult& aRv);
IMPL_EVENT_HANDLER(activated);
IMPL_EVENT_HANDLER(deactivated);
IMPL_EVENT_HANDLER(metadatachange);

View File

@@ -0,0 +1,17 @@
diff --git a/toolkit/actors/AudioPlaybackParent.sys.mjs b/toolkit/actors/AudioPlaybackParent.sys.mjs
index db682fd90b2bb5330497d2cf2158ff4cac6bbc47..c3eacff3b2215d29104216dd6086c486a86013e9 100644
--- a/toolkit/actors/AudioPlaybackParent.sys.mjs
+++ b/toolkit/actors/AudioPlaybackParent.sys.mjs
@@ -11,9 +11,12 @@ export class AudioPlaybackParent extends JSWindowActorParent {
}
receiveMessage(aMessage) {
const browser = this.browsingContext.top.embedderElement;
+ const mediaController = this.browsingContext.mediaController;
+
switch (aMessage.name) {
case "AudioPlayback:Start":
this._hasAudioPlayback = true;
+ browser.ownerGlobal.gZenMediaController.activateMediaControls(mediaController, browser);
browser.audioPlaybackStarted();
break;
case "AudioPlayback:Stop":