Bug 1476555 - Show notification when autoplay blocked globally. r=cpearce,johannh
authorDale Harvey <dale@arandomurl.com>
Mon, 23 Jul 2018 16:43:08 +0100
changeset 431775 1cee6e0d07e7aaf3a152d2eb15e2114937ce4151
parent 431774 41443a067773651862461210bd73a521fb396aaf
child 431776 68faa8b52f60394faf626d19dc3c439c48e23cfb
push id106548
push userdvarga@mozilla.com
push dateWed, 15 Aug 2018 22:25:34 +0000
treeherdermozilla-inbound@baba90c7c28f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, johannh
bugs1476555
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 1476555 - Show notification when autoplay blocked globally. r=cpearce,johannh MozReview-Commit-ID: EI0GiaoBNqX
browser/base/content/browser-siteIdentity.js
browser/base/content/pageinfo/permissions.js
browser/base/content/test/permissions/browser.ini
browser/base/content/test/permissions/browser_autoplay_blocked.html
browser/base/content/test/permissions/browser_autoplay_blocked.js
browser/modules/PermissionUI.jsm
browser/modules/SitePermissions.jsm
dom/html/HTMLMediaElement.cpp
dom/media/AutoplayPolicy.cpp
dom/media/AutoplayPolicy.h
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -1010,17 +1010,19 @@ var gIdentityHandler = {
 
     let nameLabel = document.createXULElement("label");
     nameLabel.setAttribute("flex", "1");
     nameLabel.setAttribute("class", "identity-popup-permission-label");
     nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
     let nameLabelId = "identity-popup-permission-label-" + aPermission.id;
     nameLabel.setAttribute("id", nameLabelId);
 
-    let isPolicyPermission = aPermission.scope == SitePermissions.SCOPE_POLICY;
+    let isPolicyPermission = [
+      SitePermissions.SCOPE_POLICY, SitePermissions.SCOPE_GLOBAL
+    ].includes(aPermission.scope);
 
     if (aPermission.id == "popup" && !isPolicyPermission) {
       let menulist = document.createXULElement("menulist");
       let menupopup = document.createXULElement("menupopup");
       let block = document.createXULElement("vbox");
       block.setAttribute("id", "identity-popup-popup-container");
       menulist.setAttribute("sizetopopup", "none");
       menulist.setAttribute("class", "identity-popup-popup-menulist");
@@ -1078,18 +1080,18 @@ var gIdentityHandler = {
     stateLabel.textContent = SitePermissions.getCurrentStateLabel(state, aPermission.id, scope);
 
     container.appendChild(img);
     container.appendChild(nameLabel);
     container.appendChild(stateLabel);
     container.setAttribute("aria-labelledby", nameLabelId + " " + stateLabelId);
 
     /* We return the permission item here without a remove button if the permission is a
-       SCOPE_POLICY permission. Policy permissions cannot be removed/changed for the duration
-       of the browser session. */
+       SCOPE_POLICY or SCOPE_GLOBAL permission. Policy permissions cannot be
+       removed/changed for the duration of the browser session. */
     if (isPolicyPermission) {
       return container;
     }
 
     let button = document.createXULElement("button");
     button.setAttribute("class", "identity-popup-permission-remove-button");
     let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
     button.setAttribute("tooltiptext", tooltiptext);
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -92,17 +92,17 @@ function initRow(aPartId) {
   if (state != defaultState) {
     checkbox.checked = false;
     command.removeAttribute("disabled");
   } else {
     checkbox.checked = true;
     command.setAttribute("disabled", "true");
   }
 
-  if (scope == SitePermissions.SCOPE_POLICY) {
+  if ([SitePermissions.SCOPE_POLICY, SitePermissions.SCOPE_GLOBAL].includes(scope)) {
     checkbox.setAttribute("disabled", "true");
     command.setAttribute("disabled", "true");
   }
 
   setRadioState(aPartId, state);
 }
 
 function createRow(aPartId) {
--- a/browser/base/content/test/permissions/browser.ini
+++ b/browser/base/content/test/permissions/browser.ini
@@ -5,11 +5,15 @@ support-files=
 
 [browser_canvas_fingerprinting_resistance.js]
 [browser_permissions.js]
 [browser_reservedkey.js]
 [browser_temporary_permissions.js]
 support-files =
   temporary_permissions_subframe.html
   ../webrtc/get_user_media.html
+[browser_autoplay_blocked.js]
+support-files =
+  browser_autoplay_blocked.html
+  ../general/audio.ogg
 [browser_temporary_permissions_expiry.js]
 [browser_temporary_permissions_navigation.js]
 [browser_temporary_permissions_tabs.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/permissions/browser_autoplay_blocked.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<!-- 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/. -->
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <meta charset="utf8">
+  </head>
+  <body>
+    <audio autoplay="autoplay" >
+      <source src="audio.ogg" />
+    </audio>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/permissions/browser_autoplay_blocked.js
@@ -0,0 +1,57 @@
+/*
+ * Test that a blocked request to autoplay media is shown to the user
+ */
+
+const AUTOPLAY_PAGE  = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "browser_autoplay_blocked.html";
+
+function openIdentityPopup() {
+  let promise = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
+  gIdentityHandler._identityBox.click();
+  return promise;
+}
+
+function closeIdentityPopup() {
+  let promise = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
+  gIdentityHandler._identityPopup.hidePopup();
+  return promise;
+}
+
+function autoplayBlockedIcon() {
+  return document.querySelector("#blocked-permissions-container " +
+                                ".blocked-permission-icon.autoplay-media-icon");
+}
+
+add_task(async function testMainViewVisible() {
+
+  Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.ALLOWED);
+
+  await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() {
+    let permissionsList = document.getElementById("identity-popup-permission-list");
+    let emptyLabel = permissionsList.nextSibling.nextSibling;
+
+    ok(BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon not shown");
+
+    await openIdentityPopup();
+    ok(!BrowserTestUtils.is_hidden(emptyLabel), "List of permissions is empty");
+    await closeIdentityPopup();
+  });
+
+  Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.BLOCKED);
+
+  await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() {
+    let permissionsList = document.getElementById("identity-popup-permission-list");
+    let emptyLabel = permissionsList.nextSibling.nextSibling;
+
+    ok(!BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon is shown");
+
+    await openIdentityPopup();
+    ok(BrowserTestUtils.is_hidden(emptyLabel), "List of permissions is not empty");
+    let labelText = SitePermissions.getPermissionLabel("autoplay-media");
+    let labels = permissionsList.querySelectorAll(".identity-popup-permission-label");
+    is(labels.length, 1, "One permission visible in main view");
+    is(labels[0].textContent, labelText, "Correct value");
+    await closeIdentityPopup();
+  });
+
+  Services.prefs.clearUserPref("media.autoplay.default");
+});
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -274,16 +274,29 @@ var PermissionPromptPrototype = {
       // If we're reading and setting permissions, then we need
       // to check to see if we already have a permission setting
       // for this particular principal.
       let {state} = SitePermissions.get(requestingURI,
                                         this.permissionKey,
                                         this.browser);
 
       if (state == SitePermissions.BLOCK) {
+        // If the request is blocked by a global setting then we record
+        // a flag that lasts for the duration of the current page load
+        // to notify the user that the permission has been blocked.
+        // Currently only applies to autoplay-media
+        if (state == SitePermissions.getDefault(this.permissionKey) &&
+            SitePermissions.showGloballyBlocked(this.permissionKey)) {
+          SitePermissions.set(this.principal.URI,
+                              this.permissionKey,
+                              state,
+                              SitePermissions.SCOPE_GLOBAL,
+                              this.browser);
+        }
+
         this.cancel();
         return;
       }
 
       if (state == SitePermissions.ALLOW) {
         this.allow();
         return;
       }
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -124,16 +124,81 @@ const TemporaryBlockedPermissions = {
   copy(browser, newBrowser) {
     let entry = this._stateByBrowser.get(browser);
     if (entry) {
       this._stateByBrowser.set(newBrowser, entry);
     }
   },
 };
 
+// This hold a flag per browser to indicate whether we should show the
+// user a notification as a permission has been requested that has been
+// blocked globally. We only want to notify the user in the case that
+// they actually requested the permission within the current page load
+// so will clear the flag on navigation.
+const GloballyBlockedPermissions = {
+
+  _stateByBrowser: new WeakMap(),
+
+  set(browser, id) {
+    if (!this._stateByBrowser.has(browser)) {
+      this._stateByBrowser.set(browser, {});
+    }
+    let entry = this._stateByBrowser.get(browser);
+    let prePath = browser.currentURI.prePath;
+    if (!entry[prePath]) {
+      entry[prePath] = {};
+    }
+
+    entry[prePath][id] = true;
+
+    // Listen to any top level navigations, once we see one clear the flag
+    // and remove the listener.
+    browser.addProgressListener({
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                              Ci.nsISupportsWeakReference]),
+      onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+        if (aWebProgress.isTopLevel) {
+          GloballyBlockedPermissions.remove(browser, id);
+          browser.removeProgressListener(this);
+        }
+      },
+    });
+  },
+
+  // Removes a permission with the specified id for the specified browser.
+  remove(browser, id) {
+    let entry = this._stateByBrowser.get(browser);
+    let prePath = browser.currentURI.prePath;
+    if (entry && entry[prePath]) {
+      delete entry[prePath][id];
+    }
+  },
+
+  // Gets all permissions for the specified browser.
+  // Note that only permissions that apply to the current URI
+  // of the passed browser element will be returned.
+  getAll(browser) {
+    let permissions = [];
+    let entry = this._stateByBrowser.get(browser);
+    let prePath = browser.currentURI.prePath;
+    if (entry && entry[prePath]) {
+      let timeStamps = entry[prePath];
+      for (let id of Object.keys(timeStamps)) {
+        permissions.push({
+          id,
+          state: SitePermissions.BLOCK,
+          scope: SitePermissions.SCOPE_GLOBAL
+        });
+      }
+    }
+    return permissions;
+  },
+};
+
 /**
  * A module to manage permanent and temporary permissions
  * by URI and browser.
  *
  * Some methods have the side effect of dispatching a "PermissionStateChange"
  * event on changes to temporary permissions, as mentioned in the respective docs.
  */
 var SitePermissions = {
@@ -148,16 +213,17 @@ var SitePermissions = {
   PROMPT_HIDE: Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET,
 
   // Permission scopes.
   SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
   SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}",
   SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}",
   SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}",
   SCOPE_POLICY: "{SitePermissions.SCOPE_POLICY}",
+  SCOPE_GLOBAL: "{SitePermissions.SCOPE_GLOBAL}",
 
   _defaultPrefBranch: Services.prefs.getBranch("permissions.default."),
 
   /**
    * Gets all custom permissions for a given URI.
    * Install addon permission is excluded, check bug 1303108.
    *
    * @return {Array} a list of objects with the keys:
@@ -226,16 +292,20 @@ var SitePermissions = {
   getAllForBrowser(browser) {
     let permissions = {};
 
     for (let permission of TemporaryBlockedPermissions.getAll(browser)) {
       permission.scope = this.SCOPE_TEMPORARY;
       permissions[permission.id] = permission;
     }
 
+    for (let permission of GloballyBlockedPermissions.getAll(browser)) {
+      permissions[permission.id] = permission;
+    }
+
     for (let permission of this.getAllByURI(browser.currentURI)) {
       permissions[permission.id] = permission;
     }
 
     return Object.values(permissions);
   },
 
   /**
@@ -326,16 +396,33 @@ var SitePermissions = {
         gPermissionObject[permissionID].getDefault)
       return gPermissionObject[permissionID].getDefault();
 
     // Otherwise try to get the default preference for that permission.
     return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN);
   },
 
   /**
+   * Return whether the browser should notify the user if a permission was
+   * globally blocked due to a preference.
+   *
+   * @param {string} permissionID
+   *        The ID to get the state for.
+   *
+   * @return boolean Whether to show notification for globally blocked permissions.
+   */
+  showGloballyBlocked(permissionID) {
+    if (permissionID in gPermissionObject &&
+        gPermissionObject[permissionID].showGloballyBlocked)
+      return gPermissionObject[permissionID].showGloballyBlocked;
+
+    return false;
+  },
+
+  /**
    * Returns the state and scope of a particular permission for a given URI.
    *
    * This method will NOT dispatch a "PermissionStateChange" event on the specified
    * browser if a temporary permission was removed because it has expired.
    *
    * @param {nsIURI} uri
    *        The URI to check.
    * @param {String} permissionID
@@ -399,16 +486,23 @@ var SitePermissions = {
    *        The state of the permission.
    * @param {SitePermissions scope} scope (optional)
    *        The scope of the permission. Defaults to SCOPE_PERSISTENT.
    * @param {Browser} browser (optional)
    *        The browser object to set temporary permissions on.
    *        This needs to be provided if the scope is SCOPE_TEMPORARY!
    */
   set(uri, permissionID, state, scope = this.SCOPE_PERSISTENT, browser = null) {
+
+    if (scope == this.SCOPE_GLOBAL && state == this.BLOCK) {
+      GloballyBlockedPermissions.set(browser, permissionID);
+      browser.dispatchEvent(new browser.ownerGlobal.CustomEvent("PermissionStateChange"));
+      return;
+    }
+
     if (state == this.UNKNOWN || state == this.getDefault(permissionID)) {
       // Because they are controlled by two prefs with many states that do not
       // correspond to the classical ALLOW/DENY/PROMPT model, we want to always
       // allow the user to add exceptions to their cookie rules without removing them.
       if (permissionID != "cookie") {
         this.remove(uri, permissionID, browser);
         return;
       }
@@ -602,23 +696,24 @@ var gPermissionObject = {
    *    Array of permission states to be exposed to the user.
    *    Defaults to ALLOW, BLOCK and the default state (see getDefault).
    *    The PROMPT_HIDE state is deliberately excluded from "plugin:flash" since we
    *    don't want to expose a "Hide Prompt" button to the user through pageinfo.
    */
 
   "autoplay-media": {
     exactHostMatch: true,
+    showGloballyBlocked: true,
     getDefault() {
       let state = Services.prefs.getIntPref("media.autoplay.default",
                                             Ci.nsIAutoplay.PROMPT);
-      if (state == Ci.nsIAutoplay.ALLOW) {
+      if (state == Ci.nsIAutoplay.ALLOWED) {
         return SitePermissions.ALLOW;
-      } if (state == Ci.nsIAutoplay.BLOCK) {
-        return SitePermissions.DENY;
+      } if (state == Ci.nsIAutoplay.BLOCKED) {
+        return SitePermissions.BLOCK;
       }
       return SitePermissions.UNKNOWN;
     },
     labelID: "autoplay-media"
   },
 
   "image": {
     states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2005,17 +2005,17 @@ HTMLMediaElement::Load()
        "ownerDoc=%p (%s) ownerDocUserActivated=%d "
        "muted=%d volume=%f",
        this,
        !!mSrcAttrStream,
        HasAttr(kNameSpaceID_None, nsGkAtoms::src),
        HasSourceChildren(this),
        EventStateManager::IsHandlingUserInput(),
        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay),
-       AutoplayPolicy::IsAllowedToPlay(*this) == nsIAutoplay::ALLOWED,
+       AutoplayPolicy::IsAllowedToPlay(*this),
        OwnerDoc(),
        DocumentOrigin(OwnerDoc()).get(),
        OwnerDoc() ? OwnerDoc()->HasBeenUserGestureActivated() : 0,
        mMuted,
        mVolume));
 
   if (mIsRunningLoadMethod) {
     return;
@@ -2524,26 +2524,26 @@ HTMLMediaElement::ResumeLoad(PreloadActi
       LoadFromSourceChildren();
     }
   }
 }
 
 bool
 HTMLMediaElement::AllowedToPlay() const
 {
-  return AutoplayPolicy::IsAllowedToPlay(*this) == nsIAutoplay::ALLOWED;
+  return AutoplayPolicy::IsAllowedToPlay(*this);
 }
 
 void
 HTMLMediaElement::UpdatePreloadAction()
 {
   PreloadAction nextAction = PRELOAD_UNDEFINED;
   // If autoplay is set, or we're playing, we should always preload data,
   // as we'll need it to play.
-  if ((AutoplayPolicy::IsAllowedToPlay(*this) == nsIAutoplay::ALLOWED &&
+  if ((AutoplayPolicy::IsAllowedToPlay(*this) &&
        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
       !mPaused) {
     nextAction = HTMLMediaElement::PRELOAD_ENOUGH;
   } else {
     // Find the appropriate preload action by looking at the attribute.
     const nsAttrValue* val =
       mAttrs.GetAttr(nsGkAtoms::preload, kNameSpaceID_None);
     // MSE doesn't work if preload is none, so it ignores the pref when src is
@@ -3066,17 +3066,17 @@ HTMLMediaElement::SetMutedInternal(uint3
 }
 
 void
 HTMLMediaElement::PauseIfShouldNotBePlaying()
 {
   if (GetPaused()) {
     return;
   }
-  if (AutoplayPolicy::IsAllowedToPlay(*this) != nsIAutoplay::ALLOWED) {
+  if (!AutoplayPolicy::IsAllowedToPlay(*this)) {
     AUTOPLAY_LOG("pause because not allowed to play, element=%p", this);
     ErrorResult rv;
     Pause(rv);
     OwnerDoc()->SetDocTreeHadPlayRevoked();
   }
 }
 
 void
@@ -4098,37 +4098,24 @@ HTMLMediaElement::Play(ErrorResult& aRv)
       DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
     }
     return promise.forget();
   }
 
   UpdateHadAudibleAutoplayState();
 
   const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
-  switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
-    case nsIAutoplay::ALLOWED: {
-      mPendingPlayPromises.AppendElement(promise);
-      PlayInternal(handlingUserInput);
-      UpdateCustomPolicyAfterPlayed();
-      break;
-    }
-    case nsIAutoplay::BLOCKED: {
-      AUTOPLAY_LOG("%p play blocked.", this);
-      promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
-      if (StaticPrefs::MediaBlockEventEnabled()) {
-        DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
-      }
-      break;
-    }
-    case nsIAutoplay::PROMPT: {
-      // Prompt the user for permission to play.
-      mPendingPlayPromises.AppendElement(promise);
-      EnsureAutoplayRequested(handlingUserInput);
-      break;
-    }
+  if (AutoplayPolicy::IsAllowedToPlay(*this)) {
+    mPendingPlayPromises.AppendElement(promise);
+    PlayInternal(handlingUserInput);
+    UpdateCustomPolicyAfterPlayed();
+  } else {
+    // Prompt the user for permission to play.
+    mPendingPlayPromises.AppendElement(promise);
+    EnsureAutoplayRequested(handlingUserInput);
   }
   return promise.forget();
 }
 
 void
 HTMLMediaElement::EnsureAutoplayRequested(bool aHandlingUserInput)
 {
   if (mAutoplayPermissionRequest.Exists()) {
@@ -6146,18 +6133,17 @@ HTMLMediaElement::ChangeReadyState(nsMed
     DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
     mLoadedDataFired = true;
   }
 
   if (oldState < HAVE_FUTURE_DATA && mReadyState >= HAVE_FUTURE_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
     if (!mPaused) {
       if (mDecoder && !mPausedForInactiveDocumentOrChannel) {
-        MOZ_ASSERT(AutoplayPolicy::IsAllowedToPlay(*this) ==
-                   nsIAutoplay::ALLOWED);
+        MOZ_ASSERT(AutoplayPolicy::IsAllowedToPlay(*this));
         mDecoder->Play();
       }
       NotifyAboutPlaying();
     }
   }
 
   CheckAutoplayDataReady();
 
@@ -6259,24 +6245,19 @@ HTMLMediaElement::CanActivateAutoplay()
 void
 HTMLMediaElement::CheckAutoplayDataReady()
 {
   if (!CanActivateAutoplay()) {
     return;
   }
 
   UpdateHadAudibleAutoplayState();
-  switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
-    case nsIAutoplay::BLOCKED:
-      return;
-    case nsIAutoplay::PROMPT:
-      EnsureAutoplayRequested(false);
-      return;
-    case nsIAutoplay::ALLOWED:
-      break;
+  if (!AutoplayPolicy::IsAllowedToPlay(*this)) {
+    EnsureAutoplayRequested(false);
+    return;
   }
 
   mPaused = false;
   // We changed mPaused which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -151,35 +151,39 @@ IsMediaElementAllowedToPlay(const HTMLMe
 }
 
 /* static */ bool
 AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(const HTMLMediaElement& aElement)
 {
   return IsMediaElementAllowedToPlay(aElement);
 }
 
-/* static */ uint32_t
+/* static */ bool
 AutoplayPolicy::IsAllowedToPlay(const HTMLMediaElement& aElement)
 {
   const uint32_t autoplayDefault = DefaultAutoplayBehaviour();
   // TODO : this old way would be removed when user-gestures-needed becomes
   // as a default option to block autoplay.
   if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
     // If element is blessed, it would always be allowed to play().
     return (autoplayDefault == nsIAutoplay::ALLOWED ||
             aElement.IsBlessed() ||
-            EventStateManager::IsHandlingUserInput())
-              ? nsIAutoplay::ALLOWED : nsIAutoplay::BLOCKED;
+            EventStateManager::IsHandlingUserInput());
   }
 
-  const uint32_t result = IsMediaElementAllowedToPlay(aElement) ?
-    nsIAutoplay::ALLOWED : autoplayDefault;
+  if (IsMediaElementAllowedToPlay(aElement)) {
+    return true;
+  }
+
+  const bool result = IsMediaElementAllowedToPlay(aElement) ||
+    autoplayDefault == nsIAutoplay::ALLOWED;
 
   AUTOPLAY_LOG("IsAllowedToPlay, mediaElement=%p, isAllowToPlay=%s",
                 &aElement, AllowAutoplayToStr(result));
+
   return result;
 }
 
 /* static */ bool
 AutoplayPolicy::IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext)
 {
   if (!Preferences::GetBool("media.autoplay.block-webaudio", false)) {
     return true;
--- a/dom/media/AutoplayPolicy.h
+++ b/dom/media/AutoplayPolicy.h
@@ -31,17 +31,17 @@ class AudioContext;
  *    We restrict user gestures to "mouse click", "keyboard press" and "touch".
  * 2) Muted media content or video without audio content.
  * 3) Document's origin has the "autoplay-media" permission.
  */
 class AutoplayPolicy
 {
 public:
   // Returns whether a given media element is allowed to play.
-  static uint32_t IsAllowedToPlay(const HTMLMediaElement& aElement);
+  static bool IsAllowedToPlay(const HTMLMediaElement& aElement);
 
   // Returns true if a given media element would be allowed to play
   // if block autoplay was enabled. If this returns false, it means we would
   // either block or ask for permission.
   // Note: this is for telemetry purposes, and doesn't check the prefs
   // which enable/disable block autoplay. Do not use for blocking logic!
   static bool WouldBeAllowedToPlayIfAutoplayDisabled(const HTMLMediaElement& aElement);