Bug 1470082 - Change autoplay checkbox to combobox. r=cpearce, r=johannh draft
authorDale Harvey <dale@arandomurl.com>
Fri, 29 Jun 2018 14:14:33 +0100
changeset 819301 dfd55057fb2e96264e4e9b3710fb0ca465f71995
parent 819234 e0d9ee9dc6c0737a5687dcfc98f10414ffccd23a
push id116498
push userbmo:dharvey@mozilla.com
push dateTue, 17 Jul 2018 15:16:47 +0000
reviewerscpearce, johannh
bugs1470082
milestone63.0a1
Bug 1470082 - Change autoplay checkbox to combobox. r=cpearce, r=johannh MozReview-Commit-ID: E71TxvgfJlJ
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_site_autoplay_media_exceptions.js
browser/components/preferences/in-content/tests/browser_site_autoplay_media_prompt.js
browser/locales/en-US/browser/preferences/preferences.ftl
browser/modules/SitePermissions.jsm
browser/modules/test/browser/browser_PermissionUI_prompts.js
dom/html/HTMLMediaElement.cpp
dom/media/AutoplayPolicy.cpp
dom/media/AutoplayPolicy.h
dom/media/moz.build
dom/media/nsIAutoplay.idl
dom/media/test/test_autoplay_policy.html
dom/media/test/test_autoplay_policy_activation.html
dom/media/test/test_autoplay_policy_eventdown_activation.html
dom/media/test/test_autoplay_policy_key_blacklist.html
dom/media/test/test_autoplay_policy_permission.html
dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
dom/media/test/test_autoplay_policy_unmute_pauses.html
dom/media/tests/mochitest/head.js
dom/media/webaudio/test/test_notAllowedToStartAudioContextGC.html
mobile/android/app/src/main/res/xml/preferences_advanced.xml
modules/libpref/init/all.js
toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
toolkit/content/tests/browser/browser_autoplay_policy_request_permission.js
toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js
toolkit/content/tests/browser/browser_block_autoplay_media.js
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -72,17 +72,18 @@ Preferences.addAll([
   { id: "network.cookie.blockFutureCookies", type: "bool" },
   // Clear Private Data
   { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
   { id: "privacy.sanitize.timeSpan", type: "int" },
   // Do not track
   { id: "privacy.donottrackheader.enabled", type: "bool" },
 
   // Media
-  { id: "media.autoplay.enabled", type: "bool" },
+  { id: "media.autoplay.default", type: "int" },
+  { id: "media.autoplay.enabled.ask-permission", type: "bool" },
   { id: "media.autoplay.enabled.user-gestures-needed", type: "bool" },
 
   // Popups
   { id: "dom.disable_open_during_load", type: "bool" },
   // Passwords
   { id: "signon.rememberSignons", type: "bool" },
 
   // Buttons
@@ -253,17 +254,16 @@ var gPrivacyPane = {
   init() {
     function setEventListener(aId, aEventType, aCallback) {
       document.getElementById(aId)
         .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
     }
 
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
-    this.updateAutoplayMediaControls();
     this.updateAutoplayMediaControlsVisibility();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
     this._initTrackingProtection();
     this._initTrackingProtectionPBM();
     this._initTrackingProtectionExtensionControl();
     this._initAutocomplete();
@@ -271,18 +271,18 @@ var gPrivacyPane = {
     Preferences.get("privacy.sanitize.sanitizeOnShutdown").on("change",
       gPrivacyPane._updateSanitizeSettingsButton.bind(gPrivacyPane));
     Preferences.get("browser.privatebrowsing.autostart").on("change",
       gPrivacyPane.updatePrivacyMicroControls.bind(gPrivacyPane));
     Preferences.get("privacy.trackingprotection.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
     Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
-    Preferences.get("media.autoplay.enabled").on("change",
-     gPrivacyPane.updateAutoplayMediaControls.bind(gPrivacyPane));
+    Preferences.get("media.autoplay.enabled.ask-permission").on("change",
+     gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
     Preferences.get("media.autoplay.enabled.user-gestures-needed").on("change",
      gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
     setEventListener("historyMode", "command", function() {
       gPrivacyPane.updateHistoryModePane();
       gPrivacyPane.updateHistoryModePrefs();
       gPrivacyPane.updatePrivacyMicroControls();
       gPrivacyPane.updateAutostart();
     });
@@ -337,20 +337,22 @@ var gPrivacyPane = {
     setEventListener("locationSettingsButton", "command",
       gPrivacyPane.showLocationExceptions);
     setEventListener("cameraSettingsButton", "command",
       gPrivacyPane.showCameraExceptions);
     setEventListener("microphoneSettingsButton", "command",
       gPrivacyPane.showMicrophoneExceptions);
     setEventListener("popupPolicyButton", "command",
       gPrivacyPane.showPopupExceptions);
-    setEventListener("autoplayMediaPolicy", "command",
+    setEventListener("autoplayMediaCheckbox", "command",
       gPrivacyPane.toggleAutoplayMedia);
     setEventListener("autoplayMediaPolicyButton", "command",
       gPrivacyPane.showAutoplayMediaExceptions);
+    setEventListener("autoplayMediaPolicyComboboxButton", "command",
+      gPrivacyPane.showAutoplayMediaExceptions);
     setEventListener("notificationsDoNotDisturb", "command",
       gPrivacyPane.toggleDoNotDisturbNotifications);
 
     if (AlertsServiceDND) {
       let notificationsDoNotDisturbBox =
         document.getElementById("notificationsDoNotDisturbBox");
       notificationsDoNotDisturbBox.removeAttribute("hidden");
       let checkbox = document.getElementById("notificationsDoNotDisturb");
@@ -983,44 +985,53 @@ var gPrivacyPane = {
         .getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
     } catch (e) { }
   },
 
 
   // MEDIA
 
   /**
-   * media.autoplay.enabled works the opposite to most of the other preferences.
-   * The checkbox enabled sets the pref to false
+   * The checkbox enabled sets the pref to BLOCKED
    */
   toggleAutoplayMedia(event) {
-    Services.prefs.setBoolPref("media.autoplay.enabled", !event.target.checked);
-  },
-
-  updateAutoplayMediaControls() {
-    let autoPlayEnabled = Preferences.get("media.autoplay.enabled").value;
-    document.getElementById("autoplayMediaPolicy").checked = !autoPlayEnabled;
-    document.getElementById("autoplayMediaPolicyButton").disabled = autoPlayEnabled;
+    let blocked = event.target.checked ? Ci.nsIAutoplay.BLOCKED : Ci.nsIAutoplay.ALLOWED;
+    Services.prefs.setIntPref("media.autoplay.default", blocked);
   },
 
   /**
-   * Show the controls for the new media autoplay behaviour behind a pref for now
+   * If user-gestures-needed is false we do not show any UI for configuring autoplay,
+   * if user-gestures-needed is false and ask-permission is false we show a checkbox
+   * which only allows the user to block autoplay
+   * if user-gestures-needed and ask-permission are true we show a combobox that
+   * allows the user to block / allow or prompt for autoplay
+   * We will be performing a shield study to determine the behaviour to be
+   * shipped, at which point we can remove these pref switches.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1475099
    */
   updateAutoplayMediaControlsVisibility() {
-    document.getElementById("autoplayMediaBox").hidden =
-      !Services.prefs.getBoolPref("media.autoplay.enabled.user-gestures-needed", false);
+    let askPermission =
+      Services.prefs.getBoolPref("media.autoplay.ask-permission", false);
+    let userGestures =
+        Services.prefs.getBoolPref("media.autoplay.enabled.user-gestures-needed", false);
+    // Hide the combobox if we don't let the user ask for permission.
+    document.getElementById("autoplayMediaComboboxWrapper").hidden =
+      !userGestures || !askPermission;
+    // If the user may ask for permission, hide the checkbox instead.
+    document.getElementById("autoplayMediaCheckboxWrapper").hidden =
+      !userGestures || askPermission;
   },
 
   /**
    * Displays the autoplay exceptions dialog where specific site autoplay preferences
    * can be set.
    */
   showAutoplayMediaExceptions() {
     var params = {
-      blockVisible: false, sessionVisible: false, allowVisible: true,
+      blockVisible: true, sessionVisible: false, allowVisible: true,
       prefilledHost: "", permissionType: "autoplay-media"
     };
 
     gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
       "resizable=yes", params);
   },
 
   // POP-UPS
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -491,18 +491,18 @@
   </grid>
 
   <vbox id="notificationsDoNotDisturbBox" hidden="true">
     <checkbox id="notificationsDoNotDisturb" class="indent"/>
   </vbox>
 
   <separator flex="1"/>
 
-  <hbox align="start" id="autoplayMediaBox" hidden="true">
-    <checkbox id="autoplayMediaPolicy"
+  <hbox align="start" id="autoplayMediaCheckboxWrapper" hidden="true">
+    <checkbox id="autoplayMediaCheckbox"
               data-l10n-id="permissions-block-autoplay-media"
               flex="1" />
     <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
     <hbox>
       <button id="autoplayMediaPolicyButton"
               class="accessory-button"
               data-l10n-id="permissions-block-autoplay-media-exceptions"
               search-l10n-ids="permissions-address,
@@ -563,16 +563,47 @@
     <hbox flex="1" align="center">
       <checkbox id="a11yPrivacyCheckbox" class="tail-with-learn-more"
                 data-l10n-id="permissions-a11y-privacy-checkbox"
                 oncommand="return gPrivacyPane.updateA11yPrefs(this.checked)"/>
       <label id="a11yLearnMoreLink" class="learnMore text-link"
              data-l10n-id="permissions-a11y-privacy-link"/>
     </hbox>
   </vbox>
+
+  <hbox align="center" id="autoplayMediaComboboxWrapper" hidden="true">
+    <hbox align="center" flex="1">
+      <label id="autoplayMediaPolicy"
+             control="autoplayMediaPolicyMenu"
+             data-l10n-id="permissions-block-autoplay-media-menu"/>
+      <menulist id="autoplayMediaPolicyMenu"
+                sizetopopup="always"
+                preference="media.autoplay.default">
+        <menupopup>
+          <!-- Defined in dom/media/nsIAutoplay.idl -->
+          <menuitem data-l10n-id="autoplay-option-allow" value="0"/>
+          <menuitem data-l10n-id="autoplay-option-ask" value="2"/>
+          <menuitem data-l10n-id="autoplay-option-block" value="1"/>
+        </menupopup>
+      </menulist>
+    </hbox>
+
+    <hbox pack="end">
+      <button id="autoplayMediaPolicyComboboxButton"
+              class="accessory-button"
+              data-l10n-id="permissions-block-autoplay-media-exceptions"
+              search-l10n-ids="permissions-address,
+                               permissions-button-cancel.label,
+                               permissions-button-ok.label,
+                               permissions-exceptions-autoplay-media-window.title,
+                               permissions-exceptions-autoplay-media-desc
+                               " />
+    </hbox>
+  </hbox>
+
 </groupbox>
 
 <!-- Firefox Data Collection and Use -->
 #ifdef MOZ_DATA_REPORTING
 <hbox id="dataCollectionCategory"
       class="subcategory"
       hidden="true"
       data-category="panePrivacy">
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -78,16 +78,17 @@ run-if = nightly_build
 [browser_sanitizeOnShutdown_prefLocked.js]
 [browser_searchShowSuggestionsFirst.js]
 [browser_searchsuggestions.js]
 [browser_security-1.js]
 [browser_security-2.js]
 [browser_spotlight.js]
 [browser_site_login_exceptions.js]
 [browser_site_autoplay_media_exceptions.js]
+[browser_site_autoplay_media_prompt.js]
 [browser_permissions_dialog.js]
 [browser_subdialogs.js]
 support-files =
   subdialog.xul
   subdialog2.xul
 [browser_sync_sanitize.js]
 [browser_telemetry.js]
 # Skip this test on Android as FHR and Telemetry are separate systems there.
--- a/browser/components/preferences/in-content/tests/browser_site_autoplay_media_exceptions.js
+++ b/browser/components/preferences/in-content/tests/browser_site_autoplay_media_exceptions.js
@@ -2,58 +2,62 @@
 
 ChromeUtils.import("resource:///modules/SitePermissions.jsm");
 
 const URL = "http://www.example.com";
 const PRINCIPAL = Services.scriptSecurityManager
   .createCodebasePrincipal(Services.io.newURI(URL), {});
 
 const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
-const AUTOPLAY_ENABLED_KEY = "media.autoplay.enabled";
+const AUTOPLAY_ENABLED_KEY = "media.autoplay.default";
 const GESTURES_NEEDED_KEY = "media.autoplay.enabled.user-gestures-needed";
+const ASK_PERMISSIONS_KEY = "media.autoplay.enabled.ask-permissions";
 
 var exceptionsDialog;
 
-Services.prefs.setBoolPref(AUTOPLAY_ENABLED_KEY, true);
+Services.prefs.setIntPref(AUTOPLAY_ENABLED_KEY, Ci.nsIAutoplay.ALLOWED);
 Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, false);
+Services.prefs.setBoolPref(ASK_PERMISSIONS_KEY, true);
 
 async function openExceptionsDialog() {
   let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     let exceptionsButton = content.document.getElementById("autoplayMediaPolicyButton");
     exceptionsButton.click();
   });
   exceptionsDialog = await dialogOpened;
 }
 
 add_task(async function ensureCheckboxHidden() {
 
   registerCleanupFunction(async function() {
     Services.prefs.clearUserPref(AUTOPLAY_ENABLED_KEY);
     Services.prefs.clearUserPref(GESTURES_NEEDED_KEY);
+    Services.prefs.clearUserPref(ASK_PERMISSIONS_KEY);
     gBrowser.removeCurrentTab();
   });
 
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   let win = gBrowser.selectedBrowser.contentWindow;
   is_element_hidden(win.document.getElementById("autoplayMediaPolicy"),
                     "Ensure checkbox is hidden when preffed off");
 });
 
 add_task(async function enableBlockingAutoplay() {
 
   Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, true);
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     let doc = content.document;
-    let autoplayCheckBox = doc.getElementById("autoplayMediaPolicy");
+    let autoplayCheckBox = doc.getElementById("autoplayMediaCheckbox");
     autoplayCheckBox.click();
   });
 
-  Assert.equal(Services.prefs.getBoolPref(AUTOPLAY_ENABLED_KEY), false,
+  Assert.equal(Services.prefs.getIntPref(AUTOPLAY_ENABLED_KEY),
+               Ci.nsIAutoplay.BLOCKED,
                "Ensure we have set autoplay to false");
 });
 
 add_task(async function addException() {
   await openExceptionsDialog();
   let doc = exceptionsDialog.document;
 
   let richlistbox = doc.getElementById("permissionsBox");
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_site_autoplay_media_prompt.js
@@ -0,0 +1,114 @@
+"use strict";
+
+ChromeUtils.import("resource:///modules/SitePermissions.jsm");
+
+const URL = "http://www.example.com";
+const PRINCIPAL = Services.scriptSecurityManager
+  .createCodebasePrincipal(Services.io.newURI(URL), {});
+
+const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
+const AUTOPLAY_ENABLED_KEY = "media.autoplay.default";
+const GESTURES_NEEDED_KEY = "media.autoplay.enabled.user-gestures-needed";
+const ASK_PERMISSIONS_KEY = "media.autoplay.enabled.ask-permissions";
+
+var exceptionsDialog;
+
+Services.prefs.setIntPref(AUTOPLAY_ENABLED_KEY, Ci.nsIAutoplay.ALLOWED);
+Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, false);
+Services.prefs.setBoolPref(ASK_PERMISSIONS_KEY, true);
+
+async function openExceptionsDialog() {
+  let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    let exceptionsButton = content.document.getElementById("autoplayMediaPolicyButton");
+    exceptionsButton.click();
+  });
+  exceptionsDialog = await dialogOpened;
+}
+
+add_task(async function ensureMenuHidden() {
+
+  registerCleanupFunction(async function() {
+    Services.prefs.clearUserPref(AUTOPLAY_ENABLED_KEY);
+    Services.prefs.clearUserPref(GESTURES_NEEDED_KEY);
+    Services.prefs.clearUserPref(ASK_PERMISSIONS_KEY);
+    gBrowser.removeCurrentTab();
+  });
+
+  await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+  let win = gBrowser.selectedBrowser.contentWindow;
+  is_element_hidden(win.document.getElementById("autoplayMediaPolicy"),
+                    "Ensure checkbox is hidden when preffed off");
+});
+
+add_task(async function enableBlockingAutoplay() {
+
+  Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, true);
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    let doc = content.document;
+    let autoplayMenu = doc.getElementById("autoplayMediaPolicyMenu");
+    autoplayMenu.click();
+    let askMenuItem = autoplayMenu.childNodes[0].childNodes[1];
+    askMenuItem.click();
+  });
+
+  Assert.equal(Services.prefs.getIntPref(AUTOPLAY_ENABLED_KEY),
+               Ci.nsIAutoplay.PROMPT,
+               "Ensure we have set autoplay to false");
+});
+
+add_task(async function addException() {
+  await openExceptionsDialog();
+  let doc = exceptionsDialog.document;
+
+  let richlistbox = doc.getElementById("permissionsBox");
+  Assert.equal(richlistbox.itemCount, 0, "Row count should initially be 0");
+
+  let inputBox = doc.getElementById("url");
+  inputBox.focus();
+
+  EventUtils.sendString(URL, exceptionsDialog);
+
+  let btnAllow = doc.getElementById("btnAllow");
+  btnAllow.click();
+
+  await TestUtils.waitForCondition(() => richlistbox.itemCount == 1);
+  Assert.equal(richlistbox.getItemAtIndex(0).getAttribute("origin"), URL);
+
+  let permChanged = TestUtils.topicObserved("perm-changed");
+  let btnApplyChanges = doc.getElementById("btnApplyChanges");
+  btnApplyChanges.click();
+  await permChanged;
+
+  is(Services.perms.testPermissionFromPrincipal(PRINCIPAL, "autoplay-media"),
+     Ci.nsIPermissionManager.ALLOW_ACTION, "Correctly added the exception");
+});
+
+add_task(async function deleteException() {
+  await openExceptionsDialog();
+  let doc = exceptionsDialog.document;
+
+  let richlistbox = doc.getElementById("permissionsBox");
+  Assert.equal(richlistbox.itemCount, 1, "Row count should initially be 1");
+  richlistbox.focus();
+  richlistbox.selectedIndex = 0;
+
+  if (AppConstants.platform == "macosx") {
+    EventUtils.synthesizeKey("KEY_Backspace");
+  } else {
+    EventUtils.synthesizeKey("KEY_Delete");
+  }
+
+  await TestUtils.waitForCondition(() => richlistbox.itemCount == 0);
+  is_element_visible(content.gSubDialog._dialogs[0]._box,
+    "Subdialog is visible after deleting an element");
+
+  let permChanged = TestUtils.topicObserved("perm-changed");
+  let btnApplyChanges = doc.getElementById("btnApplyChanges");
+  btnApplyChanges.click();
+  await permChanged;
+
+  is(Services.perms.testPermissionFromPrincipal(PRINCIPAL, "autoplay-media"),
+     Ci.nsIPermissionManager.UNKNOWN_ACTION, "Correctly removed the exception");
+});
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -844,20 +844,29 @@ permissions-notification-link = Learn mo
 permissions-notification-pause =
     .label = Pause notifications until { -brand-short-name } restarts
     .accesskey = n
 
 permissions-block-autoplay-media =
     .label = Block websites from automatically playing media with sound
     .accesskey = B
 
+permissions-block-autoplay-media-menu = For websites that autoplay sound
+
 permissions-block-autoplay-media-exceptions =
     .label = Exceptions…
     .accesskey = E
 
+autoplay-option-ask =
+    .label = Always Ask
+autoplay-option-allow =
+    .label = Allow Autoplay
+autoplay-option-block =
+    .label = Block Autoplay
+
 permissions-block-popups =
     .label = Block pop-up windows
     .accesskey = B
 
 permissions-block-popups-exceptions =
     .label = Exceptions…
     .accesskey = E
 
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -603,18 +603,22 @@ var gPermissionObject = {
    *    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,
     getDefault() {
-      if (Services.prefs.getBoolPref("media.autoplay.enabled")) {
+      let state = Services.prefs.getIntPref("media.autoplay.default",
+                                            Ci.nsIAutoplay.PROMPT);
+      if (state == Ci.nsIAutoplay.ALLOW) {
         return SitePermissions.ALLOW;
+      } if (state == Ci.nsIAutoplay.BLOCK) {
+        return SitePermissions.DENY;
       }
       return SitePermissions.UNKNOWN;
     },
     labelID: "autoplay-media"
   },
 
   "image": {
     states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
--- a/browser/modules/test/browser/browser_PermissionUI_prompts.js
+++ b/browser/modules/test/browser/browser_PermissionUI_prompts.js
@@ -27,19 +27,19 @@ add_task(async function test_persistent_
 
 // Tests that MidiPrompt works as expected
 add_task(async function test_midi_permission_prompt() {
   await testPrompt(PermissionUI.MIDIPermissionPrompt);
 });
 
 // Tests that AutoplayPermissionPrompt works as expected
 add_task(async function test_autoplay_permission_prompt() {
-  Services.prefs.setBoolPref("media.autoplay.enabled", false);
+  Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.PROMPT);
   await testPrompt(PermissionUI.AutoplayPermissionPrompt);
-  Services.prefs.clearUserPref("media.autoplay.enabled");
+  Services.prefs.clearUserPref("media.autoplay.default");
 });
 
 async function testPrompt(Prompt) {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: "http://example.com",
   }, async function(browser) {
     let mockRequest = makeMockPermissionRequest(browser);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -82,16 +82,17 @@
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDisplayList.h"
 #include "nsDocShell.h"
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIAutoplay.h"
 #include "nsICachingChannel.h"
 #include "nsICategoryManager.h"
 #include "nsIClassOfService.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
@@ -1993,17 +1994,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) == Authorization::Allowed,
+       AutoplayPolicy::IsAllowedToPlay(*this) == nsIAutoplay::ALLOWED,
        OwnerDoc(),
        DocumentOrigin(OwnerDoc()).get(),
        OwnerDoc() ? OwnerDoc()->HasBeenUserGestureActivated() : 0,
        mMuted,
        mVolume));
 
   if (mIsRunningLoadMethod) {
     return;
@@ -2515,17 +2516,17 @@ HTMLMediaElement::ResumeLoad(PreloadActi
 }
 
 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) == Authorization::Allowed &&
+  if ((AutoplayPolicy::IsAllowedToPlay(*this) == nsIAutoplay::ALLOWED &&
        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
       !mPaused) {
     nextAction = HTMLMediaElement::PRELOAD_ENOUGH;
   } else {
     // Find the appropriate preload action by looking at the attribute.
     const nsAttrValue* val =
       mAttrsAndChildren.GetAttr(nsGkAtoms::preload, kNameSpaceID_None);
     // MSE doesn't work if preload is none, so it ignores the pref when src is
@@ -3048,17 +3049,17 @@ HTMLMediaElement::SetMutedInternal(uint3
 }
 
 void
 HTMLMediaElement::PauseIfShouldNotBePlaying()
 {
   if (GetPaused()) {
     return;
   }
-  if (AutoplayPolicy::IsAllowedToPlay(*this) != Authorization::Allowed) {
+  if (AutoplayPolicy::IsAllowedToPlay(*this) != nsIAutoplay::ALLOWED) {
     ErrorResult rv;
     Pause(rv);
   }
 }
 
 void
 HTMLMediaElement::SetVolumeInternal()
 {
@@ -4059,31 +4060,31 @@ HTMLMediaElement::Play(ErrorResult& aRv)
     if (StaticPrefs::MediaBlockEventEnabled()) {
       DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
     }
     return promise.forget();
   }
 
   const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
   switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
-    case Authorization::Allowed: {
+    case nsIAutoplay::ALLOWED: {
       mPendingPlayPromises.AppendElement(promise);
       PlayInternal(handlingUserInput);
       UpdateCustomPolicyAfterPlayed();
       break;
     }
-    case Authorization::Blocked: {
+    case nsIAutoplay::BLOCKED: {
       LOG(LogLevel::Debug, ("%p play not blocked.", this));
       promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
       if (StaticPrefs::MediaBlockEventEnabled()) {
         DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
       }
       break;
     }
-    case Authorization::Prompt: {
+    case nsIAutoplay::PROMPT: {
       // Prompt the user for permission to play.
       mPendingPlayPromises.AppendElement(promise);
       EnsureAutoplayRequested(handlingUserInput);
       break;
     }
   }
   return promise.forget();
 }
@@ -6122,17 +6123,17 @@ HTMLMediaElement::ChangeReadyState(nsMed
     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) ==
-                   Authorization::Allowed);
+                   nsIAutoplay::ALLOWED);
         mDecoder->Play();
       }
       NotifyAboutPlaying();
     }
   }
 
   CheckAutoplayDataReady();
 
@@ -6234,22 +6235,22 @@ HTMLMediaElement::CanActivateAutoplay()
 void
 HTMLMediaElement::CheckAutoplayDataReady()
 {
   if (!CanActivateAutoplay()) {
     return;
   }
 
   switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
-    case Authorization::Blocked:
+    case nsIAutoplay::BLOCKED:
       return;
-    case Authorization::Prompt:
+    case nsIAutoplay::PROMPT:
       EnsureAutoplayRequested(false);
       return;
-    case Authorization::Allowed:
+    case nsIAutoplay::ALLOWED:
       break;
   }
 
   mPaused = false;
   // We changed mPaused which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -7,16 +7,17 @@
 #include "AutoplayPolicy.h"
 
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/AutoplayPermissionManager.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
+#include "nsIAutoplay.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "MediaManager.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
@@ -83,52 +84,57 @@ AutoplayPolicy::RequestFor(const nsIDocu
   }
   nsPIDOMWindowInner* window = document->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
   return window->GetAutoplayPermissionManager();
 }
 
-/* static */ Authorization
+static uint32_t
+DefaultAutoplayBehaviour()
+{
+  int prefValue = Preferences::GetInt("media.autoplay.default", nsIAutoplay::ALLOWED);
+  if (prefValue < nsIAutoplay::ALLOWED || prefValue > nsIAutoplay::PROMPT) {
+    // Invalid pref values are just converted to ALLOWED.
+    return nsIAutoplay::ALLOWED;
+  }
+  return prefValue;
+}
+
+/* static */ uint32_t
 AutoplayPolicy::IsAllowedToPlay(const HTMLMediaElement& aElement)
 {
-  if (Preferences::GetBool("media.autoplay.enabled")) {
-    return Authorization::Allowed;
-  }
-
+  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 (aElement.IsBlessed() || EventStateManager::IsHandlingUserInput())
-             ? Authorization::Allowed
-             : Authorization::Blocked;
+    return (autoplayDefault == nsIAutoplay::ALLOWED ||
+            aElement.IsBlessed() ||
+            EventStateManager::IsHandlingUserInput())
+              ? nsIAutoplay::ALLOWED : nsIAutoplay::BLOCKED;
   }
 
   // Muted content
   if (aElement.Volume() == 0.0 || aElement.Muted()) {
-    return Authorization::Allowed;
+    return nsIAutoplay::ALLOWED;
   }
 
   if (IsWindowAllowedToPlay(aElement.OwnerDoc()->GetInnerWindow())) {
-    return Authorization::Allowed;
+    return nsIAutoplay::ALLOWED;
   }
 
-  if (Preferences::GetBool("media.autoplay.ask-permission", false)) {
-    return Authorization::Prompt;
-  }
-
-  return Authorization::Blocked;
+  return autoplayDefault;
 }
 
 /* static */ bool
 AutoplayPolicy::IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext)
 {
-  if (Preferences::GetBool("media.autoplay.enabled")) {
+  if (DefaultAutoplayBehaviour() == nsIAutoplay::ALLOWED) {
     return true;
   }
 
   if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
     return true;
   }
 
   // Offline context won't directly output sound to audio devices.
--- a/dom/media/AutoplayPolicy.h
+++ b/dom/media/AutoplayPolicy.h
@@ -15,40 +15,33 @@ namespace mozilla {
 
 class AutoplayPermissionManager;
 
 namespace dom {
 
 class HTMLMediaElement;
 class AudioContext;
 
-enum class Authorization
-{
-  Allowed,
-  Blocked,
-  Prompt
-};
-
 /**
  * AutoplayPolicy is used to manage autoplay logic for all kinds of media,
  * including MediaElement, Web Audio and Web Speech.
  *
- * Autoplay could be disable by turn off the pref "media.autoplay.enabled".
- * Once user disable autoplay, media could only be played if one of following
- * conditions is true.
+ * Autoplay could be disable by setting the pref "media.autoplay.default"
+ * to anything but nsIAutoplay::Allowed. Once user disables autoplay, media
+ * could only be played if one of following conditions is true.
  * 1) Owner document is activated by user gestures
  *    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 Authorization IsAllowedToPlay(const HTMLMediaElement& aElement);
+  static uint32_t IsAllowedToPlay(const HTMLMediaElement& aElement);
 
   // Returns whether a given AudioContext is allowed to play.
   static bool IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext);
 
   // Returns the AutoplayPermissionManager that a given document must request on
   // for autoplay permission.
   static already_AddRefed<AutoplayPermissionManager> RequestFor(
     const nsIDocument& aDocument);
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -76,16 +76,17 @@ MOCHITEST_MANIFESTS += [
 
 if CONFIG['MOZ_WEBRTC']:
     MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
     WEBRTC_SIGNALLING_TEST_MANIFESTS += ['tests/mochitest/steeplechase.ini']
     WEBRTC_SIGNALLING_TEST_MANIFESTS += ['tests/mochitest/steeplechase_long/steeplechase_long.ini']
 
 XPIDL_SOURCES += [
     'nsIAudioDeviceInfo.idl',
+    'nsIAutoplay.idl',
     'nsIDOMNavigatorUserMedia.idl',
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'ADTSDecoder.h',
new file mode 100644
--- /dev/null
+++ b/dom/media/nsIAutoplay.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(048a24f6-c4d6-47bc-bea2-f6038d1db80a)]
+interface nsIAutoplay : nsISupports
+{
+  /*
+   * Possible values for the "media.autoplay.default" preference.
+   */
+  const uint32_t ALLOWED = 0;
+  const uint32_t BLOCKED = 1;
+  const uint32_t PROMPT  = 2;
+};
--- a/dom/media/test/test_autoplay_policy.html
+++ b/dom/media/test/test_autoplay_policy.html
@@ -9,17 +9,17 @@
 </head>
 <body>
 <pre id="test">
 
 <script>
 
 let manager = new MediaTestManager;
 
-gTestPrefs.push(["media.autoplay.enabled", false],
+gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
                 ["media.autoplay.enabled.user-gestures-needed", true]);
 
 window.info = function(msg, token) {
   SimpleTest.info(msg + ", token=" + token);
 }
 
 window.is = function(valA, valB, msg, token) {
   SimpleTest.is(valA, valB, msg + ", token=" + token);
@@ -66,17 +66,17 @@ function createTestArray()
     }
   }
   return tests;
 }
 
 /**
  * Main test function for different autoplay cases without user interaction.
  *
- * When the pref "media.autoplay.enabled" is false and the pref
+ * When the pref "media.autoplay.default" is 1 and the pref
  * "media.autoplay.enabled.user-gestures-needed" is true, we only allow
  * audible media to autoplay after the website has been activated by specific
  * user gestures. However, inaudible media won't be restricted.
  *
  * Audible means the volume is non zero and muted is false.
  *
  * Inaudible means the volume is zero, or the muted is true.
  *
@@ -147,9 +147,9 @@ async function testAutoplayKeyword(test,
   } else {
     await once(element, "play");
     ok(!element.paused, `start with 'autoplay' keyword for ${state} media`, token);
   }
 
   removeNodeAndSource(element);
 }
 
-</script>
\ No newline at end of file
+</script>
--- a/dom/media/test/test_autoplay_policy_activation.html
+++ b/dom/media/test/test_autoplay_policy_activation.html
@@ -9,17 +9,17 @@
   </head>
   <body>
     <pre id="test">
       <script>
 
         // Tests that videos can only play audibly in windows/frames
         // which have been activated by same-origin user gesture.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
                         ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({'set': gTestPrefs}, () => {
           runTest();
         });
 
         let test_cases = [
           {
--- a/dom/media/test/test_autoplay_policy_eventdown_activation.html
+++ b/dom/media/test/test_autoplay_policy_eventdown_activation.html
@@ -10,17 +10,17 @@
 </head>
 
 <body>
   <pre id="test">
       <script>
 
         // Tests that we gesture activate on mousedown and keydown.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
           ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
         let child_url = "file_autoplay_policy_eventdown_activation.html";
 
@@ -47,9 +47,9 @@
         }
 
         SimpleTest.waitForExplicitFinish();
 
       </script>
     </pre>
 </body>
 
-</html>
\ No newline at end of file
+</html>
--- a/dom/media/test/test_autoplay_policy_key_blacklist.html
+++ b/dom/media/test/test_autoplay_policy_key_blacklist.html
@@ -13,17 +13,17 @@
   <pre id="test">
       <script>
 
         // Tests that keypresses for non-printable characters,
         // and mouse/keyboard interaction with editable elements,
         // don't gesture activate documents, and don't unblock
         // audible autoplay.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
           ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
         let child_url = "file_autoplay_policy_key_blacklist.html";
 
@@ -39,9 +39,9 @@
         }
 
         SimpleTest.waitForExplicitFinish();
 
       </script>
     </pre>
 </body>
 
-</html>
\ No newline at end of file
+</html>
--- a/dom/media/test/test_autoplay_policy_permission.html
+++ b/dom/media/test/test_autoplay_policy_permission.html
@@ -10,17 +10,17 @@
 </head>
 
 <body>
   <pre id="test">
       <script>
 
         // Tests that origins with "autoplay-media" permission can autoplay.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
           ["media.autoplay.enabled.user-gestures-needed", true],
           // Note: permission prompt disabled, as we want immediate
           // notification in this test that an origin is blocked.
           ["media.autoplay.ask-permission", false]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
--- a/dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
+++ b/dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
@@ -14,17 +14,17 @@
       <script>
 
         window.is = SimpleTest.is;
         window.info = SimpleTest.info;
 
         // Tests that videos which have no audio track will play if play()
         // is called before the video has reached readyState >= HAVE_METADATA.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
           ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
         let testCases = [
           {
@@ -65,9 +65,9 @@
         }
 
         SimpleTest.waitForExplicitFinish();
 
       </script>
     </pre>
 </body>
 
-</html>
\ No newline at end of file
+</html>
--- a/dom/media/test/test_autoplay_policy_unmute_pauses.html
+++ b/dom/media/test/test_autoplay_policy_unmute_pauses.html
@@ -14,17 +14,17 @@
       <script>
 
         window.is = SimpleTest.is;
         window.info = SimpleTest.info;
 
         // Tests that videos can only play audibly in windows/frames
         // which have been activated by same-origin user gesture.
 
-        gTestPrefs.push(["media.autoplay.enabled", false],
+        gTestPrefs.push(["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
           ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
         let testCases = [
           {
@@ -56,9 +56,9 @@
         }
 
         SimpleTest.waitForExplicitFinish();
 
       </script>
     </pre>
 </body>
 
-</html>
\ No newline at end of file
+</html>
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -434,17 +434,17 @@ function setupEnvironment() {
 
   const isAndroid = !!navigator.userAgent.includes("Android");
 
   if (isAndroid) {
     defaultMochitestPrefs.set.push(
       ["media.navigator.video.default_width", 320],
       ["media.navigator.video.default_height", 240],
       ["media.navigator.video.max_fr", 10],
-      ["media.autoplay.enabled", true]
+      ["media.autoplay.default", Ci.nsIAutoplay.ALLOWED]
     );
   }
 
   // Running as a Mochitest.
   SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts");
   window.finish = () => SimpleTest.finish();
   SpecialPowers.pushPrefEnv(defaultMochitestPrefs, setTestOptions);
 
--- a/dom/media/webaudio/test/test_notAllowedToStartAudioContextGC.html
+++ b/dom/media/webaudio/test/test_notAllowedToStartAudioContextGC.html
@@ -20,17 +20,17 @@ function observer(subject, topic, data) 
   ok(id != destId, "dropping another node, not the context's destination");
 }
 
 SpecialPowers.addAsyncObserver(observer, "webaudio-node-demise", false);
 SimpleTest.registerCleanupFunction(function() {
   SpecialPowers.removeAsyncObserver(observer, "webaudio-node-demise");
 });
 
-SpecialPowers.pushPrefEnv({"set": [["media.autoplay.enabled", false],
+SpecialPowers.pushPrefEnv({"set": [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
                                    ["media.autoplay.enabled.user-gestures-needed", true]]},
                           startTest);
 
 function startTest() {
   info("- create audio context -");
   let ac = new AudioContext();
 
   info("- get node Id -");
--- a/mobile/android/app/src/main/res/xml/preferences_advanced.xml
+++ b/mobile/android/app/src/main/res/xml/preferences_advanced.xml
@@ -49,17 +49,17 @@
         <SwitchPreference android:key="browser.display.use_document_fonts"
                           android:title="@string/pref_show_web_fonts"
                           android:summary="@string/pref_show_web_fonts_summary"/>
 
     </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/pref_category_media">
 
-        <SwitchPreference android:key="media.autoplay.enabled"
+        <SwitchPreference android:key="media.autoplay.default"
                           android:title="@string/pref_media_autoplay_enabled"
                           android:summary="@string/pref_media_autoplay_enabled_summary" />
 
     </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/pref_category_developer_tools">
 
         <SwitchPreference android:key="devtools.remote.usb.enabled"
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -558,22 +558,24 @@ pref("media.encoder.webm.enabled", true)
 
 // Whether to allow recording of AudioNodes with MediaRecorder
 pref("media.recorder.audio_node.enabled", false);
 
 // Whether MediaRecorder's video encoder should allow dropping frames in order
 // to keep up under load. Useful for tests but beware of memory consumption!
 pref("media.recorder.video.frame_drops", true);
 
-// Whether to autostart a media element with an |autoplay| attribute
-pref("media.autoplay.enabled", true);
-
-// If "media.autoplay.enabled" is false, and this pref is true, then audible media
-// would only be allowed to autoplay after website has been activated by specific
-// user gestures, but the non-audible media won't be restricted.
+// Whether to autostart a media element with an |autoplay| attribute.
+// ALLOWED=0, BLOCKED=1, PROMPT=2, defined in dom/media/Autoplay.idl
+pref("media.autoplay.default", 0);
+
+// If "media.autoplay.default" is not ALLOWED, and this pref is true,
+// then audible media would only be allowed to autoplay after website has
+// been activated by specific user gestures, but non-audible
+// media won't be restricted.
 #ifdef NIGHTLY_BUILD
 pref("media.autoplay.enabled.user-gestures-needed", false);
 #endif
 
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 10);
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -12,17 +12,17 @@
 const {escaped} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
 
 const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
 
 Cu.importGlobalProperties(["URL"]);
 
 // Make sure media pre-loading is enabled on Android so that our <audio> and
 // <video> elements trigger the expected requests.
-Services.prefs.setBoolPref("media.autoplay.enabled", true);
+Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.ALLOWED);
 Services.prefs.setIntPref("media.preload.default", 3);
 
 // Increase the length of the code samples included in CSP reports so that we
 // can correctly validate them.
 Services.prefs.setIntPref("security.csp.reporting.script-sample.max-length", 4096);
 
 // ExtensionContent.jsm needs to know when it's running from xpcshell,
 // to use the right timeout for content scripts executed at document_idle.
--- a/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
+++ b/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
@@ -19,17 +19,17 @@ const PAGE_A1_B2 = "https://example.com/
 const PAGE_A1_B2_C3 = "https://test1.example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
 const PAGE_A1_B2_A3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
 const PAGE_A1_B2_B3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
 const PAGE_A1_A2_A3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
 const PAGE_A1_A2_B3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
 
 function setup_test_preference() {
   return SpecialPowers.pushPrefEnv({"set": [
-    ["media.autoplay.enabled", false],
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
     ["media.autoplay.enabled.user-gestures-needed", true]
   ]});
 }
 
 var frameTestArray = [
   { name: "A1_A2",    layersNum: 2, src: PAGE_A1_A2 },
   { name: "A1_B2",    layersNum: 2, src: PAGE_A1_B2 },
   { name: "A1_B2_C3", layersNum: 3, src: PAGE_A1_B2_C3 },
--- a/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
+++ b/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
@@ -1,15 +1,15 @@
 const VIDEO_PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_video.html";
 
 function setup_test_preference(enableUserGesture) {
   let state = enableUserGesture ? "enable" : "disable";
   info(`- set pref : ${state} user gesture -`);
   return SpecialPowers.pushPrefEnv({"set": [
-    ["media.autoplay.enabled", false],
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
     ["media.autoplay.enabled.user-gestures-needed", enableUserGesture]
   ]});
 }
 
 async function allow_play_for_played_video() {
   info("- open new tab  -");
   let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
                                                         "about:blank");
--- a/toolkit/content/tests/browser/browser_autoplay_policy_request_permission.js
+++ b/toolkit/content/tests/browser/browser_autoplay_policy_request_permission.js
@@ -5,17 +5,17 @@
 "use strict";
 
 ChromeUtils.import("resource:///modules/SitePermissions.jsm", this);
 
 const VIDEO_PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_empty.html";
 
 add_task(() => {
   return SpecialPowers.pushPrefEnv({"set": [
-    ["media.autoplay.enabled", false],
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.PROMPT],
     ["media.autoplay.enabled.user-gestures-needed", true],
     ["media.autoplay.ask-permission", true],
     ["media.autoplay.block-event.enabled", true],
   ]});
 });
 
 // Runs a content script that creates an autoplay video.
 //  browser: the browser to run the script in.
--- a/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js
+++ b/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js
@@ -11,17 +11,17 @@ var UserGestures = {
 var UserGestureTests = [
   {type: UserGestures.MOUSE_CLICK, isActivationGesture: true},
   {type: UserGestures.MOUSE_MOVE, isActivationGesture: false},
   {type: UserGestures.KEYBOARD_PRESS, isActivationGesture: true}
 ];
 
 function setup_test_preference() {
   return SpecialPowers.pushPrefEnv({"set": [
-    ["media.autoplay.enabled", false],
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
     ["media.autoplay.enabled.user-gestures-needed", true],
     ["media.navigator.permission.fake", true]
   ]});
 }
 
 function simulateUserGesture(gesture, targetBrowser) {
   info(`- simulate ${gesture.type} event -`);
   switch (gesture.type) {
--- a/toolkit/content/tests/browser/browser_block_autoplay_media.js
+++ b/toolkit/content/tests/browser/browser_block_autoplay_media.js
@@ -79,17 +79,17 @@ add_task(async function block_autoplay_m
   //
   // Clicking "play" on the audio tab indicator should always start playback
   // in that tab, even if it's in an autoplay-blocked origin.
   //
   // Also test that that this block-autoplay logic override doesn't survive
   // a new document being loaded into the tab; the new document should have
   // to satisfy the autoplay requirements on its own.
   await SpecialPowers.pushPrefEnv({"set": [
-    ["media.autoplay.enabled", false],
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.PROMPT],
     ["media.autoplay.enabled.user-gestures-needed", true],
     ["media.autoplay.ask-permission", true],
   ]});
 
   info("- open new background tab4 -");
   let tab4 = window.gBrowser.addTab("about:blank");
   tab4.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab4.linkedBrowser);