diff --git a/src/browser/base/content/ZenUIManager.mjs b/src/browser/base/content/ZenUIManager.mjs index 1f84425ae..185fe8cc3 100644 --- a/src/browser/base/content/ZenUIManager.mjs +++ b/src/browser/base/content/ZenUIManager.mjs @@ -770,252 +770,3 @@ 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(); - } - }, -}; diff --git a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch index b2f5e92ae..104f2a9d1 100644 --- a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch +++ b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml -index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695b0d5af37 100644 +index 2a57e254c876589bf64bfbd5df188a2b435d8482..fcb6958cd1379bb5f2a7f63d5c7d8145726001cd 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 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695 @@ -82,7 +82,6 @@ index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695 #include titlebar-items.inc.xhtml - +#endif -+#include zen-media-controller.inc.xhtml +#include zen-sidebar-icons.inc.xhtml - @@ -90,7 +89,7 @@ index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695 diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml index c5f6d0b17..9895f4093 100644 --- a/src/browser/base/content/zen-assets.inc.xhtml +++ b/src/browser/base/content/zen-assets.inc.xhtml @@ -43,6 +43,7 @@