diff --git a/browser/base/content/pictureInPictureContent.js b/browser/base/content/pictureInPictureContent.js index 074415b598..a2735082fd 100644 --- a/browser/base/content/pictureInPictureContent.js +++ b/browser/base/content/pictureInPictureContent.js @@ -19,6 +19,11 @@ function update() { if (!source) { return; } + // Gate already-buffered audio here rather than waiting for source mute + // changes to travel through the decoder's capture queue. + if (output) { + output.muted = !!source.srcObject || source.muted; + } // Capture streams do not produce frames while their source is paused. // Keep a snapshot for that case, without changing the source's play state. still.hidden = !source.paused && !source.ended; @@ -116,14 +121,14 @@ addMessageListener("PictureInPicture:Init", message => { "max-width:none;max-height:none;object-fit:contain"; doc.body.appendChild(element); } - // Decoder capture redirects audio to the captured stream. Its samples - // already include the source's volume/mute settings. MediaStream sources - // keep their own audio output, so mute only that case to avoid an echo. - output.muted = !!source.srcObject; + // Decoder capture redirects audio to this output. PiP content mute is + // applied here; other volume/mute policies remain in the decoder. + // MediaStream sources keep their own output, so avoid doubling it. + output.muted = !!source.srcObject || source.muted; output.volume = 1; - source.mozPictureInPicture = true; stream = source.mozCaptureStream(); output.srcObject = stream; + source.mozPictureInPicture = true; output.play().catch(error => { Components.utils.reportError(error); closePlayer(); @@ -161,6 +166,7 @@ addMessageListener("PictureInPicture:Command", message => { break; case "mute": source.muted = !source.muted; + update(); break; case "seek": seekTo(message.data.time); diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 0a1ecceeb5..269f134d39 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2404,6 +2404,12 @@ void HTMLMediaElement::SetVolumeInternal() float effectiveVolume = ComputedVolume(); if (mDecoder) { + // PiP applies content mute at its playback element, after the capture + // queue. Keep those samples available for immediate unmute, while still + // honoring audio-channel, playback-rate and disabled-track muting. + if (mPictureInPicture && !(mMuted & ~MUTED_BY_CONTENT)) { + effectiveVolume = float(mVolume * mAudioChannelVolume); + } mDecoder->SetVolume(effectiveVolume); } else if (MediaStream* stream = GetSrcMediaStream()) { if (mSrcStreamIsPlaying) { @@ -5188,6 +5194,7 @@ void HTMLMediaElement::SetMozPictureInPicture(bool aEnabled) MOZ_ASSERT(NS_IsMainThread()); mPictureInPicture = aEnabled; if (mDecoder && !mShuttingDown) { + SetVolumeInternal(); mDecoder->NotifyOwnerActivityChanged(!IsHidden()); } } diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index eb20b94282..232443fc66 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3207,6 +3207,12 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide) ::SetWindowLongPtrW(hwnd, GWL_STYLE, style); ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle); + // Apply the new non-client metrics immediately. Otherwise the client area + // keeps its old titlebar/border dimensions until the first move or resize. + ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | + SWP_NOZORDER | SWP_NOACTIVATE); + return NS_OK; }