Bug 1543812 - Add ability to block all autoplay. r=johannh,alwu,flod,fluent-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D30135

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dale Harvey 2019-06-04 17:33:06 +00:00
Родитель 523e69c325
Коммит 6066384ce3
21 изменённых файлов: 338 добавлений и 171 удалений

Просмотреть файл

@ -1518,7 +1518,7 @@ pref("media.gmp-gmpopenh264.enabled", true);
// Switch block autoplay logic to v2, and enable UI.
pref("media.autoplay.enabled.user-gestures-needed", true);
// Set Firefox to block autoplay, asking for permission by default.
pref("media.autoplay.default", 1); // 0=Allowed, 1=Blocked
pref("media.autoplay.default", 1); // 0=Allowed, 1=Blocked, 5=All Blocked
#ifdef NIGHTLY_BUILD
// Block WebAudio from playing automatically.

Просмотреть файл

@ -643,7 +643,8 @@ var gIdentityHandler = {
// show permission icons
let permissions = SitePermissions.getAllForBrowser(gBrowser.selectedBrowser);
for (let permission of permissions) {
if (permission.state == SitePermissions.BLOCK) {
if (permission.state == SitePermissions.BLOCK ||
permission.state == SitePermissions.AUTOPLAY_BLOCKED_ALL) {
let icon = permissionAnchors[permission.id];
if (icon) {
icon.setAttribute("showing", "true");
@ -1141,7 +1142,8 @@ var gIdentityHandler = {
} else {
menuitem.setAttribute("value", state);
}
menuitem.setAttribute("label", SitePermissions.getMultichoiceStateLabel(state));
menuitem.setAttribute("label", SitePermissions.getMultichoiceStateLabel(aPermission.id, state));
menupopup.appendChild(menuitem);
}

Просмотреть файл

@ -151,7 +151,7 @@ function createRow(aPartId) {
for (let state of SitePermissions.getAvailableStates(aPartId)) {
let radio = document.createXULElement("radio");
radio.setAttribute("id", aPartId + "#" + state);
radio.setAttribute("label", SitePermissions.getMultichoiceStateLabel(state));
radio.setAttribute("label", SitePermissions.getMultichoiceStateLabel(aPartId, state));
radio.setAttribute("command", commandId);
radiogroup.appendChild(radio);
}

Просмотреть файл

@ -22,6 +22,7 @@ support-files =
support-files =
browser_autoplay_blocked.html
browser_autoplay_blocked_slow.sjs
browser_autoplay_muted.html
../general/audio.ogg
skip-if = verify && os == 'linux' && debug # Bug 1483648
[browser_temporary_permissions_expiry.js]

Просмотреть файл

@ -6,6 +6,8 @@ const AUTOPLAY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/c
const SLOW_AUTOPLAY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "browser_autoplay_blocked_slow.sjs";
const MUTED_AUTOPLAY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "browser_autoplay_muted.html";
const AUTOPLAY_PREF = "media.autoplay.default";
const AUTOPLAY_PERM = "autoplay-media";
@ -80,15 +82,15 @@ add_task(async function testMainViewVisible() {
is(labels[0].textContent, labelText, "Correct value");
let menulist = document.getElementById("identity-popup-popup-menulist");
Assert.equal(menulist.label, "Block");
Assert.equal(menulist.label, "Block Audio");
await EventUtils.synthesizeMouseAtCenter(menulist, { type: "mousedown" });
await BrowserTestUtils.waitForCondition(() => {
return menulist.getElementsByTagName("menuitem")[0].label === "Allow";
await TestUtils.waitForCondition(() => {
return menulist.getElementsByTagName("menuitem")[0].label === "Allow Audio and Video";
});
let menuitem = menulist.getElementsByTagName("menuitem")[0];
Assert.equal(menuitem.getAttribute("label"), "Allow");
Assert.equal(menuitem.getAttribute("label"), "Allow Audio and Video ");
menuitem.click();
menulist.menupopup.hidePopup();
@ -155,6 +157,7 @@ add_task(async function testChangingBlockingSettingDuringNavigation() {
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
await BrowserTestUtils.withNewTab("about:home", async function(browser) {
await blockedIconHidden();
await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE);
await blockedIconShown();
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.ALLOWED);
@ -193,3 +196,29 @@ add_task(async function testSlowLoadingPage() {
Services.perms.removeAll();
});
add_task(async function testBlockedAll() {
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED_ALL);
await BrowserTestUtils.withNewTab("about:home", async function(browser) {
await blockedIconHidden();
await BrowserTestUtils.loadURI(browser, MUTED_AUTOPLAY_PAGE);
await blockedIconShown();
await openIdentityPopup();
let menulist = document.getElementById("identity-popup-popup-menulist");
await EventUtils.synthesizeMouseAtCenter(menulist, { type: "mousedown" });
await TestUtils.waitForCondition(() => {
return menulist.getElementsByTagName("menuitem")[1].label === "Block Audio";
});
let menuitem = menulist.getElementsByTagName("menuitem")[0];
menuitem.click();
menulist.menupopup.hidePopup();
await closeIdentityPopup();
gBrowser.reload();
await blockedIconHidden();
});
Services.perms.removeAll();
});

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html dir="ltr" xml:lang="en-US" lang="en-US">
<head>
<meta charset="utf8">
</head>
<body>
<audio autoplay="autoplay" muted>
<source src="audio.ogg" />
</audio>
</body>
</html>

Просмотреть файл

@ -288,16 +288,12 @@ var gPrivacyPane = {
/* Initialize Content Blocking */
this.initContentBlocking();
this.blockAutoplayReadPrefs();
this.trackingProtectionReadPrefs();
this.networkCookieBehaviorReadPrefs();
this._initTrackingProtectionExtensionControl();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
Preferences.get("media.autoplay.default").on("change",
gPrivacyPane.blockAutoplayReadPrefs.bind(gPrivacyPane));
Preferences.get("privacy.trackingprotection.enabled").on("change",
gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
@ -361,6 +357,8 @@ var gPrivacyPane = {
this._initMasterPasswordUI();
this._initSafeBrowsing();
setEventListener("autoplaySettingsButton", "command",
gPrivacyPane.showAutoplayMediaExceptions);
setEventListener("notificationSettingsButton", "command",
gPrivacyPane.showNotificationExceptions);
setEventListener("locationSettingsButton", "command",
@ -371,10 +369,6 @@ var gPrivacyPane = {
gPrivacyPane.showMicrophoneExceptions);
setEventListener("popupPolicyButton", "command",
gPrivacyPane.showPopupExceptions);
setEventListener("autoplayMediaCheckbox", "command",
gPrivacyPane.toggleAutoplayMedia);
setEventListener("autoplayMediaPolicyButton", "command",
gPrivacyPane.showAutoplayMediaExceptions);
setEventListener("notificationsDoNotDisturb", "command",
gPrivacyPane.toggleDoNotDisturbNotifications);
@ -1291,31 +1285,10 @@ var gPrivacyPane = {
// MEDIA
blockAutoplayReadPrefs() {
let blocked =
Preferences.get("media.autoplay.default").value == Ci.nsIAutoplay.BLOCKED;
document.getElementById("autoplayMediaCheckbox").checked = blocked;
},
/**
* The checkbox enabled sets the pref to BLOCKED
*/
toggleAutoplayMedia(event) {
let blocked = event.target.checked ? Ci.nsIAutoplay.BLOCKED : Ci.nsIAutoplay.ALLOWED;
Services.prefs.setIntPref("media.autoplay.default", blocked);
},
/**
* Displays the autoplay exceptions dialog where specific site autoplay preferences
* can be set.
*/
showAutoplayMediaExceptions() {
var params = {
blockVisible: true, sessionVisible: false, allowVisible: true,
prefilledHost: "", permissionType: "autoplay-media",
};
var params = { permissionType: "autoplay-media" };
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
"resizable=yes", params);
},

Просмотреть файл

@ -708,33 +708,39 @@
" />
</hbox>
</hbox>
</vbox>
<vbox id="notificationsDoNotDisturbBox" hidden="true">
<checkbox id="notificationsDoNotDisturb" class="indent"/>
<vbox id="notificationsDoNotDisturbBox" hidden="true">
<checkbox id="notificationsDoNotDisturb" class="indent"/>
</vbox>
<hbox id="autoplaySettingsRow" align="center" role="group" aria-labelledby="autoplayPermissionsLabel">
<description flex="1">
<image class="autoplay-icon permission-icon" />
<separator orient="vertical" class="thin"/>
<label id="autoplayPermissionsLabel"
data-l10n-id="permissions-autoplay"/>
</description>
<hbox pack="end">
<button id="autoplaySettingsButton"
is="highlightable-button"
class="accessory-button"
data-l10n-id="permissions-autoplay-settings"
search-l10n-ids="
permissions-remove.label,
permissions-remove-all.label,
permissions-button-cancel.label,
permissions-button-ok.label,
permissions-site-autoplay-window.title,
permissions-site-autoplay-desc,
permissions-site-autoplay-disable-label,
permissions-site-autoplay-disable-desc,
" />
</hbox>
</hbox>
</vbox>
<separator flex="1"/>
<hbox align="start" id="autoplayMediaCheckboxWrapper">
<checkbox id="autoplayMediaCheckbox"
data-l10n-id="permissions-block-autoplay-media2"
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"
is="highlightable-button"
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-window2.title,
permissions-exceptions-autoplay-media-desc2
" />
</hbox>
</hbox>
<hbox data-subcategory="permissions-block-popups">
<checkbox id="popupPolicy" preference="dom.disable_open_during_load"
data-l10n-id="permissions-block-popups"

Просмотреть файл

@ -26,10 +26,6 @@ const permissionExceptionsL10n = {
window: "permissions-exceptions-addons-window",
description: "permissions-exceptions-addons-desc",
},
"autoplay-media": {
window: "permissions-exceptions-autoplay-media-window2",
description: "permissions-exceptions-autoplay-media-desc2",
},
};
function Permission(principal, type, capability) {

Просмотреть файл

@ -33,6 +33,26 @@ const sitePermissionsL10n = {
disableLabel: "permissions-site-microphone-disable-label",
disableDescription: "permissions-site-microphone-disable-desc",
},
"autoplay-media": {
window: "permissions-site-autoplay-window",
description: "permissions-site-autoplay-desc",
},
};
const sitePermissionsConfig = {
"autoplay-media": {
_getCapabilityString(capability) {
switch (capability) {
case SitePermissions.ALLOW:
return "permissions-capabilities-autoplay-allow";
case SitePermissions.BLOCK:
return "permissions-capabilities-autoplay-block";
case SitePermissions.AUTOPLAY_BLOCKED_ALL:
return "permissions-capabilities-autoplay-blockall";
}
throw new Error(`Unknown capability: ${capability}`);
},
},
};
function Permission(principal, type, capability, l10nId) {
@ -43,10 +63,16 @@ function Permission(principal, type, capability, l10nId) {
this.l10nId = l10nId;
}
const PERMISSION_STATES = [SitePermissions.ALLOW, SitePermissions.BLOCK, SitePermissions.PROMPT];
const PERMISSION_STATES = [
SitePermissions.ALLOW, SitePermissions.BLOCK,
SitePermissions.PROMPT, SitePermissions.AUTOPLAY_BLOCKED_ALL,
];
const NOTIFICATIONS_PERMISSION_OVERRIDE_KEY = "webNotificationsDisabled";
const NOTIFICATIONS_PERMISSION_PREF = "permissions.default.desktop-notification";
const AUTOPLAY_PREF = "media.autoplay.default";
var gSitePermissionsManager = {
_type: "",
_isObserving: false,
@ -80,13 +106,18 @@ var gSitePermissionsManager = {
this._checkbox = document.getElementById("permissionsDisableCheckbox");
this._disableExtensionButton = document.getElementById("disableNotificationsPermissionExtension");
this._permissionsDisableDescription = document.getElementById("permissionsDisableDescription");
this._setAutoplayPref = document.getElementById("setAutoplayPref");
let permissionsText = document.getElementById("permissionsText");
let l10n = sitePermissionsL10n[this._type];
document.l10n.setAttributes(permissionsText, l10n.description);
document.l10n.setAttributes(this._checkbox, l10n.disableLabel);
document.l10n.setAttributes(this._permissionsDisableDescription, l10n.disableDescription);
if (l10n.disableLabel) {
document.l10n.setAttributes(this._checkbox, l10n.disableLabel);
}
if (l10n.disableDescription) {
document.l10n.setAttributes(this._permissionsDisableDescription, l10n.disableDescription);
}
document.l10n.setAttributes(document.documentElement, l10n.window);
await document.l10n.translateElements([
@ -104,6 +135,11 @@ var gSitePermissionsManager = {
this._loadPermissions();
this.buildPermissionsList();
if (params.permissionType == "autoplay-media") {
this.buildAutoplayMenulist();
this._setAutoplayPref.hidden = false;
}
this._searchBox.focus();
},
@ -112,6 +148,9 @@ var gSitePermissionsManager = {
Services.obs.removeObserver(this, "perm-changed");
this._isObserving = false;
}
if (this._setAutoplayPref) {
this._setAutoplayPref.hidden = true;
}
},
observe(subject, topic, data) {
@ -130,7 +169,7 @@ var gSitePermissionsManager = {
} else if (data == "changed") {
let p = this._permissions.get(permission.principal.origin);
p.capability = permission.capability;
p.l10nId = this._getCapabilityString(permission.capability);
p.l10nId = this._getCapabilityString(permission.type, permission.capability);
this._handleCapabilityChange(p);
this.buildPermissionsList();
} else if (data == "deleted") {
@ -207,29 +246,29 @@ var gSitePermissionsManager = {
}
},
_getCapabilityString(capability) {
let stringKey = null;
_getCapabilityString(type, capability) {
if (type in sitePermissionsConfig &&
sitePermissionsConfig[type]._getCapabilityString) {
return sitePermissionsConfig[type]._getCapabilityString(capability);
}
switch (capability) {
case Services.perms.ALLOW_ACTION:
stringKey = "permissions-capabilities-allow";
break;
return "permissions-capabilities-allow";
case Services.perms.DENY_ACTION:
stringKey = "permissions-capabilities-block";
break;
return "permissions-capabilities-block";
case Services.perms.PROMPT_ACTION:
stringKey = "permissions-capabilities-prompt";
break;
return "permissions-capabilities-prompt";
default:
throw new Error(`Unknown capability: ${capability}`);
}
return stringKey;
},
_addPermissionToList(perm) {
// Ignore unrelated permission types and permissions with unknown states.
if (perm.type !== this._type || !PERMISSION_STATES.includes(perm.capability))
return;
let l10nId = this._getCapabilityString(perm.capability);
let l10nId = this._getCapabilityString(perm.type, perm.capability);
let p = new Permission(perm.principal, perm.type, perm.capability, l10nId);
this._permissions.set(p.origin, p);
},
@ -250,6 +289,7 @@ var gSitePermissionsManager = {
},
_createPermissionListItem(permission) {
let width = (permission.type == "autoplay-media") ? "75" : "50";
let richlistitem = document.createXULElement("richlistitem");
richlistitem.setAttribute("origin", permission.origin);
let row = document.createXULElement("hbox");
@ -258,14 +298,14 @@ var gSitePermissionsManager = {
let hbox = document.createXULElement("hbox");
let website = document.createXULElement("label");
website.setAttribute("value", permission.origin);
website.setAttribute("width", "50");
website.setAttribute("width", width);
hbox.setAttribute("class", "website-name");
hbox.setAttribute("flex", "3");
hbox.appendChild(website);
let menulist = document.createXULElement("menulist");
menulist.setAttribute("flex", "1");
menulist.setAttribute("width", "50");
menulist.setAttribute("width", width);
menulist.setAttribute("class", "website-status");
let states = SitePermissions.getAvailableStates(permission.type);
for (let state of states) {
@ -279,7 +319,7 @@ var gSitePermissionsManager = {
continue;
}
let m = menulist.appendItem(undefined, state);
document.l10n.setAttributes(m, this._getCapabilityString(state));
document.l10n.setAttributes(m, this._getCapabilityString(permission.type, state));
}
menulist.value = permission.capability;
@ -360,7 +400,7 @@ var gSitePermissionsManager = {
if (p.capability == capability)
return;
p.capability = capability;
p.l10nId = this._getCapabilityString(capability);
p.l10nId = this._getCapabilityString(perm.type, perm.capability);
this._permissionsToChange.set(p.origin, p);
// enable "remove all" button as needed
@ -418,6 +458,23 @@ var gSitePermissionsManager = {
this._setRemoveButtonState();
},
buildAutoplayMenulist() {
let menulist = document.createXULElement("menulist");
let states = SitePermissions.getAvailableStates("autoplay-media");
for (let state of states) {
let m = menulist.appendItem(undefined, state);
document.l10n.setAttributes(m, this._getCapabilityString("autoplay-media", state));
}
menulist.value = SitePermissions.getDefault("autoplay-media");
menulist.addEventListener("select", () => {
SitePermissions.setDefault("autoplay-media", Number(menulist.value));
});
document.getElementById("setAutoplayPref").appendChild(menulist);
},
_sortPermissions(list, frag, column) {
let sortDirection;

Просмотреть файл

@ -32,6 +32,10 @@
</keyset>
<vbox class="contentPane">
<hbox align="center" id="setAutoplayPref" hidden="true">
<label data-l10n-id="permissions-autoplay-menu"/>
</hbox>
<description id="permissionsText" control="url"/>
<separator class="thin"/>
<hbox align="start">

Просмотреть файл

@ -46,9 +46,18 @@ permissions-button-ok =
.label = Save Changes
.accesskey = S
permissions-autoplay-menu = Default for all websites:
permissions-searchbox =
.placeholder = Search Website
permissions-capabilities-autoplay-allow =
.label = Allow Audio and Video
permissions-capabilities-autoplay-block =
.label = Block Audio
permissions-capabilities-autoplay-blockall =
.label = Block Audio and Video
permissions-capabilities-allow =
.label = Allow
permissions-capabilities-block =
@ -103,12 +112,12 @@ permissions-exceptions-addons-window =
.style = { permissions-window.style }
permissions-exceptions-addons-desc = You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
## Exceptions - Autoplay Media
## Site Permissions - Autoplay
permissions-exceptions-autoplay-media-window2 =
.title = Exceptions - Autoplay
permissions-site-autoplay-window =
.title = Settings - Autoplay
.style = { permissions-window.style }
permissions-exceptions-autoplay-media-desc2 = You can specify which websites are always or never allowed to autoplay media with sound. Type the address of the site you want to manage and then click Block or Allow.
permissions-site-autoplay-desc = You can manage the sites that do not follow your default autoplay settings here.
## Site Permissions - Notifications

Просмотреть файл

@ -887,7 +887,7 @@ content-blocking-cookies-label =
.label = Cookies
.accesskey = C
content-blocking-expand-section =
content-blocking-expand-section =
.tooltiptext = More information
# Cryptomining refers to using scripts on websites that can use a computers resources to mine cryptocurrency without a users knowledge.
@ -936,13 +936,11 @@ permissions-notification-pause =
.label = Pause notifications until { -brand-short-name } restarts
.accesskey = n
permissions-block-autoplay-media2 =
.label = Block websites from automatically playing sound
.accesskey = B
permissions-autoplay = Autoplay
permissions-block-autoplay-media-exceptions =
.label = Exceptions…
.accesskey = E
permissions-autoplay-settings =
.label = Settings…
.accesskey = t
permissions-block-popups =
.label = Block pop-up windows

Просмотреть файл

@ -28,6 +28,10 @@ state.multichoice.allow = Allow
state.multichoice.allowForSession = Allow for Session
state.multichoice.block = Block
state.multichoice.autoplayblock = Block Audio
state.multichoice.autoplayblockall = Block Audio and Video
state.multichoice.autoplayallow = Allow Audio and Video
permission.autoplay-media2.label = Autoplay sound
permission.cookie.label = Set Cookies
permission.desktop-notification3.label = Send Notifications

Просмотреть файл

@ -211,7 +211,7 @@ const GloballyBlockedPermissions = {
for (let id of Object.keys(timeStamps)) {
permissions.push({
id,
state: SitePermissions.BLOCK,
state: gPermissionObject[id].getDefault(),
scope: SitePermissions.SCOPE_GLOBAL,
});
}
@ -246,6 +246,7 @@ var SitePermissions = {
PROMPT: Services.perms.PROMPT_ACTION,
ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION,
PROMPT_HIDE: Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET,
AUTOPLAY_BLOCKED_ALL: Ci.nsIAutoplay.BLOCKED_ALL,
// Permission scopes.
SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
@ -484,6 +485,23 @@ var SitePermissions = {
return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN);
},
/**
* Set the default state of a particular permission.
*
* @param {string} permissionID
* The ID to set the default for.
*
* @param {string} state
* The state to set.
*/
setDefault(permissionID, state) {
if (permissionID in gPermissionObject &&
gPermissionObject[permissionID].setDefault) {
return gPermissionObject[permissionID].setDefault(state);
}
let key = "permissions.default." + permissionID;
return Services.prefs.setIntPref(key, state);
},
/**
* Returns the state and scope of a particular permission for a given URI.
*
@ -770,7 +788,14 @@ var SitePermissions = {
* @return {String|null} the localized label or null if an
* unknown state was passed.
*/
getMultichoiceStateLabel(state) {
getMultichoiceStateLabel(permissionID, state) {
// If the permission has custom logic for getting its default value,
// try that first.
if (permissionID in gPermissionObject &&
gPermissionObject[permissionID].getMultichoiceStateLabel) {
return gPermissionObject[permissionID].getMultichoiceStateLabel(state);
}
switch (state) {
case this.UNKNOWN:
case this.PROMPT:
@ -855,17 +880,42 @@ var gPermissionObject = {
"autoplay-media": {
exactHostMatch: true,
getDefault() {
let state = Services.prefs.getIntPref("media.autoplay.default",
let pref = Services.prefs.getIntPref("media.autoplay.default",
Ci.nsIAutoplay.BLOCKED);
if (state == Ci.nsIAutoplay.ALLOWED) {
if (pref == Ci.nsIAutoplay.ALLOWED) {
return SitePermissions.ALLOW;
} else if (state == Ci.nsIAutoplay.BLOCKED) {
return SitePermissions.BLOCK;
}
return SitePermissions.UNKNOWN;
if (pref == Ci.nsIAutoplay.BLOCKED_ALL) {
return SitePermissions.AUTOPLAY_BLOCKED_ALL;
}
return SitePermissions.BLOCK;
},
setDefault(value) {
let prefValue = Ci.nsIAutoplay.BLOCKED;
if (value == SitePermissions.ALLOW) {
prefValue = Ci.nsIAutoplay.ALLOWED;
} else if (value == SitePermissions.AUTOPLAY_BLOCKED_ALL) {
prefValue = Ci.nsIAutoplay.BLOCKED_ALL;
}
Services.prefs.setIntPref("media.autoplay.default", prefValue);
},
labelID: "autoplay-media2",
states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
states: [
SitePermissions.ALLOW,
SitePermissions.BLOCK,
SitePermissions.AUTOPLAY_BLOCKED_ALL,
],
getMultichoiceStateLabel(state) {
switch (state) {
case SitePermissions.AUTOPLAY_BLOCKED_ALL:
return gStringBundle.GetStringFromName("state.multichoice.autoplayblockall");
case SitePermissions.BLOCK:
return gStringBundle.GetStringFromName("state.multichoice.autoplayblock");
case SitePermissions.ALLOW:
return gStringBundle.GetStringFromName("state.multichoice.autoplayallow");
}
throw new Error(`Unkown state: ${state}`);
},
},
"image": {

Просмотреть файл

@ -82,6 +82,10 @@
list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
}
.autoplay-icon {
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-detailed.svg);
}
.midi-icon {
list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
}

Просмотреть файл

@ -21,6 +21,8 @@
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsPIDOMWindow.h"
#include "mozilla/Services.h"
#include "nsIPermissionManager.h"
mozilla::LazyLogModule gAutoplayPermissionLog("Autoplay");
@ -63,18 +65,18 @@ static bool IsActivelyCapturingOrHasAPermission(nsPIDOMWindowInner* aWindow) {
NS_LITERAL_CSTRING("screen")));
}
static bool IsSiteInAutoplayWhiteList(const Document* aDocument) {
return aDocument ? nsContentUtils::IsExactSitePermAllow(
aDocument->NodePrincipal(),
NS_LITERAL_CSTRING("autoplay-media"))
: false;
}
static uint32_t SiteAutoplayPerm(const Document* aDocument) {
nsIPrincipal* principal = aDocument->NodePrincipal();
if (!principal) {
return nsIPermissionManager::DENY_ACTION;
}
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
NS_ENSURE_TRUE(permMgr, nsIPermissionManager::DENY_ACTION);
static bool IsSiteInAutoplayBlackList(const Document* aDocument) {
return aDocument ? nsContentUtils::IsExactSitePermDeny(
aDocument->NodePrincipal(),
NS_LITERAL_CSTRING("autoplay-media"))
: false;
uint32_t perm;
nsresult rv = permMgr->TestExactPermissionFromPrincipal(principal, NS_LITERAL_CSTRING("autoplay-media"), &perm);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
return perm;
}
static bool IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow) {
@ -119,25 +121,24 @@ static bool IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow) {
static uint32_t DefaultAutoplayBehaviour() {
int prefValue =
Preferences::GetInt("media.autoplay.default", nsIAutoplay::ALLOWED);
if (prefValue < nsIAutoplay::ALLOWED || prefValue > nsIAutoplay::BLOCKED) {
// Invalid pref values are just converted to BLOCKED.
return nsIAutoplay::BLOCKED;
if (prefValue == nsIAutoplay::ALLOWED) {
return nsIAutoplay::ALLOWED;
}
return prefValue;
if (prefValue == nsIAutoplay::BLOCKED_ALL) {
return nsIAutoplay::BLOCKED_ALL;
}
return nsIAutoplay::BLOCKED;
}
static bool IsMediaElementAllowedToPlay(const HTMLMediaElement& aElement) {
const bool isAllowedMuted =
Preferences::GetBool("media.autoplay.allow-muted", true);
if ((aElement.Volume() == 0.0 || aElement.Muted()) && isAllowedMuted) {
AUTOPLAY_LOG("Allow muted media %p to autoplay.", &aElement);
static bool IsMediaElementInaudible(const HTMLMediaElement& aElement) {
if (aElement.Volume() == 0.0 || aElement.Muted()) {
AUTOPLAY_LOG("Media %p is muted.", &aElement);
return true;
}
if (!aElement.HasAudio() &&
aElement.ReadyState() >= HTMLMediaElement_Binding::HAVE_METADATA &&
isAllowedMuted) {
AUTOPLAY_LOG("Allow media %p without audio track to autoplay", &aElement);
aElement.ReadyState() >= HTMLMediaElement_Binding::HAVE_METADATA) {
AUTOPLAY_LOG("Media %p has no audio track", &aElement);
return true;
}
@ -153,14 +154,13 @@ static bool IsAudioContextAllowedToPlay(const AudioContext& aContext) {
static bool IsEnableBlockingWebAudioByUserGesturePolicy() {
return DefaultAutoplayBehaviour() != nsIAutoplay::ALLOWED &&
Preferences::GetBool("media.autoplay.block-webaudio", false) &&
Preferences::GetBool("media.autoplay.enabled.user-gestures-needed",
false);
StaticPrefs::MediaAutoplayUserGesturesNeeded();
}
/* static */
bool AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(
const HTMLMediaElement& aElement) {
return IsMediaElementAllowedToPlay(aElement) ||
return IsMediaElementInaudible(aElement) ||
IsWindowAllowedToPlay(aElement.OwnerDoc()->GetInnerWindow());
}
@ -170,44 +170,54 @@ bool AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(
return IsAudioContextAllowedToPlay(aContext);
}
static bool IsAllowedToPlayInternal(const HTMLMediaElement& aElement) {
/**
* The autoplay checking has 4 different phases,
* 1. check whether media element itself meets the autoplay condition
* 2. check whethr the site is in the autoplay whitelist
* 3. check global autoplay setting and check wether the site is in the
* autoplay blacklist.
* 4. check whether media is allowed under current blocking model
* (click-to-play or user-gesture-activation)
*/
if (IsMediaElementAllowedToPlay(aElement)) {
return true;
}
Document* approver = ApproverDocOf(*aElement.OwnerDoc());
if (IsSiteInAutoplayWhiteList(approver)) {
AUTOPLAY_LOG(
"Allow autoplay as document has permanent autoplay permission.");
return true;
}
if (DefaultAutoplayBehaviour() == nsIAutoplay::ALLOWED &&
!(IsSiteInAutoplayBlackList(approver) &&
StaticPrefs::MediaAutoplayBlackListOverrideDefault())) {
AUTOPLAY_LOG(
"Allow autoplay as global autoplay setting is allowing autoplay by "
"default.");
return true;
}
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();
static bool IsAllowedToPlayByBlockingModel(const HTMLMediaElement& aElement) {
if (!StaticPrefs::MediaAutoplayUserGesturesNeeded()) {
// If element is blessed, it would always be allowed to play().
return aElement.IsBlessed() || EventStateManager::IsHandlingUserInput();
}
return IsWindowAllowedToPlay(aElement.OwnerDoc()->GetInnerWindow());
}
static bool IsAllowedToPlayInternal(const HTMLMediaElement& aElement) {
Document* approver = ApproverDocOf(*aElement.OwnerDoc());
bool isInaudible = IsMediaElementInaudible(aElement);
bool isUsingAutoplayModel = IsAllowedToPlayByBlockingModel(aElement);
uint32_t defaultBehaviour = DefaultAutoplayBehaviour();
uint32_t sitePermission = SiteAutoplayPerm(approver);
AUTOPLAY_LOG("IsAllowedToPlayInternal, isInaudible=%d,"
"isUsingAutoplayModel=%d, sitePermission=%d, defaultBehaviour=%d",
isInaudible, isUsingAutoplayModel, sitePermission, defaultBehaviour);
// For site permissions we store permissionManager values except
// for BLOCKED_ALL, for the default pref values we store
// nsIAutoplay values.
if (sitePermission == nsIPermissionManager::ALLOW_ACTION) {
return true;
}
if (sitePermission == nsIPermissionManager::DENY_ACTION) {
return isInaudible || isUsingAutoplayModel;
}
if (sitePermission == nsIAutoplay::BLOCKED_ALL) {
return isUsingAutoplayModel;
}
if (defaultBehaviour == nsIAutoplay::ALLOWED) {
return true;
}
if (defaultBehaviour == nsIAutoplay::BLOCKED) {
return isInaudible || isUsingAutoplayModel;
}
MOZ_ASSERT(defaultBehaviour == nsIAutoplay::BLOCKED_ALL);
return isUsingAutoplayModel;
}
/* static */
bool AutoplayPolicy::IsAllowedToPlay(const HTMLMediaElement& aElement) {
const bool result = IsAllowedToPlayInternal(aElement);
@ -235,14 +245,17 @@ bool AutoplayPolicy::IsAllowedToPlay(const AudioContext& aContext) {
Document* approver = aContext.GetParentObject()
? ApproverDocOf(*(window->GetExtantDoc()))
: nullptr;
if (IsSiteInAutoplayWhiteList(approver)) {
uint32_t sitePermission = SiteAutoplayPerm(approver);
if (sitePermission == nsIPermissionManager::ALLOW_ACTION) {
AUTOPLAY_LOG(
"Allow autoplay as document has permanent autoplay permission.");
return true;
}
if (DefaultAutoplayBehaviour() == nsIAutoplay::ALLOWED &&
!IsSiteInAutoplayBlackList(approver)) {
sitePermission != nsIPermissionManager::DENY_ACTION &&
sitePermission != nsIAutoplay::BLOCKED_ALL) {
AUTOPLAY_LOG(
"Allow autoplay as global autoplay setting is allowing autoplay by "
"default.");
@ -263,7 +276,7 @@ DocumentAutoplayPolicy AutoplayPolicy::IsAllowedToPlay(
return DocumentAutoplayPolicy::Allowed;
}
if (StaticPrefs::MediaAutoplayAllowMuted()) {
if (DefaultAutoplayBehaviour() == nsIAutoplay::BLOCKED) {
return DocumentAutoplayPolicy::Allowed_muted;
}

Просмотреть файл

@ -13,4 +13,5 @@ interface nsIAutoplay : nsISupports
*/
const uint32_t ALLOWED = 0;
const uint32_t BLOCKED = 1;
const uint32_t BLOCKED_ALL = 5;
};

Просмотреть файл

@ -4714,6 +4714,13 @@ VARCACHE_PREF(
// These prefs use camel case instead of snake case for the getter because one
// reviewer had an unshakeable preference for that. Who could that be?
VARCACHE_PREF(
Live,
"media.autoplay.enabled.user-gestures-needed",
MediaAutoplayUserGesturesNeeded,
bool, false
)
VARCACHE_PREF(
Live,
"media.autoplay.allow-muted",

Просмотреть файл

@ -545,9 +545,6 @@ pref("media.autoplay.default", 0);
// By default, don't block WebAudio from playing automatically.
pref("media.autoplay.block-webaudio", false);
// By default, don't block muted media from playing automatically.
pref("media.autoplay.allow-muted", true);
// By default, don't block the media from extension background script.
pref("media.autoplay.allow-extension-background-pages", true);

Просмотреть файл

@ -6,13 +6,15 @@
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_empty.html";
function setupTestPreferences(isAllowedAutoplay, isAllowedMuted) {
const autoplayDefault = isAllowedAutoplay ?
SpecialPowers.Ci.nsIAutoplay.ALLOWED : SpecialPowers.Ci.nsIAutoplay.BLOCKED;
let autoplayDefault = SpecialPowers.Ci.nsIAutoplay.ALLOWED;
if (!isAllowedAutoplay) {
autoplayDefault = isAllowedMuted ? SpecialPowers.Ci.nsIAutoplay.BLOCKED
: SpecialPowers.Ci.nsIAutoplay.BLOCKED_ALL;
}
return SpecialPowers.pushPrefEnv({"set": [
["dom.media.autoplay.autoplay-policy-api", true],
["media.autoplay.default", autoplayDefault],
["media.autoplay.enabled.user-gestures-needed", true],
["media.autoplay.allow-muted", isAllowedMuted],
]});
}