Bug 1347791 - part3 : update block stauts to front-end side. draft
authorAlastor Wu <alwu@mozilla.com>
Thu, 29 Jun 2017 05:46:20 -0700
changeset 602018 36379253c46c115323f0c3d4a3047f337965765b
parent 602017 25f23cd7c103ca6287896b4103e90a1629959472
child 602019 9ab7ba94ce6f76b5295411e2dcd0beb5813cf524
push id66245
push useralwu@mozilla.com
push dateThu, 29 Jun 2017 13:14:24 +0000
bugs1347791
milestone56.0a1
Bug 1347791 - part3 : update block stauts to front-end side. When the block stauts of the window was changed, we would notify front-end side to update the vaule, so that we can save it for session restore. MozReview-Commit-ID: FyclKmAxZHf
browser/base/content/tabbrowser.xml
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
toolkit/content/browser-content.js
toolkit/content/tests/browser/browser_mediaPlayback_suspended.js
toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js
toolkit/content/widgets/browser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5076,17 +5076,17 @@
           } else if (tab._overPlayingIcon) {
             let stringID;
             if (tab.selected) {
               stringID = tab.linkedBrowser.audioMuted ?
                 "tabs.unmuteAudio.tooltip" :
                 "tabs.muteAudio.tooltip";
               label = stringWithShortcut(stringID, "key_toggleMute");
             } else {
-              if (tab.linkedBrowser.audioBlocked) {
+              if (tab.hasAttribute("activemedia-blocked")) {
                 stringID = "tabs.unblockAudio.tooltip";
               } else {
                 stringID = tab.linkedBrowser.audioMuted ?
                   "tabs.unmuteAudio.background.tooltip" :
                   "tabs.muteAudio.background.tooltip";
               }
 
               label = this.mStringBundle.getString(stringID);
@@ -7577,17 +7577,17 @@
             return;
           }
 
           let tabContainer = this.parentNode;
           let browser = this.linkedBrowser;
           let modifiedAttrs = [];
           let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
 
-          if (browser.audioBlocked) {
+          if (this.hasAttribute("activemedia-blocked")) {
             this.removeAttribute("activemedia-blocked");
             modifiedAttrs.push("activemedia-blocked");
 
             browser.resumeMedia();
             hist.add(3 /* unblockByClickingIcon */);
             this.finishMediaBlockTimer();
           } else {
             if (browser.audioMuted) {
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -651,20 +651,16 @@ AudioChannelService::NotifyMediaResumedF
     return;
   }
 
   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
   if (!winData) {
     return;
   }
 
-  if (!winData->mShouldSendBlockStopEvent) {
-    return;
-  }
-
   winData->NotifyMediaBlockStop(aWindow);
 }
 
 void
 AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
 
@@ -855,32 +851,50 @@ AudioChannelService::AudioChannelWindow:
   AudioAudibleChanged(aAgent,
                       AudibleState::eNotAudible,
                       AudibleChangedReasons::ePauseStateChanged);
 }
 
 void
 AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow)
 {
-  mShouldSendBlockStopEvent = false;
   // Can't use raw pointer for lamba variable capturing, use smart ptr.
   nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
   NS_DispatchToCurrentThread(NS_NewRunnableFunction(
     "dom::AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop",
     [window]() -> void {
       nsCOMPtr<nsIObserverService> observerService =
         services::GetObserverService();
       if (NS_WARN_IF(!observerService)) {
         return;
       }
 
       observerService->NotifyObservers(ToSupports(window),
                                        "audio-playback",
-                                       u"blockStop");
-    }));
+                                       u"mediaBlockStop");
+    })
+  );
+
+  if (mShouldSendActiveMediaBlockStopEvent) {
+    mShouldSendActiveMediaBlockStopEvent = false;
+    NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+      "dom::AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop",
+      [window]() -> void {
+        nsCOMPtr<nsIObserverService> observerService =
+          services::GetObserverService();
+        if (NS_WARN_IF(!observerService)) {
+          return;
+        }
+
+        observerService->NotifyObservers(ToSupports(window),
+                                         "audio-playback",
+                                         u"activeMediaBlockStop");
+      })
+    );
+  }
 }
 
 void
 AudioChannelService::AudioChannelWindow::AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
   MOZ_ASSERT(!mAgents.Contains(aAgent));
 
@@ -1032,29 +1046,29 @@ AudioChannelService::AudioChannelWindow:
     return;
   }
 
   if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK ||
       !doc->Hidden()) {
     return;
   }
 
-  if (!mShouldSendBlockStopEvent) {
-      mShouldSendBlockStopEvent = true;
+  if (!mShouldSendActiveMediaBlockStopEvent) {
+      mShouldSendActiveMediaBlockStopEvent = true;
       NS_DispatchToCurrentThread(NS_NewRunnableFunction(
         "dom::AudioChannelService::AudioChannelWindow::"
         "MaybeNotifyMediaBlockStart",
         [window]() -> void {
           nsCOMPtr<nsIObserverService> observerService =
             services::GetObserverService();
           if (NS_WARN_IF(!observerService)) {
             return;
           }
 
           observerService->NotifyObservers(
-            ToSupports(window), "audio-playback", u"blockStart");
+            ToSupports(window), "audio-playback", u"activeMediaBlockStart");
         }));
   }
 }
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -177,17 +177,17 @@ private:
 
   class AudioChannelWindow final
   {
   public:
     explicit AudioChannelWindow(uint64_t aWindowID)
       : mWindowID(aWindowID)
       , mIsAudioCaptured(false)
       , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting())
-      , mShouldSendBlockStopEvent(false)
+      , mShouldSendActiveMediaBlockStopEvent(false)
     {}
 
     void AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent);
     void AudioAudibleChanged(AudioChannelAgent* aAgent,
                              AudibleState aAudible,
                              AudibleChangedReasons aReason);
 
     void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible);
@@ -202,19 +202,20 @@ private:
     // Raw pointer because the AudioChannelAgent must unregister itself.
     nsTObserverArray<AudioChannelAgent*> mAgents;
     nsTObserverArray<AudioChannelAgent*> mAudibleAgents;
 
     // Owning audio focus when the window starts playing audible sound, and
     // lose audio focus when other windows starts playing.
     bool mOwningAudioFocus;
 
-    // If we've dispatched "blockStart" event, we must dispatch another event
-    // "blockStop" when the window is resumed from suspend-block.
-    bool mShouldSendBlockStopEvent;
+    // If we've dispatched "activeMediaBlockStart" event, we must dispatch
+    // another event "activeMediablockStop" when the window is resumed from
+    // suspend-block.
+    bool mShouldSendActiveMediaBlockStopEvent;
   private:
     void AudioCapturedChanged(AudioChannelAgent* aAgent,
                               AudioCaptureState aCapture);
 
     void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
                                           AudibleChangedReasons aReason);
     void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
                                        AudibleChangedReasons aReason);
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1017,20 +1017,22 @@ var AudioPlaybackListener = {
         break;
     }
   },
 
   observe(subject, topic, data) {
     if (topic === "audio-playback") {
       if (subject && subject.top == global.content) {
         let name = "AudioPlayback:";
-        if (data === "blockStart") {
-          name += "BlockStart";
-        } else if (data === "blockStop") {
-          name += "BlockStop";
+        if (data === "activeMediaBlockStart") {
+          name += "ActiveMediaBlockStart";
+        } else if (data === "activeMediaBlockStop") {
+          name += "ActiveMediaBlockStop";
+        } else if (data == "mediaBlockStop") {
+          name += "MediaBlockStop";
         } else {
           name += (data === "active") ? "Start" : "Stop";
         }
         sendAsyncMessage(name);
       }
     }
   },
 
--- a/toolkit/content/tests/browser/browser_mediaPlayback_suspended.js
+++ b/toolkit/content/tests/browser/browser_mediaPlayback_suspended.js
@@ -151,17 +151,18 @@ async function suspended_block(url, brow
   info("- resume blocked audio -");
   browser.resumeMedia();
   await ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
                                    check_audio_suspended);
 }
 
 add_task(async function setup_test_preference() {
   await SpecialPowers.pushPrefEnv({"set": [
-    ["media.useAudioChannelService.testing", true]
+    ["media.useAudioChannelService.testing", true],
+    ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function test_suspended_pause() {
   await BrowserTestUtils.withNewTab({
       gBrowser,
       url: "about:blank"
     }, suspended_pause.bind(this, PAGE));
--- a/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js
+++ b/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js
@@ -271,17 +271,18 @@ async function suspended_block(url, brow
   info("- both audio should be resumed at the same time -");
   browser.resumeMedia();
   await ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
                                    check_all_audio_suspended);
 }
 
 add_task(async function setup_test_preference() {
   await SpecialPowers.pushPrefEnv({"set": [
-    ["media.useAudioChannelService.testing", true]
+    ["media.useAudioChannelService.testing", true],
+    ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function test_suspended_pause() {
   await BrowserTestUtils.withNewTab({
       gBrowser,
       url: "about:blank"
     }, suspended_pause.bind(this, PAGE));
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -715,51 +715,80 @@
           <![CDATA[
             let event = document.createEvent("Events");
             event.initEvent("DOMAudioPlaybackStopped", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
 
-      <method name="audioPlaybackBlockStarted">
+      <!--
+        When the pref "media.block-autoplay-until-in-foreground" is on, all
+        windows would be blocked by default in gecko. The "block" means the
+        autoplay media can't be started in that tab unless the tab has been
+        visited or resumed by tab's play tab icon. Since the window is blocked
+        by default, there's no method to signal entering that state.
+        (1) If the window is resumed, no matter it has autoplay media or not
+            - will call mediaBlockStopped()
+        (2) If the window has blocked any autoplay media
+            - will call activeMediaBlockStarted()
+        (3) If the window has resumed any autoplay media
+            - will call activeMediaBlockStopped()
+       -->
+      <method name="activeMediaBlockStarted">
         <body>
           <![CDATA[
-            this._audioBlocked = true;
+            this._hasAnyPlayingMediaBeenBlocked = true;
             let event = document.createEvent("Events");
             event.initEvent("DOMAudioPlaybackBlockStarted", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
 
-      <method name="audioPlaybackBlockStopped">
+      <method name="activeMediaBlockStopped">
         <body>
           <![CDATA[
-            if (!this._audioBlocked) {
+            if (!this._hasAnyPlayingMediaBeenBlocked) {
               return;
             }
-            this._audioBlocked = false;
+            this._hasAnyPlayingMediaBeenBlocked = false;
             let event = document.createEvent("Events");
             event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
 
+      <method name="mediaBlockStopped">
+        <body>
+          <![CDATA[
+            this._mediaBlocked = false;
+          ]]>
+        </body>
+      </method>
+
       <field name="_audioMuted">false</field>
       <property name="audioMuted"
                 onget="return this._audioMuted;"
                 readonly="true"/>
 
+      <field name="_mediaBlocked">true</field>
+      <property name="mediaBlocked" readonly="true">
+        <getter>
+          <![CDATA[
+            if (this.mPrefs.getBoolPref("media.block-autoplay-until-in-foreground", true)) {
+              return this._mediaBlocked;
+            }
+            return false;
+          ]]>
+        </getter>
+      </property>
 
-      <field name="_audioBlocked">false</field>
-      <property name="audioBlocked"
-                onget="return this._audioBlocked;"
-                readonly="true"/>
+      <field name="_hasAnyPlayingMediaBeenBlocked">false</field>
 
       <method name="mute">
         <parameter name="transientState"/>
         <body>
           <![CDATA[
             if (!transientState) {
               this._audioMuted = true;
             }
@@ -803,32 +832,38 @@
                                                  {type: "mediaControlStopped"});
           ]]>
         </body>
       </method>
 
       <method name="blockMedia">
         <body>
           <![CDATA[
-            this._audioBlocked = true;
+            if (!this.mPrefs.getBoolPref("media.block-autoplay-until-in-foreground", true)) {
+              return;
+            }
+            this._mediaBlocked = true;
             this.messageManager.sendAsyncMessage("AudioPlayback",
                                                  {type: "blockInactivePageMedia"});
           ]]>
         </body>
       </method>
 
       <method name="resumeMedia">
         <body>
           <![CDATA[
-            this._audioBlocked = false;
+            this._mediaBlocked = false;
             this.messageManager.sendAsyncMessage("AudioPlayback",
                                                  {type: "resumeMedia"});
-            let event = document.createEvent("Events");
-            event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
-            this.dispatchEvent(event);
+            if (this._hasAnyPlayingMediaBeenBlocked) {
+              this._hasAnyPlayingMediaBeenBlocked = false;
+              let event = document.createEvent("Events");
+              event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
+              this.dispatchEvent(event);
+            }
           ]]>
         </body>
       </method>
 
       <property name="securityUI">
         <getter>
           <![CDATA[
             if (!this.docShell.securityUI) {
@@ -951,18 +986,19 @@
           }
 
           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:BlockStart", this);
-            this.messageManager.addMessageListener("AudioPlayback:BlockStop", this);
+            this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStart", this);
+            this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStop", this);
+            this.messageManager.addMessageListener("AudioPlayback:MediaBlockStop", this);
 
             if (this.hasAttribute("selectmenulist")) {
               this.messageManager.addMessageListener("Forms:ShowDropDown", this);
               this.messageManager.addMessageListener("Forms:HideDropDown", this);
             }
 
           }
         ]]>
@@ -1050,21 +1086,24 @@
               this._autoScrollPopup.hidePopup();
               break;
             case "AudioPlayback:Start":
               this.audioPlaybackStarted();
               break;
             case "AudioPlayback:Stop":
               this.audioPlaybackStopped();
               break;
-            case "AudioPlayback:BlockStart":
-              this.audioPlaybackBlockStarted();
+            case "AudioPlayback:ActiveMediaBlockStart":
+              this.activeMediaBlockStarted();
               break;
-            case "AudioPlayback:BlockStop":
-              this.audioPlaybackBlockStopped();
+            case "AudioPlayback:ActiveMediaBlockStop":
+              this.activeMediaBlockStopped();
+              break;
+            case "AudioPlayback:MediaBlockStop":
+              this.mediaBlockStopped();
               break;
             case "Forms:ShowDropDown": {
               if (!this._selectParentHelper) {
                 this._selectParentHelper =
                   Cu.import("resource://gre/modules/SelectParentHelper.jsm", {}).SelectParentHelper;
               }
 
               let menulist = document.getElementById(this.getAttribute("selectmenulist"));