Bug 1308153 - part2 : implement visual indicator for media-blocking. draft
authorAlastor Wu <alwu@mozilla.com>
Thu, 27 Oct 2016 15:40:48 +0800
changeset 430149 a06c5bf879015dfe54098d0ac5db128afa3c26bb
parent 430148 20ea9334f7d698157f0d32df1dd3e8576fa91f52
child 430150 bba3a5a3937c4d61cdd40f77cd8c1cc5692d6515
push id33751
push useralwu@mozilla.com
push dateThu, 27 Oct 2016 07:54:24 +0000
bugs1308153
milestone52.0a1
Bug 1308153 - part2 : implement visual indicator for media-blocking. MozReview-Commit-ID: EFDqxtgtD3n * * * new svg MozReview-Commit-ID: AW7tNveYGV3
browser/base/content/tabbrowser.css
browser/base/content/tabbrowser.xml
browser/themes/shared/tabbrowser/tab-audio-small.svg
browser/themes/shared/tabbrowser/tab-audio.svg
browser/themes/shared/tabs.inc.css
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -19,26 +19,27 @@
 }
 
 .tab-close-button[pinned],
 .tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([selected="true"]),
 .tab-icon-image:not([src]):not([pinned]):not([crashed])[selected],
 .tab-icon-image:not([src]):not([pinned]):not([crashed]):not([sharing]),
 .tab-icon-image[busy],
 .tab-throbber:not([busy]),
-.tab-icon-sound:not([soundplaying]):not([muted]),
+.tab-icon-sound:not([soundplaying]):not([muted]):not([blocked]),
 .tab-icon-sound[pinned],
 .tab-sharing-icon-overlay,
 .tab-icon-overlay {
   display: none;
 }
 
 .tab-sharing-icon-overlay[sharing]:not([selected]),
 .tab-icon-overlay[soundplaying][pinned],
 .tab-icon-overlay[muted][pinned],
+.tab-icon-overlay[blocked][pinned],
 .tab-icon-overlay[crashed] {
   display: -moz-box;
 }
 
 .tab-label[pinned] {
   width: 0;
   margin-left: 0 !important;
   margin-right: 0 !important;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5048,31 +5048,37 @@
               !event.isTrusted)
             return;
 
           var browser = event.originalTarget;
           var tab = this.getTabForBrowser(browser);
           if (!tab)
             return;
 
-          // TODO : implement media-blocking icon in next patch.
+          if (!tab.hasAttribute("blocked")) {
+            tab.setAttribute("blocked", true);
+            this._tabAttrModified(tab, ["blocked"]);
+          }
         ]]>
       </handler>
       <handler event="DOMAudioPlaybackBlockedEnd">
         <![CDATA[
           if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
               !event.isTrusted)
             return;
 
           var browser = event.originalTarget;
           var tab = this.getTabForBrowser(browser);
           if (!tab)
             return;
 
-          // TODO : implement media-blocking icon in next patch.
+          if (tab.hasAttribute("blocked")) {
+            tab.removeAttribute("blocked");
+            this._tabAttrModified(tab, ["blocked"]);
+          }
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tabbox">
     <implementation>
@@ -6506,25 +6512,25 @@
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,selected=visuallyselected"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,blocked,selected=visuallyselected"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected=visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
-          <xul:image xbl:inherits="soundplaying,pinned,muted,selected=visuallyselected"
+          <xul:image xbl:inherits="soundplaying,pinned,muted,blocked,selected=visuallyselected"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected=visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
@@ -6587,16 +6593,21 @@
           return this.getAttribute("hidden") == "true";
         </getter>
       </property>
       <property name="muted" readonly="true">
         <getter>
           return this.getAttribute("muted") == "true";
         </getter>
       </property>
+      <property name="blocked" readonly="true">
+        <getter>
+          return this.getAttribute("blocked") == "true";
+        </getter>
+      </property>
       <!--
       Describes how the tab ended up in this mute state. May be any of:
 
        - undefined: The tabs mute state has never changed.
        - null: The mute state was last changed through the UI.
        - Any string: The ID was changed through an extension API. The string
                      must be the ID of the extension which changed it.
       -->
@@ -6620,17 +6631,18 @@
       </method>
 
       <field name="cachePosition">Infinity</field>
 
       <field name="mOverCloseButton">false</field>
       <property name="_overPlayingIcon" readonly="true">
         <getter><![CDATA[
           let iconVisible = this.hasAttribute("soundplaying") ||
-                            this.hasAttribute("muted");
+                            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>
@@ -6693,26 +6705,41 @@
       </method>
 
       <method name="toggleMuteAudio">
         <parameter name="aMuteReason"/>
         <body>
         <![CDATA[
           let tabContainer = this.parentNode;
           let browser = this.linkedBrowser;
-          if (browser.audioMuted) {
-            browser.unmute();
-            this.removeAttribute("muted");
-            BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
+          if (browser.audioBlocked) {
+            this.removeAttribute("blocked");
+            tabContainer.tabbrowser._tabAttrModified(this, ["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);
+              tabContainer.tabbrowser._tabAttrModified(this, ["soundplaying"]);
+            }
+
+            browser.resumeMedia();
           } else {
-            browser.mute();
-            this.setAttribute("muted", "true");
-            BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
+            if (browser.audioMuted) {
+              browser.unmute();
+              this.removeAttribute("muted");
+              BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
+            } else {
+              browser.mute();
+              this.setAttribute("muted", "true");
+              BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
+            }
+            this.muteReason = aMuteReason || null;
           }
-          this.muteReason = aMuteReason || null;
           tabContainer.tabbrowser._tabAttrModified(this, ["muted"]);
         ]]>
         </body>
       </method>
 
       <method name="setUserContextId">
         <parameter name="aUserContextId"/>
         <body>
--- a/browser/themes/shared/tabbrowser/tab-audio-small.svg
+++ b/browser/themes/shared/tabbrowser/tab-audio-small.svg
@@ -17,16 +17,23 @@
 
     .icon.white {
       fill: #fff;
     }
     .icon.white > .outline {
       fill: #000;
       fill-opacity: .5;
     }
+
+    .st0 {
+      fill:#FFFFFF;
+    }
+    .st1 {
+      fill:#000333;
+    }
   </style>
 
   <g id="tab-audio" class="icon">
     <path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
     <path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
   </g>
   <g id="tab-audio-muted" class="icon">
     <path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
@@ -36,9 +43,23 @@
   <g id="tab-audio-white" class="icon white">
     <path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
     <path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
   </g>
   <g id="tab-audio-white-muted" class="icon white">
     <path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
     <path d="M11.5,3.5L9,5.9V3L6,6H4C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h0.9l-2.5,2.5l1.1,1.1l9-9L11.5,3.5z M9,13V9.7l-1.7,1.7L9,13z"/>
   </g>
+
+  <g id="tab-audio-blocked" class="icon">
+    <path id="outline" class="st0" d="M8,1.2C4.3,1.2,1.2,4.3,1.2,8s3.1,6.8,6.8,6.8s6.8-3.1,6.8-6.8S11.7,1.2,8,1.2z M8,11.9
+      c-2.1,0-3.9-1.7-3.9-3.9c0-2.1,1.7-3.9,3.9-3.9s3.9,1.7,3.9,3.9C11.9,10.1,10.1,11.9,8,11.9z M11.1,7.3L6.6,4.6L5.4,3.9v1.4v5.3V12
+      l1.2-0.7L11,8.6L12.2,8L11.1,7.3z"/>
+    <path id="play-tab" class="st1" d="M8,2C4.7,2,2,4.7,2,8s2.7,6,6,6s6-2.7,6-6S11.3,2,8,2z M8,12.7c-2.6,0-4.7-2.1-4.7-4.7
+      S5.4,3.3,8,3.3s4.7,2.1,4.7,4.7S10.6,12.7,8,12.7z M10.7,8L6.2,5.3v5.4L10.7,8z"/>
+  </g>
+  <g id="tab-audio-white-blocked" class="icon">
+    <path id="circle" class="st0" d="M8,0c3.3,0,6.4,2.2,7.5,5.3c1.1,3.1,0.1,6.7-2.5,8.9c-2.6,2.1-6.3,2.4-9.2,0.7
+      C1,13.1-0.5,9.8,0.1,6.5C0.9,2.8,4.2,0,8,0z"/>
+    <path id="play-tab" class="st1" d="M8,2C4.7,2,2,4.7,2,8s2.7,6,6,6s6-2.7,6-6S11.3,2,8,2z M8,12.7c-2.6,0-4.7-2.1-4.7-4.7
+      S5.4,3.3,8,3.3s4.7,2.1,4.7,4.7S10.6,12.7,8,12.7z M10.7,8L6.2,5.3v5.4L10.7,8z"/>
+  </g>
 </svg>
--- a/browser/themes/shared/tabbrowser/tab-audio.svg
+++ b/browser/themes/shared/tabbrowser/tab-audio.svg
@@ -2,14 +2,20 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
   <style>
     path:not(:target) {
       display: none;
     }
+    .st0 {
+      fill:#333333;
+    }
   </style>
 
   <path id="tab-audio" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
 
   <path id="tab-audio-muted" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
+
+  <path id="tab-audio-blocked" class="st0" d="M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0z M5.6,11.6l6-3.6l-6-3.6V11.6z M8,14.2
+  c-3.4,0-6.2-2.8-6.2-6.2S4.6,1.8,8,1.8s6.2,2.8,6.2,6.2S11.4,14.2,8,14.2z"/>
 </svg>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -134,43 +134,54 @@
   position: relative;
 }
 
 .tab-icon-overlay[crashed] {
   list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
 }
 
 .tab-icon-overlay[soundplaying],
-.tab-icon-overlay[muted]:not([crashed]) {
+.tab-icon-overlay[muted]:not([crashed]),
+.tab-icon-overlay[blocked]:not([crashed]) {
   border-radius: 10px;
 }
 
 .tab-icon-overlay[soundplaying]:hover,
-.tab-icon-overlay[muted]:not([crashed]):hover {
+.tab-icon-overlay[muted]:not([crashed]):hover,
+.tab-icon-overlay[blocked]:not([crashed]):hover {
   background-color: white;
 }
 
 .tab-icon-overlay[soundplaying] {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
 }
 
 .tab-icon-overlay[muted]:not([crashed]) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
 }
 
+.tab-icon-overlay[blocked]:not([crashed]) {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-blocked");
+}
+
 #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not([selected]):not(:hover),
 .tab-icon-overlay[soundplaying][selected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white");
 }
 
 #TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not([selected]):not(:hover),
 .tab-icon-overlay[muted][selected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted");
 }
 
+#TabsToolbar[brighttext] .tab-icon-overlay[blocked]:not([crashed]):not([selected]):not(:hover),
+.tab-icon-overlay[blocked][selected]:-moz-lwtheme-brighttext:not(:hover) {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-blocked");
+}
+
 .tab-throbber[busy] {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
   list-style-image: url("chrome://global/skin/icons/loading.png");
 }
 
@@ -188,38 +199,46 @@
 .tab-icon-sound {
   margin-inline-start: 4px;
   width: 16px;
   height: 16px;
   padding: 0;
 }
 
 .tab-icon-sound[soundplaying],
-.tab-icon-sound[muted] {
+.tab-icon-sound[muted],
+.tab-icon-sound[blocked] {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio);
   filter: url(chrome://browser/skin/filters.svg#fill);
   fill: currentColor;
 }
 
 .tab-icon-sound[muted] {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted);
 }
 
+.tab-icon-sound[blocked] {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-blocked);
+}
+
 .tab-icon-sound:-moz-lwtheme-darktext[soundplaying],
-.tab-icon-sound:-moz-lwtheme-darktext[muted] {
+.tab-icon-sound:-moz-lwtheme-darktext[muted],
+.tab-icon-sound:-moz-lwtheme-darktext[blocked] {
   filter: url(chrome://browser/skin/filters.svg#fill) drop-shadow(1px 1px 1px white);
 }
 
 .tab-icon-sound:-moz-lwtheme-brighttext[soundplaying],
-.tab-icon-sound:-moz-lwtheme-brighttext[muted] {
+.tab-icon-sound:-moz-lwtheme-brighttext[muted],
+.tab-icon-sound:-moz-lwtheme-brighttext[blocked] {
   filter: url(chrome://browser/skin/filters.svg#fill) drop-shadow(1px 1px 1px black);
 }
 
 .tab-icon-sound[soundplaying]:not(:hover),
-.tab-icon-sound[muted]:not(:hover) {
+.tab-icon-sound[muted]:not(:hover),
+.tab-icon-sound[blocked]:not(:hover)  {
   opacity: .8;
 }
 
 .tab-background,
 .tabs-newtab-button {
   /* overlap the tab curves */
   margin-inline-end: -@tabCurveHalfWidth@;
   margin-inline-start: -@tabCurveHalfWidth@;
@@ -396,16 +415,17 @@
 .tabbrowser-tab {
   pointer-events: none;
 }
 
 .tab-background-middle,
 .tabs-newtab-button,
 .tab-icon-overlay[soundplaying],
 .tab-icon-overlay[muted]:not([crashed]),
+.tab-icon-overlay[blocked]:not([crashed]),
 .tab-icon-sound,
 .tab-close-button {
   pointer-events: auto;
 }
 
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
@@ -519,17 +539,22 @@
 /* All tabs menupopup */
 
 .alltabs-item[tabIsVisible] {
   /* box-shadow instead of background-color to work around native styling */
   box-shadow: inset -5px 0 ThreeDShadow;
 }
 
 .alltabs-endimage[soundplaying],
-.alltabs-endimage[muted] {
+.alltabs-endimage[muted],
+.alltabs-endimage[blocked] {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio);
   filter: url(chrome://browser/skin/filters.svg#fill);
   fill: currentColor;
 }
 
 .alltabs-endimage[muted] {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted);
 }
+
+.alltabs-endimage[blocked] {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-blocked);
+}