Bug 1470082 - Change autoplay checkbox to combobox. r=cpearce,flod,johannh
authorDale Harvey <dale@arandomurl.com>
Fri, 29 Jun 2018 14:14:33 +0100
changeset 482170 36ddc886d33bb23d11061ea65874c5902b80a64a
parent 482169 0cf499a0fee9540c2e98a5df5b487cd7be0f11c3
child 482171 8a34e2e1ed6f1d4abe725c1584309fc198ba9272
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, flod, johannh
bugs1470082
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 1470082 - Change autoplay checkbox to combobox. r=cpearce,flod,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);