Bug 1018504 - (Part 1) Add indicator to show which tab is playing sound. r=mhaigh
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Fri, 31 Jul 2015 11:08:26 -0700
changeset 287592 d065e876a83b7903f87b54cb2f1ea7c6fd157ba7
parent 287591 d192b58f439bac3f982aeec3cad74b67972b648d
child 287593 23e480296ddcfdea0cf7950b22d88d3c70a29297
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmhaigh
bugs1018504
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1018504 - (Part 1) Add indicator to show which tab is playing sound. r=mhaigh
mobile/android/app/mobile.js
mobile/android/base/Tab.java
mobile/android/base/Tabs.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/resources/drawable-hdpi/tab_audio_playing.png
mobile/android/base/resources/drawable-xhdpi/tab_audio_playing.png
mobile/android/base/resources/layout-large-v11/tab_strip_item_view.xml
mobile/android/base/resources/layout-v11/new_tablet_tabs_item_cell.xml
mobile/android/base/resources/layout/tabs_item_cell.xml
mobile/android/base/resources/layout/tabs_item_row.xml
mobile/android/base/strings.xml.in
mobile/android/base/tabs/TabStrip.java
mobile/android/base/tabs/TabStripItemView.java
mobile/android/base/tabs/TabsGridLayout.java
mobile/android/base/tabs/TabsLayoutItemView.java
mobile/android/base/tabs/TabsListLayout.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -929,8 +929,11 @@ pref("consoleservice.logcat", true);
 // Enable Service Workers for Android on non-release builds
 #ifndef RELEASE_BUILD
 pref("dom.serviceWorkers.enabled", true);
 pref("dom.serviceWorkers.interception.enabled", true);
 #endif
 
 // Enable Cardboard VR on mobile, assuming VR at all is enabled
 pref("dom.vr.cardboard.enabled", true);
+
+// TODO: Disabled until bug 1190301 is fixed.
+pref("browser.tabs.showAudioPlayingIcon", false);
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -76,16 +76,17 @@ public class Tab {
     private int mState;
     private Bitmap mThumbnailBitmap;
     private boolean mDesktopMode;
     private boolean mEnteringReaderMode;
     private final Context mAppContext;
     private ErrorType mErrorType = ErrorType.NONE;
     private volatile int mLoadProgress;
     private volatile int mRecordingCount;
+    private volatile boolean mIsAudioPlaying;
     private String mMostRecentHomePanel;
 
     private int mHistoryIndex;
     private int mHistorySize;
     private boolean mCanDoBack;
     private boolean mCanDoForward;
 
     private boolean mIsEditing;
@@ -901,16 +902,24 @@ public class Tab {
             mRecordingCount--;
         }
     }
 
     public boolean isRecording() {
         return mRecordingCount > 0;
     }
 
+    public void setIsAudioPlaying(boolean isAudioPlaying) {
+        mIsAudioPlaying = isAudioPlaying;
+    }
+
+    public boolean isAudioPlaying() {
+        return mIsAudioPlaying;
+    }
+
     public boolean isEditing() {
         return mIsEditing;
     }
 
     public void setIsEditing(final boolean isEditing) {
         this.mIsEditing = isEditing;
     }
 
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -113,17 +113,18 @@ public class Tabs implements GeckoEventL
             "DOMContentLoaded",
             "DOMTitleChanged",
             "Link:Favicon",
             "Link:Feed",
             "Link:OpenSearch",
             "DesktopMode:Changed",
             "Tab:ViewportMetadata",
             "Tab:StreamStart",
-            "Tab:StreamStop");
+            "Tab:StreamStop",
+            "Tab:AudioPlayingChange");
 
     }
 
     public synchronized void attachToContext(Context context) {
         final Context appContext = context.getApplicationContext();
         if (mAppContext == appContext) {
             return;
         }
@@ -576,16 +577,19 @@ public class Tabs implements GeckoEventL
                 tab.setIsRTL(message.getBoolean("isRTL"));
                 notifyListeners(tab, TabEvents.VIEWPORT_CHANGE);
             } else if (event.equals("Tab:StreamStart")) {
                 tab.setRecording(true);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
             } else if (event.equals("Tab:StreamStop")) {
                 tab.setRecording(false);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
+            } else if (event.equals("Tab:AudioPlayingChange")) {
+                tab.setIsAudioPlaying(message.getBoolean("isAudioPlaying"));
+                notifyListeners(tab, TabEvents.AUDIO_PLAYING_CHANGE);
             }
 
         } catch (Exception e) {
             Log.w(LOGTAG, "handleMessage threw for " + event, e);
         }
     }
 
     public void refreshThumbnails() {
@@ -636,16 +640,17 @@ public class Tabs implements GeckoEventL
         SECURITY_CHANGE,
         DESKTOP_MODE_CHANGE,
         VIEWPORT_CHANGE,
         RECORDING_CHANGE,
         BOOKMARK_ADDED,
         BOOKMARK_REMOVED,
         READING_LIST_ADDED,
         READING_LIST_REMOVED,
+        AUDIO_PLAYING_CHANGE,
     }
 
     public void notifyListeners(Tab tab, TabEvents msg) {
         notifyListeners(tab, msg, "");
     }
 
     public void notifyListeners(final Tab tab, final TabEvents msg, final Object data) {
         if (tab == null &&
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -63,16 +63,18 @@
 <!ENTITY forward "Forward">
 <!ENTITY menu "Menu">
 <!ENTITY back "Back">
 <!ENTITY stop "Stop">
 <!ENTITY site_security "Site Security">
 <!ENTITY edit_mode_cancel "Cancel">
 
 <!ENTITY close_tab "Close Tab">
+<!ENTITY tab_audio_playing "This tab is playing audio">
+<!ENTITY tab_audio_muted "This tab has been muted">
 <!ENTITY one_tab "1 tab">
 <!-- Localization note (num_tabs2) : Number of tabs is always more than one.
      We can't use android plural forms, sadly. See bug #753859. -->
 <!ENTITY num_tabs2 "&formatD; tabs">
 <!ENTITY new_tab_opened "New tab opened">
 <!ENTITY new_private_tab_opened "New private tab opened">
 <!-- Localization note (switch_button_message): This string should be as short
      as possible because it's shown as a label in a toast.  Ideally, this string
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..183691759b03f64034334bef6965c8cb734d22fe
GIT binary patch
literal 579
zc$@)40=)f+P)<h;3K|Lk000e1NJLTq000#L000sQ1^@s6v=Wsr00001b5ch_0Itp)
z=>Px${YgYYR5%f(ls$;lKorN{n`F<GT@F2LtOUhIv^lU4s|dQ$%UrNHJHf_l?G_6Q
z4@BiU!N#)@EUvIPcPDxq3)A>fSSnV2U}Yny=Z4#JlX<>bJQ6nUM%{r)9&g_J<v%m;
z1>rA87>2dYt^REe_bs&Mr9Ay?RH_)YkoN~cuz$1He@R3ac<xSg>g3ml?NX>xk5BbA
zydt0@Su$CfU9`qR*IKH{s@8#MrkI*}qA04awS&cNkFAtCm=Z4Vwx+h7=8XumL?H9N
zQT9f+dmaexu!vn}eYtEW8rK2nz;FTsONUYTJ&tF6&%67u6`TZwrFJ`fz}qhZOKHCY
zHoEaON-5-vJY3yjUt%GahM$XL&$J9Kpmq}xyM0Z|uIqkAfTut>HxX1y=L>(hgMh~e
z0ubKv5$u}?YCy!q0WkNN2&z=H2MHQn!_Nmyf<s&dpC*E8h%a*~)++*=ola*j7t9Gj
zdc;tCpHu!%=5)Pw1ublNp7*A=mE7XmP6bS@*aB>qFgf=>#qT&yk5#;ADRoh6S`a9j
z^Rn^L_I>vqFI@kVMAN41zrl0tF-_O&_rLv!(U)^8hEupX<IE59LrF-l4)y;Mx8v1(
zT;3k5zywNp=1E1sQLdcVSt6#HU9?J}CWSBJ!_I2u7<bP-o?mTYMMYHR`32csyB~D~
R@+trT002ovPDHLkV1oEv2+jZi
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..82098c4d922d61a9a4fc816c9d9b3795fd0476e3
GIT binary patch
literal 741
zc$@*{0vi2^P)<h;3K|Lk000e1NJLTq000~S000;W1^@s6>71Q{00001b5ch_0Itp)
z=>Px%pGibPR7efAmCb7tQ4qjqUQ(l2t?0q8Q1IYI1U;naAK(W~cC*RFlNS+0Xy{EZ
z^&)!GgHR&af_U*Hh}rCJiXRY=9+c*wAYMEOB`6*&ttc9sedD~1p-IX%FS^~s&YQRM
z<~KX@-aLeVeC+x89k8@~M*`##;9f3kOzF51I!3)Vj^iArugk9_(M2HIO#~CYHevd}
zK-+aQO^FKxbY#sbjjW9~9%%!K2yMr^CVeUc=mUE3TEJ?xI&7D{$5P50>_BTx8&ocP
zMtiJe-^Hq4E8l*+<`m{T7wU{vMtqV^$#!nF?c=H@<kKtx9B5B=$|i3^PGRSi_Lz_s
z5V67KxWa$>GMmk~f+Wm@!L&X17?vDw!t=a+Im37gh*#BT339qpsU!r`5BAvqmulNi
zfut<1HfgR_s|{I3y2#LXd~^;qd>HRr!$IjnY%Oym_{23oUjO{%5~oxO#0P;NlIJ1^
zn&#<6LNWOla9+t6(laHI;8^59<#>48mKRDgB4`dmh{cu)$xQ(U&O=_C0{s5-_o)_-
z-YdxsV>HkS>X8E-=lLj-oDKje9xCY*o>7tz-bN0T@>f=!YXp3g+`o60kDcIK<$?kN
zibo8@dZ0w~vMjTRLKKGLIG#$SR&BRZ;4ADXB7985U@k6VaG4~|MA-mxX6CMnzvQ}S
zK~m611&GOXI^Bp{hT-wIWY`a?=Iu)?b>Dws*4^_+I3`IZxDnh|eN=tzTSL<{KaA|$
zeu8WAq}yG8%`!7LV<^m=Wd{9Jtzfv`!zstITbbf5ew|mW3p8(;TE5mT48wCzBo(oU
zVV2`u3A5`ruMKQFQZ<hY8siiPhq^VdAHcvs1yFUxJ-bfwEpVT&o+aM$!LDovwUPV-
XtZn-TOmrtY00000NkvXXu0mjfNlsI?
--- a/mobile/android/base/resources/layout-large-v11/tab_strip_item_view.xml
+++ b/mobile/android/base/resources/layout-large-v11/tab_strip_item_view.xml
@@ -24,16 +24,27 @@
         android:textSize="14sp"
         android:ellipsize="end"
         android:textColor="@color/new_tablet_tab_strip_item_title"
         android:maxLines="1"
         gecko:fadeWidth="30dip"
         android:duplicateParentState="true"/>
 
     <org.mozilla.gecko.widget.ThemedImageButton
+        android:id="@+id/audio_playing"
+        android:visibility="gone"
+        android:layout_width="20dip"
+        android:layout_height="match_parent"
+        android:layout_marginRight="-15dp"
+        android:background="@drawable/action_bar_button_inverse"
+        android:scaleType="center"
+        android:contentDescription="@string/tab_audio_playing"
+        android:src="@drawable/tab_audio_playing"/>
+
+    <org.mozilla.gecko.widget.ThemedImageButton
         android:id="@+id/close"
         android:layout_width="40dip"
         android:layout_height="match_parent"
         android:background="@android:color/transparent"
         android:scaleType="center"
         android:contentDescription="@string/close_tab"
         android:src="@drawable/new_tablet_tab_close"
         android:duplicateParentState="true"/>
--- a/mobile/android/base/resources/layout-v11/new_tablet_tabs_item_cell.xml
+++ b/mobile/android/base/resources/layout-v11/new_tablet_tabs_item_cell.xml
@@ -28,16 +28,24 @@
                style="@style/TabLayoutItemTextAppearance"
                android:textSize="14sp"
                android:textColor="@color/new_tablet_tab_item_title"
                android:singleLine="true"
                android:duplicateParentState="true"
                gecko:fadeWidth="15dp"
                android:paddingRight="5dp"/>
 
+          <ImageButton android:id="@+id/audio_playing"
+                       android:visibility="gone"
+                       android:layout_width="20dip"
+                       android:layout_height="20dip"
+                       android:background="@drawable/action_bar_button_inverse"
+                       android:scaleType="center"
+                       android:contentDescription="@string/tab_audio_playing"
+                       android:src="@drawable/tab_audio_playing"/>
 
         <!-- Use of baselineAlignBottom only supported from API 11+ - if this needs to work on lower API versions
              we'll need to override getBaseLine() and return image height, but we assume this won't happen -->
         <ImageView android:id="@+id/close"
                      style="@style/TabsItemClose"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:scaleType="center"
--- a/mobile/android/base/resources/layout/tabs_item_cell.xml
+++ b/mobile/android/base/resources/layout/tabs_item_cell.xml
@@ -41,16 +41,25 @@
                       android:layout_weight="1.0"
                       android:padding="4dip"
                       style="@style/TabLayoutItemTextAppearance"
                       android:textSize="12sp"
                       android:textColor="@color/placeholder_active_grey"
                       android:singleLine="true"
                       android:duplicateParentState="true"/>
 
+            <ImageButton android:id="@+id/audio_playing"
+                         android:visibility="gone"
+                         android:layout_width="20dip"
+                         android:layout_height="match_parent"
+                         android:background="@drawable/action_bar_button_inverse"
+                         android:scaleType="center"
+                         android:contentDescription="@string/tab_audio_playing"
+                         android:src="@drawable/tab_audio_playing"/>
+
             <ImageButton android:id="@+id/close"
                          style="@style/TabsItemClose"
                          android:layout_width="32dip"
                          android:layout_height="match_parent"
                          android:background="@drawable/action_bar_button_inverse"
                          android:scaleType="center"
                          android:contentDescription="@string/close_tab"
                          android:src="@drawable/tab_close"/>
--- a/mobile/android/base/resources/layout/tabs_item_row.xml
+++ b/mobile/android/base/resources/layout/tabs_item_row.xml
@@ -23,29 +23,46 @@
                   android:duplicateParentState="true">
 
         <org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
                                                 android:layout_width="@dimen/tab_thumbnail_width"
                                                 android:layout_height="@dimen/tab_thumbnail_height"/>
 
     </org.mozilla.gecko.widget.TabThumbnailWrapper>
 
-    <TextView android:id="@+id/title"
-              android:layout_width="0dip"
-              android:layout_height="match_parent"
-              android:layout_weight="1.0"
-              android:paddingTop="4dip"
-              android:paddingLeft="8dip"
-              android:paddingRight="4dip"
-              style="@style/TabLayoutItemTextAppearance"
-              android:textColor="#FFFFFFFF"
-              android:textSize="14sp"
-              android:singleLine="false"
-              android:maxLines="4"
-              android:duplicateParentState="true"/>
+    <LinearLayout android:layout_width="0dip"
+                  android:layout_height="match_parent"
+                  android:orientation="vertical"
+                  android:layout_weight="1.0"
+                  android:paddingTop="4dip"
+                  android:paddingLeft="8dip"
+                  android:paddingRight="4dip">
+
+        <TextView android:id="@+id/title"
+                  android:layout_width="match_parent"
+                  android:layout_height="0dip"
+                  android:layout_weight="1.0"
+                  style="@style/TabLayoutItemTextAppearance"
+                  android:textColor="#FFFFFFFF"
+                  android:textSize="14sp"
+                  android:singleLine="false"
+                  android:maxLines="4"
+                  android:duplicateParentState="true"/>
+
+        <ImageButton android:id="@+id/audio_playing"
+                     android:visibility="gone"
+                     android:layout_width="20dip"
+                     android:layout_height="20dip"
+                     android:gravity="bottom"
+                     android:background="@drawable/action_bar_button_inverse"
+                     android:scaleType="center"
+                     android:contentDescription="@string/tab_audio_playing"
+                     android:src="@drawable/tab_audio_playing"/>
+
+    </LinearLayout>
 
     <ImageButton android:id="@+id/close"
                  style="@style/TabsItemClose"
                  android:layout_width="34dip"
                  android:layout_height="match_parent"
                  android:background="@drawable/action_bar_button_inverse"
                  android:scaleType="center"
                  android:contentDescription="@string/close_tab"
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -305,16 +305,18 @@
   <string name="search">&search;</string>
   <string name="reload">&reload;</string>
   <string name="forward">&forward;</string>
   <string name="menu">&menu;</string>
   <string name="back">&back;</string>
   <string name="stop">&stop;</string>
   <string name="site_security">&site_security;</string>
   <string name="close_tab">&close_tab;</string>
+  <string name="tab_audio_playing">&tab_audio_playing;</string>
+  <string name="tab_audio_muted">&tab_audio_muted;</string>
   <string name="new_tab_opened">&new_tab_opened;</string>
   <string name="new_private_tab_opened">&new_private_tab_opened;</string>
   <string name="switch_button_message">&switch_button_message;</string>
   <string name="one_tab">&one_tab;</string>
   <string name="num_tabs">&num_tabs2;</string>
   <string name="addons">&addons;</string>
   <string name="logins">&logins;</string>
   <string name="downloads">&downloads;</string>
--- a/mobile/android/base/tabs/TabStrip.java
+++ b/mobile/android/base/tabs/TabStrip.java
@@ -119,16 +119,17 @@ public class TabStrip extends ThemedLine
                     // Update the selected position, then fall through...
                     tabStripView.selectTab(tab);
                     setPrivateMode(tab.isPrivate());
                 case UNSELECTED:
                     // We just need to update the style for the unselected tab...
                 case TITLE:
                 case FAVICON:
                 case RECORDING_CHANGE:
+                case AUDIO_PLAYING_CHANGE:
                     tabStripView.updateTab(tab);
                     break;
             }
         }
     }
 
     @Override
     public void refresh() {
--- a/mobile/android/base/tabs/TabStripItemView.java
+++ b/mobile/android/base/tabs/TabStripItemView.java
@@ -39,16 +39,17 @@ public class TabStripItemView extends Th
     };
 
     private int id = -1;
     private boolean checked;
 
     private final ImageView faviconView;
     private final ThemedTextView titleView;
     private final ThemedImageButton closeView;
+    private final ThemedImageButton audioPlayingView;
 
     private final ResizablePathDrawable backgroundDrawable;
     private final Region tabRegion;
     private final Region tabClipRegion;
     private boolean tabRegionNeedsUpdate;
 
     private final int faviconSize;
     private Bitmap lastFavicon;
@@ -95,16 +96,18 @@ public class TabStripItemView extends Th
                 if (id < 0) {
                     throw new IllegalStateException("Invalid tab id:" + id);
                 }
 
                 final Tabs tabs = Tabs.getInstance();
                 tabs.closeTab(tabs.getTab(id), true);
             }
         });
+
+        audioPlayingView = (ThemedImageButton) findViewById(R.id.audio_playing);
     }
 
     @Override
     protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
         super.onSizeChanged(width, height, oldWidth, oldHeight);
 
         // Queue a tab region update in the next draw() call. We don't
         // update it immediately here because we need the new path from
@@ -186,16 +189,17 @@ public class TabStripItemView extends Th
             return;
         }
 
         id = tab.getId();
 
         updateTitle(tab);
         updateFavicon(tab.getFavicon());
         setPrivateMode(tab.isPrivate());
+        audioPlayingView.setVisibility(tab.isAudioPlaying() ? View.VISIBLE : View.GONE);
     }
 
     private void updateTitle(Tab tab) {
         final String title;
 
         // Avoid flickering the about:home URL on every load given how often
         // this page is used in the UI.
         if (AboutPages.isAboutHome(tab.getURL())) {
--- a/mobile/android/base/tabs/TabsGridLayout.java
+++ b/mobile/android/base/tabs/TabsGridLayout.java
@@ -233,16 +233,17 @@ class TabsGridLayout extends GridView
             case SELECTED:
                 // Update the selected position, then fall through...
                 updateSelectedPosition();
             case UNSELECTED:
                 // We just need to update the style for the unselected tab...
             case THUMBNAIL:
             case TITLE:
             case RECORDING_CHANGE:
+            case AUDIO_PLAYING_CHANGE:
                 View view = getChildAt(mTabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
                 if (view == null)
                     return;
 
                 ((TabsLayoutItemView) view).assignValues(tab);
                 break;
         }
     }
--- a/mobile/android/base/tabs/TabsLayoutItemView.java
+++ b/mobile/android/base/tabs/TabsLayoutItemView.java
@@ -28,16 +28,17 @@ public class TabsLayoutItemView extends 
     private static final String LOGTAG = "Gecko" + TabsLayoutItemView.class.getSimpleName();
     private static final int[] STATE_CHECKED = { android.R.attr.state_checked };
     private boolean mChecked;
 
     private int mTabId;
     private TextView mTitle;
     private ImageView mThumbnail;
     private ImageView mCloseButton;
+    private ImageView mAudioPlayingButton;
     private TabThumbnailWrapper mThumbnailWrapper;
 
     public TabsLayoutItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     @Override
     public int[] onCreateDrawableState(int extraSpace) {
@@ -88,16 +89,17 @@ public class TabsLayoutItemView extends 
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTitle = (TextView) findViewById(R.id.title);
         mThumbnail = (ImageView) findViewById(R.id.thumbnail);
         mCloseButton = (ImageView) findViewById(R.id.close);
+        mAudioPlayingButton = (ImageView) findViewById(R.id.audio_playing);
         mThumbnailWrapper = (TabThumbnailWrapper) findViewById(R.id.wrapper);
 
         if (HardwareUtils.isTablet()) {
             growCloseButtonHitArea();
         }
     }
 
     private void growCloseButtonHitArea() {
@@ -133,17 +135,17 @@ public class TabsLayoutItemView extends 
         Drawable thumbnailImage = tab.getThumbnail();
         mThumbnail.setImageDrawable(thumbnailImage);
 
         if (mThumbnailWrapper != null) {
             mThumbnailWrapper.setRecording(tab.isRecording());
         }
         mTitle.setText(tab.getDisplayTitle());
         mCloseButton.setTag(this);
-
+        mAudioPlayingButton.setVisibility(tab.isAudioPlaying() ? View.VISIBLE : View.GONE);
     }
 
     public int getTabId() {
         return mTabId;
     }
 
     public void setThumbnail(Drawable thumbnail) {
         mThumbnail.setImageDrawable(thumbnail);
--- a/mobile/android/base/tabs/TabsListLayout.java
+++ b/mobile/android/base/tabs/TabsListLayout.java
@@ -173,16 +173,17 @@ class TabsListLayout extends TwoWayView
             case SELECTED:
                 // Update the selected position, then fall through...
                 updateSelectedPosition();
             case UNSELECTED:
                 // We just need to update the style for the unselected tab...
             case THUMBNAIL:
             case TITLE:
             case RECORDING_CHANGE:
+            case AUDIO_PLAYING_CHANGE:
                 View view = getChildAt(mTabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
                 if (view == null)
                     return;
 
                 TabsLayoutItemView item = (TabsLayoutItemView) view;
                 item.assignValues(tab);
                 break;
         }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3573,16 +3573,18 @@ Tab.prototype = {
     this.browser.sessionHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
     this.browser.addEventListener("DOMFormHasPassword", this, true);
     this.browser.addEventListener("DOMLinkAdded", this, true);
     this.browser.addEventListener("DOMLinkChanged", this, true);
     this.browser.addEventListener("DOMMetaAdded", this, false);
     this.browser.addEventListener("DOMTitleChanged", this, true);
+    this.browser.addEventListener("DOMMediaPlaybackStarted", this, true);
+    this.browser.addEventListener("DOMMediaPlaybackStopped", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.addEventListener("DOMAutoComplete", this, true);
     this.browser.addEventListener("blur", this, true);
     this.browser.addEventListener("scroll", this, true);
     this.browser.addEventListener("MozScrolledAreaChanged", this, true);
     this.browser.addEventListener("pageshow", this, true);
     this.browser.addEventListener("MozApplicationManifest", this, true);
@@ -3755,16 +3757,18 @@ Tab.prototype = {
     this.browser.sessionHistory.removeSHistoryListener(this);
 
     this.browser.removeEventListener("DOMContentLoaded", this, true);
     this.browser.removeEventListener("DOMFormHasPassword", this, true);
     this.browser.removeEventListener("DOMLinkAdded", this, true);
     this.browser.removeEventListener("DOMLinkChanged", this, true);
     this.browser.removeEventListener("DOMMetaAdded", this, false);
     this.browser.removeEventListener("DOMTitleChanged", this, true);
+    this.browser.removeEventListener("DOMMediaPlaybackStarted", this, true);
+    this.browser.removeEventListener("DOMMediaPlaybackStopped", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("DOMAutoComplete", this, true);
     this.browser.removeEventListener("blur", this, true);
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
     this.browser.removeEventListener("pageshow", this, true);
     this.browser.removeEventListener("MozApplicationManifest", this, true);
@@ -4379,16 +4383,36 @@ Tab.prototype = {
         Messaging.sendRequest({
           type: "DOMTitleChanged",
           tabID: this.id,
           title: truncate(aEvent.target.title, MAX_TITLE_LENGTH)
         });
         break;
       }
 
+      case "DOMMediaPlaybackStarted":
+      case "DOMMediaPlaybackStopped": {
+        if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
+            !aEvent.isTrusted) {
+          return;
+        }
+
+        let browser = aEvent.originalTarget;
+        if (browser != this.browser) {
+          return;
+        }
+
+        Messaging.sendRequest({
+          type: "Tab:AudioPlayingChange",
+          tabID: this.id,
+          isAudioPlaying: aEvent.type === "DOMMediaPlaybackStarted"
+        });
+        return;
+      }
+
       case "DOMWindowClose": {
         if (!aEvent.isTrusted)
           return;
 
         // Find the relevant tab, and close it from Java
         if (this.browser.contentWindow == aEvent.target) {
           aEvent.preventDefault();