Bug 1476701 - notify observer when audible autoplay occurred. r=cpearce,jaws
authoralwu <alwu@mozilla.com>
Wed, 25 Jul 2018 09:08:44 -0700
changeset 487218 92cbea18e43015ea11f09c5113d4fda04b0923c3
parent 487217 e1e6b6c80b115823b63eeb745f9067c1232bf284
child 487219 675e65d26f4142686cb230452c7e7d97aa55c860
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, jaws
bugs1476701
milestone63.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 1476701 - notify observer when audible autoplay occurred. r=cpearce,jaws In our autoplay shield-study, we want to collect the information which could tell us how many website contains audible autoplay media, but there is no way to get this information on current API desigin. Therefore, I would like to send a new notification when autoplay occurred. The extension code could get the information by following way, ``` Services.obs.addObserver((subject, topic, data) => { // DO SOMETHING }, "AudibleAutoplayMediaOccurred"); ``` MozReview-Commit-ID: 4bSYcxDZOGK
browser/base/content/tabbrowser.js
dom/base/nsDocument.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
toolkit/content/browser-content.js
toolkit/content/widgets/browser.xml
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -4426,16 +4426,26 @@ window._gBrowser = {
       if (tab.hasAttribute("activemedia-blocked")) {
         tab.removeAttribute("activemedia-blocked");
         this._tabAttrModified(tab, ["activemedia-blocked"]);
         let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
         hist.add(2 /* unblockByVisitingTab */ );
         tab.finishMediaBlockTimer();
       }
     });
+
+    this.addEventListener("AudibleAutoplayMediaOccurred", (event) => {
+      let browser = event.originalTarget;
+      let tab = this.getTabForBrowser(browser);
+      if (!tab) {
+        return;
+      }
+
+      Services.obs.notifyObservers(tab, "AudibleAutoplayMediaOccurred");
+    });
   },
 };
 
 /**
  * A web progress listener object definition for a given tab.
  */
 class TabProgressListener {
   constructor(aTab, aBrowser, aStartsBlank, aWasPreloadedBrowser, aOrigStateFlags) {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12505,19 +12505,29 @@ nsIDocument::NotifyUserGestureActivation
     doc = doc->GetSameTypeParentDocument();
   }
 }
 
 void
 nsIDocument::SetDocTreeHadAudibleMedia()
 {
   nsIDocument* topLevelDoc = GetTopLevelContentDocument();
-  if (topLevelDoc) {
-    topLevelDoc->mDocTreeHadAudibleMedia = true;
-  }
+  if (!topLevelDoc) {
+    return;
+  }
+
+  if (!topLevelDoc->mDocTreeHadAudibleMedia) {
+    RefPtr<AsyncEventDispatcher> asyncDispatcher =
+      new AsyncEventDispatcher(topLevelDoc,
+                               NS_LITERAL_STRING("AudibleAutoplayMediaOccurred"),
+                               CanBubble::eYes,
+                               ChromeOnlyDispatch::eYes);
+    asyncDispatcher->PostDOMEvent();
+  }
+  topLevelDoc->mDocTreeHadAudibleMedia = true;
 }
 
 void
 nsIDocument::SetDocTreeHadPlayRevoked()
 {
   nsIDocument* topLevelDoc = GetTopLevelContentDocument();
   if (topLevelDoc) {
     topLevelDoc->mDocTreeHadPlayRevoked = true;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4008,17 +4008,17 @@ HTMLMediaElement::NotifyXPCOMShutdown()
 
 bool
 HTMLMediaElement::AudioChannelAgentDelayingPlayback()
 {
   return mAudioChannelWrapper && mAudioChannelWrapper->IsPlaybackBlocked();
 }
 
 void
-HTMLMediaElement::ReportAutoplayTelemetry() const
+HTMLMediaElement::UpdateHadAudibleAutoplayState() const
 {
   // If we're audible, and autoplaying...
   if ((Volume() > 0.0 && !Muted()) &&
       (!OwnerDoc()->HasBeenUserGestureActivated() || Autoplay())) {
     OwnerDoc()->SetDocTreeHadAudibleMedia();
     if (AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(*this)) {
       ScalarAdd(Telemetry::ScalarID::MEDIA_AUTOPLAY_WOULD_BE_ALLOWED_COUNT, 1);
     } else {
@@ -4081,17 +4081,17 @@ HTMLMediaElement::Play(ErrorResult& aRv)
     LOG(LogLevel::Debug, ("%p play blocked by AudioChannelAgent.", this));
     promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
     if (StaticPrefs::MediaBlockEventEnabled()) {
       DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
     }
     return promise.forget();
   }
 
-  ReportAutoplayTelemetry();
+  UpdateHadAudibleAutoplayState();
 
   const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
   switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
     case nsIAutoplay::ALLOWED: {
       mPendingPlayPromises.AppendElement(promise);
       PlayInternal(handlingUserInput);
       UpdateCustomPolicyAfterPlayed();
       break;
@@ -6241,17 +6241,17 @@ HTMLMediaElement::CanActivateAutoplay()
 
 void
 HTMLMediaElement::CheckAutoplayDataReady()
 {
   if (!CanActivateAutoplay()) {
     return;
   }
 
-  ReportAutoplayTelemetry();
+  UpdateHadAudibleAutoplayState();
   switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
     case nsIAutoplay::BLOCKED:
       return;
     case nsIAutoplay::PROMPT:
       EnsureAutoplayRequested(false);
       return;
     case nsIAutoplay::ALLOWED:
       break;
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1776,17 +1776,17 @@ public:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 private:
 
   already_AddRefed<PlayPromise> CreatePlayPromise(ErrorResult& aRv) const;
 
-  void ReportAutoplayTelemetry() const;
+  void UpdateHadAudibleAutoplayState() const;
 
   /**
    * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
    * It will not be called if the value is being unset.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aNotify Whether we plan to notify document observers.
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -387,16 +387,27 @@ var UnselectedTabHoverObserver = {
   },
   handleEvent(event) {
     sendAsyncMessage("UnselectedTabHover:Toggle",
                      { enable: event.type == "UnselectedTabHover:Enable" });
   }
 };
 UnselectedTabHoverObserver.init();
 
+
+var AudibleAutoplayObserver = {
+  init() {
+    addEventListener("AudibleAutoplayMediaOccurred", this);
+  },
+  handleEvent(event) {
+    sendAsyncMessage("AudibleAutoplayMediaOccurred");
+  }
+};
+AudibleAutoplayObserver.init();
+
 addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() {
   let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
   if (!sessionHistory) {
     return;
   }
 
   // place the entry at current index at the end of the history list, so it won't get removed
   if (sessionHistory.index < sessionHistory.count - 1) {
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -586,16 +586,26 @@
           <![CDATA[
             let event = document.createEvent("Events");
             event.initEvent("DOMAudioPlaybackStopped", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
 
+      <method name="notifyAudibleAutoplayMediaOccurred">
+        <body>
+          <![CDATA[
+            let event = document.createEvent("Events");
+            event.initEvent("AudibleAutoplayMediaOccurred", true, false);
+            this.dispatchEvent(event);
+          ]]>
+        </body>
+      </method>
+
       <!--
         When the pref "media.block-autoplay-until-in-foreground" is on,
         Gecko delays starting playback of media resources in tabs until the
         tab has been in the foreground or resumed by tab's play tab icon.
         - When Gecko delays starting playback of a media resource in a window,
         it sends a message to call activeMediaBlockStarted(). This causes the
         tab audio indicator to show.
         - When a tab is foregrounded, Gecko starts playing all delayed media
@@ -839,16 +849,17 @@
             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:ActiveMediaBlockStart", this);
             this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStop", this);
             this.messageManager.addMessageListener("UnselectedTabHover:Toggle", this);
+            this.messageManager.addMessageListener("AudibleAutoplayMediaOccurred", this);
 
             if (this.hasAttribute("selectmenulist")) {
               this.messageManager.addMessageListener("Forms:ShowDropDown", this);
               this.messageManager.addMessageListener("Forms:HideDropDown", this);
             }
 
           }
         ]]>
@@ -967,16 +978,19 @@
             case "AudioPlayback:ActiveMediaBlockStop":
               this.activeMediaBlockStopped();
               break;
             case "UnselectedTabHover:Toggle":
               this._shouldSendUnselectedTabHover = data.enable ?
                 ++this._unselectedTabHoverMessageListenerCount > 0 :
                 --this._unselectedTabHoverMessageListenerCount == 0;
               break;
+            case "AudibleAutoplayMediaOccurred":
+              this.notifyAudibleAutoplayMediaOccurred();
+              break;
             case "Forms:ShowDropDown": {
               if (!this._selectParentHelper) {
                 this._selectParentHelper =
                   ChromeUtils.import("resource://gre/modules/SelectParentHelper.jsm", {}).SelectParentHelper;
               }
 
               let menulist = document.getElementById(this.getAttribute("selectmenulist"));
               menulist.menupopup.style.direction = data.direction;