diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js index a898b6c4aeb1..e5837733f9f3 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -4763,17 +4763,14 @@ window._gBrowser = { } }); - this.addEventListener("GloballyAutoplayBlocked", (event) => { + this.addEventListener("AudibleAutoplayMediaOccurred", (event) => { let browser = event.originalTarget; let tab = this.getTabForBrowser(browser); if (!tab) { return; } - SitePermissions.set(event.detail.url, "autoplay-media", - SitePermissions.BLOCK, - SitePermissions.SCOPE_GLOBAL, - browser); + Services.obs.notifyObservers(tab, "AudibleAutoplayMediaOccurred"); }); }, diff --git a/browser/modules/PermissionUI.jsm b/browser/modules/PermissionUI.jsm index ff77f82a1ba3..21c09b3363bf 100644 --- a/browser/modules/PermissionUI.jsm +++ b/browser/modules/PermissionUI.jsm @@ -306,6 +306,19 @@ var PermissionPromptPrototype = { this.browser); if (state == SitePermissions.BLOCK) { + // If the request is blocked by a global setting then we record + // a flag that lasts for the duration of the current page load + // to notify the user that the permission has been blocked. + // Currently only applies to autoplay-media + if (state == SitePermissions.getDefault(this.permissionKey) && + SitePermissions.showGloballyBlocked(this.permissionKey)) { + SitePermissions.set(this.principal.URI, + this.permissionKey, + state, + SitePermissions.SCOPE_GLOBAL, + this.browser); + } + this.cancel(); return; } @@ -327,6 +340,8 @@ var PermissionPromptPrototype = { this.browser); if (state == SitePermissions.BLOCK) { + // TODO: Add support for showGloballyBlocked + this.cancel(); return; } diff --git a/browser/modules/SitePermissions.jsm b/browser/modules/SitePermissions.jsm index 582566001dba..3e72127843c6 100644 --- a/browser/modules/SitePermissions.jsm +++ b/browser/modules/SitePermissions.jsm @@ -434,6 +434,23 @@ var SitePermissions = { return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN); }, + /** + * Return whether the browser should notify the user if a permission was + * globally blocked due to a preference. + * + * @param {string} permissionID + * The ID to get the state for. + * + * @return boolean Whether to show notification for globally blocked permissions. + */ + showGloballyBlocked(permissionID) { + if (permissionID in gPermissionObject && + gPermissionObject[permissionID].showGloballyBlocked) + return gPermissionObject[permissionID].showGloballyBlocked; + + return false; + }, + /* * Return whether SitePermissions is permitted to store a TEMPORARY ALLOW * state for a particular permission. @@ -746,6 +763,7 @@ var gPermissionObject = { "autoplay-media": { exactHostMatch: true, + showGloballyBlocked: true, permitTemporaryAllow: true, notifyWhenTemporaryPermissionChanged: true, getDefault() { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 71004cb3210e..9b0da7c0e9f4 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -11750,28 +11750,18 @@ void nsIDocument::NotifyUserGestureActivation() { } } -void nsIDocument::MaybeNotifyAutoplayBlocked() { - nsIDocument* topLevelDoc = GetTopLevelContentDocument(); - if (!topLevelDoc || - !nsContentUtils::IsExactSitePermDeny(topLevelDoc->NodePrincipal(), - "autoplay-media")) { - return; - } - - // This event is used to notify front-end side that we've blocked autoplay - // permanantly, so front-end side should show blocking icon as well. - RefPtr asyncDispatcher = new AsyncEventDispatcher( - topLevelDoc, NS_LITERAL_STRING("GloballyAutoplayBlocked"), - CanBubble::eYes, ChromeOnlyDispatch::eYes); - asyncDispatcher->PostDOMEvent(); -} - void nsIDocument::SetDocTreeHadAudibleMedia() { nsIDocument* topLevelDoc = GetTopLevelContentDocument(); if (!topLevelDoc) { return; } + if (!topLevelDoc->mDocTreeHadAudibleMedia) { + RefPtr asyncDispatcher = new AsyncEventDispatcher( + topLevelDoc, NS_LITERAL_STRING("AudibleAutoplayMediaOccurred"), + CanBubble::eYes, ChromeOnlyDispatch::eYes); + asyncDispatcher->PostDOMEvent(); + } topLevelDoc->mDocTreeHadAudibleMedia = true; } diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 595c560dd8f5..29c4ecc9666b 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -3390,10 +3390,6 @@ class nsIDocument : public nsINode, void ReportShadowDOMUsage(); - // When the doc is blocked permanantly, we would dispatch event to notify - // front-end side to show blocking icon. - void MaybeNotifyAutoplayBlocked(); - // Sets flags for media autoplay telemetry. void SetDocTreeHadAudibleMedia(); void SetDocTreeHadPlayRevoked(); diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 0e070d7b8e91..93b8eabc422d 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3697,7 +3697,6 @@ void HTMLMediaElement::DispatchEventsWhenPlayWasNotAllowed() { ChromeOnlyDispatch::eYes); asyncDispatcher->PostDOMEvent(); #endif - OwnerDoc()->MaybeNotifyAutoplayBlocked(); } void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) { diff --git a/toolkit/actors/AutoplayChild.jsm b/toolkit/actors/AudibleAutoplayChild.jsm similarity index 68% rename from toolkit/actors/AutoplayChild.jsm rename to toolkit/actors/AudibleAutoplayChild.jsm index 93fd4eeda1ba..a6fffddc5a53 100644 --- a/toolkit/actors/AutoplayChild.jsm +++ b/toolkit/actors/AudibleAutoplayChild.jsm @@ -4,12 +4,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -var EXPORTED_SYMBOLS = ["AutoplayChild"]; +var EXPORTED_SYMBOLS = ["AudibleAutoplayChild"]; ChromeUtils.import("resource://gre/modules/ActorChild.jsm"); -class AutoplayChild extends ActorChild { +class AudibleAutoplayChild extends ActorChild { handleEvent(event) { - this.mm.sendAsyncMessage("GloballyAutoplayBlocked"); + this.mm.sendAsyncMessage("AudibleAutoplayMediaOccurred"); } } diff --git a/toolkit/actors/moz.build b/toolkit/actors/moz.build index 08d6554ad8d6..095ff3c1055e 100644 --- a/toolkit/actors/moz.build +++ b/toolkit/actors/moz.build @@ -11,8 +11,8 @@ with Files('Finder*.jsm'): BUG_COMPONENT = ('Toolkit', 'Find Toolbar') FINAL_TARGET_FILES.actors += [ + 'AudibleAutoplayChild.jsm', 'AudioPlaybackChild.jsm', - 'AutoplayChild.jsm', 'BrowserChild.jsm', 'ControllersChild.jsm', 'DateTimePickerChild.jsm', diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini index d1029ea9f7a3..cb7fc2351b64 100644 --- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -30,6 +30,7 @@ support-files = tags = audiochannel [browser_audioCompeting_onlyForActiveAgent.js] tags = audiochannel +[browser_autoplay_audibleMediaOccurred.js] [browser_autoplay_policy_iframe_hierarchy.js] support-files = file_autoplay_three_layers_frame1.html diff --git a/toolkit/content/tests/browser/browser_autoplay_audibleMediaOccurred.js b/toolkit/content/tests/browser/browser_autoplay_audibleMediaOccurred.js new file mode 100644 index 000000000000..d29cddfcb901 --- /dev/null +++ b/toolkit/content/tests/browser/browser_autoplay_audibleMediaOccurred.js @@ -0,0 +1,27 @@ +/** + * This test is used to test whether the topic 'AudibleAutoplayMediaOccurred' + * is sent correctly when the autoplay audible media tries to start. + */ +"use strict"; + +const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlayback.html"; + +add_task(async function testAudibleAutoplayMedia() { + info("- open new tab -"); + let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + let browser = tab.linkedBrowser; + + // start observing the topic before loading the page to ensure we can get it. + let audibleAutoplayOccurred = TestUtils.topicObserved("AudibleAutoplayMediaOccurred"); + browser.loadURI(PAGE, { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + + await BrowserTestUtils.browserLoaded(browser); + await audibleAutoplayOccurred; + ok(true, "Got the topic 'AudibleAutoplayMediaOccurred'."); + + info("- remove tab -"); + BrowserTestUtils.removeTab(tab); +}); diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index e3db4011756f..268fceaef5a0 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -762,14 +762,11 @@ - + @@ -1111,7 +1108,7 @@ this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStart", this); this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStop", this); this.messageManager.addMessageListener("UnselectedTabHover:Toggle", this); - this.messageManager.addMessageListener("GloballyAutoplayBlocked", this); + this.messageManager.addMessageListener("AudibleAutoplayMediaOccurred", this); if (this.hasAttribute("selectmenulist")) { this.messageManager.addMessageListener("Forms:ShowDropDown", this); @@ -1253,8 +1250,8 @@ ++this._unselectedTabHoverMessageListenerCount > 0 : --this._unselectedTabHoverMessageListenerCount == 0; break; - case "GloballyAutoplayBlocked": - this.notifyGloballyAutoplayBlocked(); + case "AudibleAutoplayMediaOccurred": + this.notifyAudibleAutoplayMediaOccurred(); break; case "Forms:ShowDropDown": { if (!this._selectParentHelper) { diff --git a/toolkit/modules/ActorManagerParent.jsm b/toolkit/modules/ActorManagerParent.jsm index 23aa7ce5ccb3..b51fe9c1f82b 100644 --- a/toolkit/modules/ActorManagerParent.jsm +++ b/toolkit/modules/ActorManagerParent.jsm @@ -101,6 +101,15 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); const {DefaultMap} = ExtensionUtils; let ACTORS = { + AudibleAutoplay: { + child: { + module: "resource://gre/actors/AudibleAutoplayChild.jsm", + events: { + "AudibleAutoplayMediaOccurred": {}, + }, + }, + }, + AudioPlayback: { child: { module: "resource://gre/actors/AudioPlaybackChild.jsm", @@ -114,15 +123,6 @@ let ACTORS = { }, }, - Autoplay: { - child: { - module: "resource://gre/actors/AutoplayChild.jsm", - events: { - "GloballyAutoplayBlocked": {}, - }, - }, - }, - Browser: { child: { module: "resource://gre/actors/BrowserChild.jsm",