summaryrefslogtreecommitdiffstats
path: root/application/palemoon/base/content
diff options
context:
space:
mode:
Diffstat (limited to 'application/palemoon/base/content')
-rw-r--r--application/palemoon/base/content/browser-sets.inc2
-rw-r--r--application/palemoon/base/content/browser-tabPreviews.js7
-rw-r--r--application/palemoon/base/content/browser-tabPreviews.xml5
-rw-r--r--application/palemoon/base/content/browser.js79
-rw-r--r--application/palemoon/base/content/browser.xul1
-rw-r--r--application/palemoon/base/content/tabbrowser.css11
-rw-r--r--application/palemoon/base/content/tabbrowser.xml266
7 files changed, 354 insertions, 17 deletions
diff --git a/application/palemoon/base/content/browser-sets.inc b/application/palemoon/base/content/browser-sets.inc
index 25794a65c..78fce2670 100644
--- a/application/palemoon/base/content/browser-sets.inc
+++ b/application/palemoon/base/content/browser-sets.inc
@@ -32,6 +32,7 @@
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
+ <command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
<command id="cmd_ToggleTabsOnTop" oncommand="TabsOnTop.toggle()"/>
<command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
<command id="cmd_restartApplication" oncommand="restart(false);"/>
@@ -212,6 +213,7 @@
<key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/>
<key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
<key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/>
+ <key id="key_toggleMute" key="&toggleMuteCmd.key;" command="cmd_toggleMute" modifiers="control"/>
<key id="key_undo"
key="&undoCmd.key;"
modifiers="accel"/>
diff --git a/application/palemoon/base/content/browser-tabPreviews.js b/application/palemoon/base/content/browser-tabPreviews.js
index eaae78ba8..e0755b81d 100644
--- a/application/palemoon/base/content/browser-tabPreviews.js
+++ b/application/palemoon/base/content/browser-tabPreviews.js
@@ -940,6 +940,13 @@ var allTabs = {
aPreview.setAttribute("image", aPreview._tab.image);
else
aPreview.removeAttribute("image");
+
+ aPreview.removeAttribute("soundplaying");
+ aPreview.removeAttribute("muted");
+ if (aPreview._tab.hasAttribute("muted"))
+ aPreview.setAttribute("muted", "true");
+ else if (aPreview._tab.hasAttribute("soundplaying"))
+ aPreview.setAttribute("soundplaying", "true");
var thumbnail = tabPreviews.get(aPreview._tab);
if (aPreview.firstChild) {
diff --git a/application/palemoon/base/content/browser-tabPreviews.xml b/application/palemoon/base/content/browser-tabPreviews.xml
index e957649e7..4f54321ea 100644
--- a/application/palemoon/base/content/browser-tabPreviews.xml
+++ b/application/palemoon/base/content/browser-tabPreviews.xml
@@ -42,7 +42,10 @@
<xul:hbox class="tabPreview-canvas" xbl:inherits="style=canvasstyle">
<children/>
</xul:hbox>
- <xul:label flex="1" xbl:inherits="value=label,crop" class="allTabs-preview-label plain"/>
+ <xul:hbox align="center">
+ <xul:image xbl:inherits="soundplaying,muted" class="allTabs-endimage"/>
+ <xul:label flex="1" xbl:inherits="value=label,crop" class="allTabs-preview-label plain"/>
+ </xul:hbox>
</xul:vbox>
<xul:hbox class="allTabs-favicon-container">
<xul:image class="allTabs-favicon" xbl:inherits="src=image"/>
diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js
index 4167f186c..591d00fbb 100644
--- a/application/palemoon/base/content/browser.js
+++ b/application/palemoon/base/content/browser.js
@@ -976,6 +976,7 @@ var gBrowserInit = {
CombinedStopReload.init();
allTabs.readPref();
TabsOnTop.init();
+ AudioIndicator.init();
gPrivateBrowsingUI.init();
TabsInTitlebar.init();
retrieveToolbarIconsizesFromTheme();
@@ -1364,6 +1365,8 @@ var gBrowserInit = {
BookmarkingUI.uninit();
TabsOnTop.uninit();
+
+ AudioIndicator.uninit();
TabsInTitlebar.uninit();
@@ -4597,6 +4600,42 @@ function setToolbarVisibility(toolbar, isVisible) {
ToolbarIconColor.inferFromText();
}
+var AudioIndicator = {
+ init: function () {
+ Services.prefs.addObserver(this._prefName, this, false);
+ this.syncUI();
+ },
+
+ uninit: function () {
+ Services.prefs.removeObserver(this._prefName, this);
+ },
+
+ toggle: function () {
+ this.enabled = !Services.prefs.getBoolPref(this._prefName);
+ },
+
+ syncUI: function () {
+ document.getElementById("context_toggleMuteTab").setAttribute("hidden", this.enabled);
+ document.getElementById("key_toggleMute").setAttribute("disabled", this.enabled);
+ },
+
+ get enabled () {
+ return !Services.prefs.getBoolPref(this._prefName);
+ },
+
+ set enabled (val) {
+ Services.prefs.setBoolPref(this._prefName, !!val);
+ return val;
+ },
+
+ observe: function (subject, topic, data) {
+ if (topic == "nsPref:changed")
+ this.syncUI();
+ },
+
+ _prefName: "browser.tabs.showAudioPlayingIcon"
+}
+
var TabsOnTop = {
init: function TabsOnTop_init() {
Services.prefs.addObserver(this._prefName, this, false);
@@ -7021,6 +7060,17 @@ function restoreLastSession() {
var TabContextMenu = {
contextTab: null,
+ _updateToggleMuteMenuItem(aTab, aConditionFn) {
+ ["muted", "soundplaying"].forEach(attr => {
+ if (!aConditionFn || aConditionFn(attr)) {
+ if (aTab.hasAttribute(attr)) {
+ aTab.toggleMuteMenuItem.setAttribute(attr, "true");
+ } else {
+ aTab.toggleMuteMenuItem.removeAttribute(attr);
+ }
+ }
+ });
+ },
updateContextMenu: function updateContextMenu(aPopupMenu) {
this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
aPopupMenu.triggerNode : gBrowser.selectedTab;
@@ -7067,6 +7117,35 @@ var TabContextMenu = {
bookmarkAllTabs.hidden = this.contextTab.pinned;
if (!bookmarkAllTabs.hidden)
PlacesCommandHook.updateBookmarkAllTabsCommand();
+
+ // Adjust the state of the toggle mute menu item.
+ let toggleMute = document.getElementById("context_toggleMuteTab");
+ if (this.contextTab.hasAttribute("muted")) {
+ toggleMute.label = gNavigatorBundle.getString("unmuteTab.label");
+ toggleMute.accessKey = gNavigatorBundle.getString("unmuteTab.accesskey");
+ } else {
+ toggleMute.label = gNavigatorBundle.getString("muteTab.label");
+ toggleMute.accessKey = gNavigatorBundle.getString("muteTab.accesskey");
+ }
+
+ this.contextTab.toggleMuteMenuItem = toggleMute;
+ this._updateToggleMuteMenuItem(this.contextTab);
+
+ this.contextTab.addEventListener("TabAttrModified", this, false);
+ aPopupMenu.addEventListener("popuphiding", this, false);
+ },
+ handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "popuphiding":
+ gBrowser.removeEventListener("TabAttrModified", this);
+ aEvent.target.removeEventListener("popuphiding", this);
+ break;
+ case "TabAttrModified":
+ let tab = aEvent.target;
+ this._updateToggleMuteMenuItem(tab,
+ attr => aEvent.detail.changed.indexOf(attr) >= 0);
+ break;
+ }
}
};
diff --git a/application/palemoon/base/content/browser.xul b/application/palemoon/base/content/browser.xul
index 07ca54722..ce2a7c5a8 100644
--- a/application/palemoon/base/content/browser.xul
+++ b/application/palemoon/base/content/browser.xul
@@ -87,6 +87,7 @@
onpopuphidden="if (event.target == this) TabContextMenu.contextTab = null;">
<menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
oncommand="gBrowser.reloadTab(TabContextMenu.contextTab);"/>
+ <menuitem id="context_toggleMuteTab" oncommand="TabContextMenu.contextTab.toggleMuteAudio();"/>
<menuseparator/>
<menuitem id="context_pinTab" label="&pinTab.label;"
accesskey="&pinTab.accesskey;"
diff --git a/application/palemoon/base/content/tabbrowser.css b/application/palemoon/base/content/tabbrowser.css
index 94d6dbb2e..43536b27a 100644
--- a/application/palemoon/base/content/tabbrowser.css
+++ b/application/palemoon/base/content/tabbrowser.css
@@ -45,10 +45,19 @@ tabpanels {
}
.tab-throbber:not([busy]),
-.tab-throbber[busy] + .tab-icon-image {
+.tab-throbber[busy] + .tab-icon-image,
+.tab-icon-sound:not([soundplaying]):not([muted]):not([blocked]),
+.tab-icon-sound[pinned],
+.tab-icon-overlay {
display: none;
}
+.tab-icon-overlay[soundplaying][pinned],
+.tab-icon-overlay[muted][pinned],
+.tab-icon-overlay[blocked][pinned] {
+ display: -moz-box;
+}
+
.closing-tabs-spacer {
pointer-events: none;
}
diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml
index dc6cb0a9d..929afd057 100644
--- a/application/palemoon/base/content/tabbrowser.xml
+++ b/application/palemoon/base/content/tabbrowser.xml
@@ -438,6 +438,22 @@
</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"/>
@@ -616,7 +632,7 @@
if (this.mTab.hasAttribute("busy")) {
this.mTab.removeAttribute("busy");
- this.mTabBrowser._tabAttrModified(this.mTab);
+ this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
if (!this.mTab.selected)
this.mTab.setAttribute("unread", "true");
}
@@ -686,6 +702,8 @@
let topLevel = aWebProgress.isTopLevel;
if (topLevel) {
+ let isSameDocument =
+ !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
// We need to clear the typed value
// if the document failed to load, to make sure the urlbar reflects the
// failed URI (particularly for SSL errors). However, don't clear the value
@@ -696,6 +714,19 @@
aLocation.spec != "about:blank"))
this.mBrowser.userTypedValue = null;
+ // If the browser was playing audio, we should remove the playing state.
+ if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
+ clearTimeout(this.mTab._soundPlayingAttrRemovalTimer);
+ this.mTab._soundPlayingAttrRemovalTimer = 0;
+ this.mTab.removeAttribute("soundplaying");
+ this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
+ }
+
+ // If the browser was previously muted, we should restore the muted state.
+ if (this.mTab.hasAttribute("muted")) {
+ this.mTab.linkedBrowser.mute();
+ }
+
// Don't clear the favicon if this onLocationChange was
// triggered by a pushState or a replaceState. See bug 550565.
if (!gMultiProcessBrowser) {
@@ -804,7 +835,7 @@
aTab.setAttribute("image", sizedIconUrl);
else
aTab.removeAttribute("image");
- this._tabAttrModified(aTab);
+ this._tabAttrModified(aTab, ["image"]);
}
this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
@@ -1116,8 +1147,8 @@
});
this.mCurrentTab.dispatchEvent(event);
- this._tabAttrModified(oldTab);
- this._tabAttrModified(this.mCurrentTab);
+ this._tabAttrModified(oldTab, ["selected"]);
+ this._tabAttrModified(this.mCurrentTab, ["selected"]);
// Adjust focus
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
@@ -1187,14 +1218,18 @@
<method name="_tabAttrModified">
<parameter name="aTab"/>
+ <parameter name="aChanged"/>
<body><![CDATA[
if (aTab.closing)
return;
- // This event should be dispatched when any of these attributes change:
- // label, crop, busy, image, selected
- var event = document.createEvent("Events");
- event.initEvent("TabAttrModified", true, false);
+ let event = new CustomEvent("TabAttrModified", {
+ bubbles: true,
+ cancelable: false,
+ detail: {
+ changed: aChanged,
+ }
+ });
aTab.dispatchEvent(event);
]]></body>
</method>
@@ -1205,7 +1240,7 @@
<![CDATA[
aTab.label = this.mStringBundle.getString("tabs.connecting");
aTab.crop = "end";
- this._tabAttrModified(aTab);
+ this._tabAttrModified(aTab, ["label", "crop"]);
]]>
</body>
</method>
@@ -1250,7 +1285,7 @@
aTab.label = title;
aTab.crop = crop;
- this._tabAttrModified(aTab);
+ this._tabAttrModified(aTab, ["label", "crop"]);
if (aTab.selected)
this.updateTitlebar();
@@ -2239,6 +2274,14 @@
var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser;
var isPending = aOtherTab.hasAttribute("pending");
+ // Expedite the removal of the icon if it was already scheduled.
+ if (aOtherTab._soundPlayingAttrRemovalTimer) {
+ clearTimeout(aOtherTab._soundPlayingAttrRemovalTimer);
+ aOtherTab._soundPlayingAttrRemovalTimer = 0;
+ aOtherTab.removeAttribute("soundplaying");
+ remoteBrowser._tabAttrModified(aOtherTab, ["soundplaying"]);
+ }
+
// First, start teardown of the other browser. Make sure to not
// fire the beforeunload event in the process. Close the other
// window if this was its last tab.
@@ -2248,6 +2291,18 @@
let ourBrowser = this.getBrowserForTab(aOurTab);
let otherBrowser = aOtherTab.linkedBrowser;
+ let modifiedAttrs = [];
+ if (aOtherTab.hasAttribute("muted")) {
+ aOurTab.setAttribute("muted", "true");
+ aOurTab.muteReason = aOtherTab.muteReason;
+ ourBrowser.mute();
+ modifiedAttrs.push("muted");
+ }
+ if (aOtherTab.hasAttribute("soundplaying")) {
+ aOurTab.setAttribute("soundplaying", "true");
+ modifiedAttrs.push("soundplaying");
+ }
+
// If the other tab is pending (i.e. has not been restored, yet)
// then do not switch docShells but retrieve the other tab's state
// and apply it to our tab.
@@ -2266,7 +2321,7 @@
var isBusy = aOtherTab.hasAttribute("busy");
if (isBusy) {
aOurTab.setAttribute("busy", "true");
- this._tabAttrModified(aOurTab);
+ modifiedAttrs.push("busy");
if (aOurTab.selected)
this.mIsBusy = true;
}
@@ -2286,6 +2341,10 @@
// of replaceTabWithWindow), notify onLocationChange, etc.
if (aOurTab.selected)
this.updateCurrentBrowser(true);
+
+ if (modifiedAttrs.length) {
+ this._tabAttrModified(aOurTab, modifiedAttrs);
+ }
]]>
</body>
</method>
@@ -3084,9 +3143,25 @@
event.preventDefault();
return;
}
- event.target.setAttribute("label", tab.mOverCloseButton ?
- tab.getAttribute("closetabtext") :
- tab.getAttribute("label"));
+
+ var stringID, label;
+ if (tab.mOverCloseButton) {
+ stringID = "tabs.closeTab";
+ } else if (tab._overPlayingIcon) {
+ if (tab.linkedBrowser.audioBlocked) {
+ stringID = "tabs.unblockAudio.tooltip";
+ } else {
+ stringID = tab.linkedBrowser.audioMuted ?
+ "tabs.unmuteAudio.tooltip" :
+ "tabs.muteAudio.tooltip";
+ }
+ } else {
+ label = tab.getAttribute("label");
+ }
+ if (stringID && !label) {
+ label = this.mStringBundle.getString(stringID);
+ }
+ event.target.setAttribute("label", label);
]]></body>
</method>
@@ -3300,6 +3375,7 @@
]]>
</getter>
</property>
+ <field name="_soundPlayingAttrRemovalTimer">0</field>
</implementation>
<handlers>
@@ -3347,6 +3423,78 @@
tab.setAttribute("titlechanged", "true");
]]>
</handler>
+ <handler event="DOMAudioPlaybackStarted">
+ <![CDATA[
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
+ return;
+ }
+
+ clearTimeout(tab._soundPlayingAttrRemovalTimer);
+ tab._soundPlayingAttrRemovalTimer = 0;
+
+ let modifiedAttrs = [];
+ if (tab.hasAttribute("soundplaying-scheduledremoval")) {
+ tab.removeAttribute("soundplaying-scheduledremoval");
+ modifiedAttrs.push("soundplaying-scheduledremoval");
+ }
+
+ if (!tab.hasAttribute("soundplaying")) {
+ tab.setAttribute("soundplaying", true);
+ modifiedAttrs.push("soundplaying");
+ }
+
+ this._tabAttrModified(tab, modifiedAttrs);
+ ]]>
+ </handler>
+ <handler event="DOMAudioPlaybackStopped">
+ <![CDATA[
+ var tab = getTabFromAudioEvent(event)
+ 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;
+ }
+
+ if (!tab.hasAttribute("blocked")) {
+ tab.setAttribute("blocked", true);
+ this._tabAttrModified(tab, ["blocked"]);
+ }
+ ]]>
+ </handler>
+ <handler event="DOMAudioPlaybackBlockStopped">
+ <![CDATA[
+ var tab = getTabFromAudioEvent(event)
+ if (!tab) {
+ return;
+ }
+
+ if (tab.hasAttribute("blocked")) {
+ tab.removeAttribute("blocked");
+ this._tabAttrModified(tab, ["blocked"]);
+ }
+ ]]>
+ </handler>
</handlers>
</binding>
@@ -4758,10 +4906,18 @@
class="tab-icon-image"
role="presentation"
anonid="tab-icon"/>
+ <xul:image xbl:inherits="busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected"
+ anonid="overlay-icon"
+ class="tab-icon-overlay"
+ role="presentation"/>
<xul:label flex="1"
xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
class="tab-text tab-label"
role="presentation"/>
+ <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected"
+ anonid="soundplaying-icon"
+ class="tab-icon-sound"
+ role="presentation"/>
<xul:toolbarbutton anonid="close-button"
xbl:inherits="fadein,pinned,selected"
class="tab-close-button close-icon"/>
@@ -4782,9 +4938,59 @@
</property>
<field name="mOverCloseButton">false</field>
+ <property name="_overPlayingIcon" readonly="true">
+ <getter><![CDATA[
+ let iconVisible = this.hasAttribute("soundplaying") ||
+ this.hasAttribute("muted") ||
+ this.hasAttribute("blocked");
+ let soundPlayingIcon =
+ document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
+ let overlayIcon =
+ document.getAnonymousElementByAttribute(this, "anonid", "overlay-icon");
+
+ return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
+ (overlayIcon && overlayIcon.matches(":hover") && iconVisible);
+ ]]></getter>
+ </property>
<field name="mCorrespondingMenuitem">null</field>
<field name="closing">false</field>
<field name="lastAccessed">0</field>
+
+ <method name="toggleMuteAudio">
+ <parameter name="aMuteReason"/>
+ <body>
+ <![CDATA[
+ let tabContainer = this.parentNode;
+ let browser = this.linkedBrowser;
+ let modifiedAttrs = [];
+ if (browser.audioBlocked) {
+ this.removeAttribute("blocked");
+ modifiedAttrs.push("blocked");
+
+ // We don't want sound icon flickering between "blocked", "none" and
+ // "sound-playing", here adding the "soundplaying" is to keep the
+ // transition smoothly.
+ if (!this.hasAttribute("soundplaying")) {
+ this.setAttribute("soundplaying", true);
+ modifiedAttrs.push("soundplaying");
+ }
+
+ browser.resumeMedia();
+ } else {
+ if (browser.audioMuted) {
+ browser.unmute();
+ this.removeAttribute("muted");
+ } else {
+ browser.mute();
+ this.setAttribute("muted", "true");
+ }
+ this.muteReason = aMuteReason || null;
+ modifiedAttrs.push("muted");
+ }
+ tabContainer.tabbrowser._tabAttrModified(this, modifiedAttrs);
+ ]]>
+ </body>
+ </method>
</implementation>
<handlers>
@@ -4843,7 +5049,8 @@
if (this.selected) {
this.style.MozUserFocus = 'ignore';
this.clientTop; // just using this to flush style updates
- } else if (this.mOverCloseButton) {
+ } else if (this.mOverCloseButton ||
+ this._overPlayingIcon) {
// Prevent tabbox.xml from selecting the tab.
event.stopPropagation();
}
@@ -4852,6 +5059,17 @@
<handler event="mouseup">
this.style.MozUserFocus = '';
</handler>
+ <handler event="click">
+ <![CDATA[
+ if (event.button != 0) {
+ return;
+ }
+
+ if (this._overPlayingIcon) {
+ this.toggleMuteAudio();
+ }
+ ]]>
+ </handler>
</handlers>
</binding>
@@ -4957,6 +5175,24 @@
aMenuitem.setAttribute("selected", "true");
else
aMenuitem.removeAttribute("selected");
+
+ function addEndImage() {
+ let endImage = document.createElement("image");
+ endImage.setAttribute("class", "allTabs-endimage");
+ let endImageContainer = document.createElement("hbox");
+ endImageContainer.setAttribute("align", "center");
+ endImageContainer.setAttribute("pack", "center");
+ endImageContainer.appendChild(endImage);
+ aMenuitem.appendChild(endImageContainer);
+ return endImage;
+ }
+
+ if (aMenuitem.firstChild)
+ aMenuitem.firstChild.remove();
+ if (aTab.hasAttribute("muted"))
+ addEndImage().setAttribute("muted", "true");
+ else if (aTab.hasAttribute("soundplaying"))
+ addEndImage().setAttribute("soundplaying", "true");
]]></body>
</method>
</implementation>