Bug 1308153 - part1 : notify tabbrowser when the tab was blocked.
We need to notify tabbrowser about media-blocking so that we can show the unblocking tab icon.
See bug1308399 for more UX details.
MozReview-Commit-ID: E25lEhZLCZk
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -469,16 +469,32 @@
if (!browser.tabModalPromptBox) {
browser.tabModalPromptBox = new TabModalPromptBox(browser);
}
return browser.tabModalPromptBox;
]]>
</body>
</method>
+ <method name="getTabFromAudioEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
+ !aEvent.isTrusted) {
+ return null;
+ }
+
+ var browser = aEvent.originalTarget;
+ var tab = this.getTabForBrowser(browser);
+ return tab;
+ ]]>
+ </body>
+ </method>
+
<method name="_callProgressListeners">
<parameter name="aBrowser"/>
<parameter name="aMethod"/>
<parameter name="aArguments"/>
<parameter name="aCallGlobalListeners"/>
<parameter name="aCallTabsListeners"/>
<body><![CDATA[
var rv = true;
@@ -5059,24 +5075,20 @@
}
tab.removeAttribute("soundplaying");
this.setIcon(tab, icon, browser.contentPrincipal);
]]>
</handler>
<handler event="DOMAudioPlaybackStarted">
<![CDATA[
- if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
- !event.isTrusted)
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
return;
-
- var browser = event.originalTarget;
- var tab = this.getTabForBrowser(browser);
- if (!tab)
- return;
+ }
clearTimeout(tab._soundPlayingAttrRemovalTimer);
tab._soundPlayingAttrRemovalTimer = 0;
let modifiedAttrs = [];
if (tab.hasAttribute("soundplaying-scheduledremoval")) {
tab.removeAttribute("soundplaying-scheduledremoval");
modifiedAttrs.push("soundplaying-scheduledremoval");
@@ -5087,40 +5099,56 @@
modifiedAttrs.push("soundplaying");
}
this._tabAttrModified(tab, modifiedAttrs);
]]>
</handler>
<handler event="DOMAudioPlaybackStopped">
<![CDATA[
- if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
- !event.isTrusted)
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
return;
-
- var browser = event.originalTarget;
- var tab = this.getTabForBrowser(browser);
- if (!tab)
- return;
+ }
if (tab.hasAttribute("soundplaying")) {
let removalDelay = Services.prefs.getIntPref("browser.tabs.delayHidingAudioPlayingIconMS");
tab.style.setProperty("--soundplaying-removal-delay", `${removalDelay - 300}ms`);
tab.setAttribute("soundplaying-scheduledremoval", "true");
this._tabAttrModified(tab, ["soundplaying-scheduledremoval"]);
tab._soundPlayingAttrRemovalTimer = setTimeout(() => {
tab.removeAttribute("soundplaying-scheduledremoval");
tab.removeAttribute("soundplaying");
this._tabAttrModified(tab, ["soundplaying", "soundplaying-scheduledremoval"]);
}, removalDelay);
}
]]>
</handler>
+ <handler event="DOMAudioPlaybackBlockStarted">
+ <![CDATA[
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
+ return;
+ }
+
+ // TODO : implement media-blocking icon in next patch.
+ ]]>
+ </handler>
+ <handler event="DOMAudioPlaybackBlockStopped">
+ <![CDATA[
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
+ return;
+ }
+
+ // TODO : implement media-blocking icon in next patch.
+ ]]>
+ </handler>
</handlers>
</binding>
<binding id="tabbrowser-tabbox"
extends="chrome://global/content/bindings/tabbox.xml#tabbox">
<implementation>
<property name="tabs" readonly="true"
onget="return document.getBindingParent(this).tabContainer;"/>
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -1246,16 +1246,17 @@ AudioChannelService::AudioChannelWindow:
AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
if (aAudible) {
AudioAudibleChanged(aAgent,
AudibleState::eAudible,
AudibleChangedReasons::eDataAudibleChanged);
} else if (IsEnableAudioCompetingForAllAgents() && !aAudible) {
NotifyAudioCompetingChanged(aAgent, true);
}
+ MaybeNotifyMediaBlocked(aAgent);
}
void
AudioChannelService::AudioChannelWindow::RemoveAgent(AudioChannelAgent* aAgent)
{
MOZ_ASSERT(aAgent);
RemoveAgentAndReduceAgentsNum(aAgent);
@@ -1394,8 +1395,35 @@ AudioChannelService::AudioChannelWindow:
AudioChannel aChannel,
bool aActive)
{
RefPtr<NotifyChannelActiveRunnable> runnable =
new NotifyChannelActiveRunnable(aWindowID, aChannel, aActive);
DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
}
+
+void
+AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
+ if (!window) {
+ return;
+ }
+
+ MOZ_ASSERT(window->IsOuterWindow());
+ if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK) {
+ return;
+ }
+
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction([window] () -> void {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (NS_WARN_IF(!observerService)) {
+ return;
+ }
+
+ observerService->NotifyObservers(ToSupports(window),
+ "audio-playback",
+ u"block");
+ })
+ );
+}
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -293,16 +293,17 @@ private:
bool IsLastAudibleAgent() const;
void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
AudibleState aAudible,
AudibleChangedReasons aReason);
void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel,
bool aActive);
+ void MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent);
void RequestAudioFocus(AudioChannelAgent* aAgent);
void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, bool aActive);
uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent,
int32_t aIncomingChannelType,
bool aIncomingChannelActive) const;
bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const;
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1008,17 +1008,21 @@ var AudioPlaybackListener = {
break;
}
},
observe(subject, topic, data) {
if (topic === "audio-playback") {
if (subject && subject.top == global.content) {
let name = "AudioPlayback:";
- name += (data === "active") ? "Start" : "Stop";
+ if (data === "block") {
+ name += "Block";
+ } else {
+ name += (data === "active") ? "Start" : "Stop";
+ }
sendAsyncMessage(name);
}
} else if (topic == "AudioFocusChanged" || topic == "MediaControl") {
this.handleMediaControlMessage(data);
}
},
receiveMessage(msg) {
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -685,35 +685,58 @@
readonly="true"/>
<method name="audioPlaybackStarted">
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackStarted", true, false);
this.dispatchEvent(event);
+ if (this._audioBlocked) {
+ this._audioBlocked = false;
+ event = document.createEvent("Events");
+ event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
+ this.dispatchEvent(event);
+ }
]]>
</body>
</method>
<method name="audioPlaybackStopped">
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackStopped", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
+ <method name="audioPlaybackBlocked">
+ <body>
+ <![CDATA[
+ this._audioBlocked = true;
+ let event = document.createEvent("Events");
+ event.initEvent("DOMAudioPlaybackBlockStarted", true, false);
+ this.dispatchEvent(event);
+ ]]>
+ </body>
+ </method>
+
<field name="_audioMuted">false</field>
<property name="audioMuted"
onget="return this._audioMuted;"
readonly="true"/>
+
+ <field name="_audioBlocked">false</field>
+ <property name="audioBlocked"
+ onget="return this._audioBlocked;"
+ readonly="true"/>
+
<method name="mute">
<body>
<![CDATA[
this._audioMuted = true;
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "mute"});
]]>
</body>
@@ -753,27 +776,32 @@
{type: "mediaControlStopped"});
]]>
</body>
</method>
<method name="blockMedia">
<body>
<![CDATA[
+ this._audioBlocked = true;
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "blockInactivePageMedia"});
]]>
</body>
</method>
<method name="resumeMedia">
<body>
<![CDATA[
+ this._audioBlocked = false;
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "resumeMedia"});
+ let event = document.createEvent("Events");
+ event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
+ this.dispatchEvent(event);
]]>
</body>
</method>
<property name="securityUI">
<getter>
<![CDATA[
if (!this.docShell.securityUI) {
@@ -917,16 +945,17 @@
}
if (this.messageManager) {
this.messageManager.addMessageListener("PopupBlocking:UpdateBlockedPopups", this);
this.messageManager.addMessageListener("Autoscroll:Start", this);
this.messageManager.addMessageListener("Autoscroll:Cancel", this);
this.messageManager.addMessageListener("AudioPlayback:Start", this);
this.messageManager.addMessageListener("AudioPlayback:Stop", this);
+ this.messageManager.addMessageListener("AudioPlayback:Block", this);
}
]]>
</constructor>
<destructor>
<![CDATA[
this.destroy();
]]>
@@ -1003,16 +1032,19 @@
this._autoScrollPopup.hidePopup();
break;
case "AudioPlayback:Start":
this.audioPlaybackStarted();
break;
case "AudioPlayback:Stop":
this.audioPlaybackStopped();
break;
+ case "AudioPlayback:Block":
+ this.audioPlaybackBlocked();
+ break;
}
return undefined;
]]></body>
</method>
<method name="receiveMessage">
<parameter name="aMessage"/>
<body><![CDATA[