Bug 1695615 - Updated permission panel for double-keyed WebRTC permissions. r=johannh

Differential Revision: https://phabricator.services.mozilla.com/D107071
This commit is contained in:
Paul Zuehlcke 2021-03-12 10:06:16 +00:00
Родитель f58f5ab06c
Коммит d94f9c0a90
9 изменённых файлов: 388 добавлений и 167 удалений

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

@ -372,6 +372,8 @@ var gPermissionPanel = {
this._permissionList
.querySelectorAll(permissionItemSelector)
.forEach(e => e.remove());
// Used by _createPermissionItem to build unique IDs.
this._permissionLabelIndex = 0;
let permissions = SitePermissions.getAllPermissionDetailsForBrowser(
gBrowser.selectedBrowser
@ -415,12 +417,14 @@ var gPermissionPanel = {
if (webrtcState[id]) {
let found = false;
for (let permission of permissions) {
if (permission.id != id) {
let [permId] = permission.id.split(
SitePermissions.PERM_KEY_DELIMITER
);
if (permId != id) {
continue;
}
found = true;
permission.sharingState = webrtcState[id];
break;
}
if (!found) {
// If the permission item we were looking for doesn't exist,
@ -461,9 +465,16 @@ var gPermissionPanel = {
if (permContainer) {
anchor.appendChild(permContainer);
}
} else if (["camera", "screen", "microphone"].includes(id)) {
item = this._createWebRTCPermissionItem(permission, id, key);
if (!item) {
continue;
}
anchor.appendChild(item);
} else {
item = this._createPermissionItem({
permission,
idNoSuffix: id,
isContainer: id == "geo" || id == "xr",
nowrapLabel: id == "3rdPartyStorage",
});
@ -524,9 +535,13 @@ var gPermissionPanel = {
showStateLabel = true,
idNoSuffix = permission.id,
nowrapLabel = false,
clearCallback = () => {},
}) {
let container = document.createXULElement("hbox");
container.setAttribute("class", "permission-popup-permission-item");
container.classList.add(
"permission-popup-permission-item",
`permission-popup-permission-item-${idNoSuffix}`
);
container.setAttribute("align", "center");
container.setAttribute("role", "group");
@ -552,7 +567,7 @@ var gPermissionPanel = {
let nameLabel = document.createXULElement("label");
nameLabel.setAttribute("flex", "1");
nameLabel.setAttribute("class", "permission-popup-permission-label");
let label = SitePermissions.getPermissionLabel(idNoSuffix);
let label = SitePermissions.getPermissionLabel(permission.id);
if (label === null) {
return null;
}
@ -563,7 +578,11 @@ var gPermissionPanel = {
} else {
nameLabel.textContent = label;
}
let nameLabelId = "permission-popup-permission-label-" + idNoSuffix;
// idNoSuffix is not unique for double-keyed permissions. Adding an index to
// ensure IDs are unique.
// permission.id is unique but may not be a valid HTML ID.
let nameLabelId = `permission-popup-permission-label-${idNoSuffix}-${this
._permissionLabelIndex++}`;
nameLabel.setAttribute("id", nameLabelId);
let isPolicyPermission = [
@ -612,7 +631,7 @@ var gPermissionPanel = {
menulist.addEventListener("command", () => {
SitePermissions.setForPrincipal(
gBrowser.contentPrincipal,
idNoSuffix,
permission.id,
menulist.selectedItem.value
);
});
@ -651,7 +670,12 @@ var gPermissionPanel = {
block.setAttribute("class", "permission-popup-permission-item-container");
if (permClearButton) {
let button = this._createPermissionClearButton(permission, block);
let button = this._createPermissionClearButton({
permission,
container: block,
idNoSuffix,
clearCallback,
});
container.appendChild(button);
}
@ -660,7 +684,12 @@ var gPermissionPanel = {
}
if (permClearButton) {
let button = this._createPermissionClearButton(permission, container);
let button = this._createPermissionClearButton({
permission,
container,
idNoSuffix,
clearCallback,
});
container.appendChild(button);
}
@ -671,7 +700,8 @@ var gPermissionPanel = {
let label = document.createXULElement("label");
label.setAttribute("flex", "1");
label.setAttribute("class", "permission-popup-permission-state-label");
let labelId = "permission-popup-permission-state-label-" + idNoSuffix;
let labelId = `permission-popup-permission-state-label-${idNoSuffix}-${this
._permissionLabelIndex++}`;
label.setAttribute("id", labelId);
let { state, scope } = aPermission;
// If the user did not permanently allow this device but it is currently
@ -698,11 +728,12 @@ var gPermissionPanel = {
}
},
_createPermissionClearButton(
aPermission,
_createPermissionClearButton({
permission,
container,
clearCallback = () => {}
) {
idNoSuffix = permission.id,
clearCallback = () => {},
}) {
let button = document.createXULElement("button");
button.setAttribute("class", "permission-popup-permission-remove-button");
let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
@ -710,42 +741,24 @@ var gPermissionPanel = {
button.addEventListener("command", () => {
let browser = gBrowser.selectedBrowser;
container.remove();
if (aPermission.sharingState) {
if (aPermission.id === "xr") {
let origins = browser.getDevicePermissionOrigins(aPermission.id);
for (let origin of origins) {
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
origin
);
this._removePermPersistentAllow(principal, aPermission.id);
}
origins.clear();
} else if (
["camera", "microphone", "screen"].includes(aPermission.id)
) {
let windowId = this._sharingState.webRTC.windowId;
if (aPermission.id == "screen") {
windowId = "screen:" + windowId;
} else {
// It's not possible to stop sharing one of camera/microphone
// without the other.
for (let id of ["camera", "microphone"]) {
if (this._sharingState.webRTC[id]) {
this._removePermPersistentAllow(gBrowser.contentPrincipal, id);
}
}
}
let bc = this._sharingState.webRTC.browsingContext;
bc.currentWindowGlobal
.getActor("WebRTC")
.sendAsyncMessage("webrtc:StopSharing", windowId);
webrtcUI.forgetActivePermissionsFromBrowser(gBrowser.selectedBrowser);
// For XR permissions we need to keep track of all origins which may have
// started XR sharing. This is necessary, because XR does not use
// permission delegation and permissions can be granted for sub-frames. We
// need to keep track of which origins we need to revoke the permission
// for.
if (permission.sharingState && idNoSuffix === "xr") {
let origins = browser.getDevicePermissionOrigins(idNoSuffix);
for (let origin of origins) {
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
origin
);
this._removePermPersistentAllow(principal, permission.id);
}
origins.clear();
}
SitePermissions.removeFromPrincipal(
gBrowser.contentPrincipal,
aPermission.id,
permission.id,
browser
);
@ -754,9 +767,9 @@ var gPermissionPanel = {
this._permissionPopupMainView
).descriptionHeightWorkaround();
if (aPermission.id === "geo") {
if (idNoSuffix === "geo") {
gBrowser.updateBrowserSharing(browser, { geo: false });
} else if (aPermission.id === "xr") {
} else if (idNoSuffix === "xr") {
gBrowser.updateBrowserSharing(browser, { xr: false });
}
@ -834,6 +847,52 @@ var gPermissionPanel = {
geoContainer.appendChild(indicator);
},
/**
* Create a permission item for a WebRTC permission. May return null if there
* already is a suitable permission item for this device type.
* @param {Object} permission - Permission object.
* @param {string} id - Permission ID without suffix.
* @param {string} [key] - Secondary permission key.
* @returns {xul:hbox|null} - Element for permission or null if permission
* should be skipped.
*/
_createWebRTCPermissionItem(permission, id, key) {
if (id != "camera" && id != "microphone" && id != "screen") {
throw new Error("Invalid permission id for WebRTC permission item.");
}
// Only show WebRTC device-specific ALLOW permissions. Since we only show
// one permission item per device type, we don't support showing mixed
// states where one devices is allowed and another one blocked.
if (key && permission.state != SitePermissions.ALLOW) {
return null;
}
// Check if there is already an item for this permission. Multiple
// permissions with the same id can be set, but with different keys.
let item = document.querySelector(
`.permission-popup-permission-item-${id}`
);
if (key) {
// We have a double keyed permission. If there is already an item it will
// have ownership of all permissions with this WebRTC permission id.
if (item) {
return null;
}
} else if (item) {
// If we have a single-key (not device specific) webRTC permission it
// overrides any existing (device specific) permission items.
item.remove();
}
return this._createPermissionItem({
permission,
idNoSuffix: id,
clearCallback: () => {
webrtcUI.clearPermissionsAndStopSharing([id], gBrowser.selectedTab);
},
});
},
_createProtocolHandlerPermissionItem(permission, key) {
let container = document.getElementById(
"permission-popup-open-protocol-handler-container"
@ -876,13 +935,17 @@ var gPermissionPanel = {
item.appendChild(text);
item.appendChild(stateLabel);
let button = this._createPermissionClearButton(permission, item, () => {
// When we're clearing the last open-protocol-handler permission, clean up
// the empty container.
// (<= 1 because the heading item is also a child of the container)
if (container.childElementCount <= 1) {
container.remove();
}
let button = this._createPermissionClearButton({
permission,
container: item,
clearCallback: () => {
// When we're clearing the last open-protocol-handler permission, clean up
// the empty container.
// (<= 1 because the heading item is also a child of the container)
if (container.childElementCount <= 1) {
container.remove();
}
},
});
item.appendChild(button);

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

@ -636,14 +636,31 @@ var gTests = [
},
{
desc: "Stop Sharing removes persistent permissions",
run: async function checkStopSharingRemovesPersistentPermissions() {
desc: "Stop Sharing removes permissions",
run: async function checkStopSharingRemovesPermissions() {
async function stopAndCheckPerm(aRequestAudio, aRequestVideo) {
let uri = gBrowser.selectedBrowser.documentURI;
// Initially set both permissions to 'allow'.
PermissionTestUtils.add(uri, "microphone", Services.perms.ALLOW_ACTION);
PermissionTestUtils.add(uri, "camera", Services.perms.ALLOW_ACTION);
// Also set device-specific temporary allows.
SitePermissions.setForPrincipal(
gBrowser.contentPrincipal,
"microphone^myDevice",
SitePermissions.ALLOW,
SitePermissions.SCOPE_TEMPORARY,
gBrowser.selectedBrowser,
10000000
);
SitePermissions.setForPrincipal(
gBrowser.contentPrincipal,
"camera^myDevice2",
SitePermissions.ALLOW,
SitePermissions.SCOPE_TEMPORARY,
gBrowser.selectedBrowser,
10000000
);
let indicator = promiseIndicatorWindow();
let observerPromise1 = expectObserverCalled("getUserMedia:request");
@ -660,49 +677,125 @@ var gTests = [
await observerPromise3;
await indicator;
await checkSharingUI({ video: aRequestVideo, audio: aRequestAudio });
await checkSharingUI(
{ video: aRequestVideo, audio: aRequestAudio },
undefined,
undefined,
{
video: { scope: SitePermissions.SCOPE_PERSISTENT },
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
}
);
await stopSharing(aRequestVideo ? "camera" : "microphone");
// Check that permissions have been removed as expected.
let audioPerm = PermissionTestUtils.testExactPermission(
uri,
"microphone"
let audioPerm = SitePermissions.getForPrincipal(
gBrowser.contentPrincipal,
"microphone",
gBrowser.selectedBrowser
);
let audioPermDevice = SitePermissions.getForPrincipal(
gBrowser.contentPrincipal,
"microphone^myDevice",
gBrowser.selectedBrowser
);
if (aRequestAudio) {
is(
Assert.deepEqual(
audioPerm,
Services.perms.UNKNOWN_ACTION,
{
state: SitePermissions.UNKNOWN,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"microphone permissions removed"
);
Assert.deepEqual(
audioPermDevice,
{
state: SitePermissions.UNKNOWN,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"microphone device-specific permissions removed"
);
} else {
is(
Assert.deepEqual(
audioPerm,
Services.perms.ALLOW_ACTION,
{
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"microphone permissions untouched"
);
Assert.deepEqual(
audioPermDevice,
{
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_TEMPORARY,
},
"microphone device-specific permissions untouched"
);
}
let videoPerm = PermissionTestUtils.testExactPermission(uri, "camera");
let videoPerm = SitePermissions.getForPrincipal(
gBrowser.contentPrincipal,
"camera",
gBrowser.selectedBrowser
);
let videoPermDevice = SitePermissions.getForPrincipal(
gBrowser.contentPrincipal,
"camera^myDevice2",
gBrowser.selectedBrowser
);
if (aRequestVideo) {
is(
Assert.deepEqual(
videoPerm,
Services.perms.UNKNOWN_ACTION,
{
state: SitePermissions.UNKNOWN,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"camera permissions removed"
);
Assert.deepEqual(
videoPermDevice,
{
state: SitePermissions.UNKNOWN,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"camera device-specific permissions removed"
);
} else {
is(
Assert.deepEqual(
videoPerm,
Services.perms.ALLOW_ACTION,
{
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_PERSISTENT,
},
"camera permissions untouched"
);
Assert.deepEqual(
videoPermDevice,
{
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_TEMPORARY,
},
"camera device-specific permissions untouched"
);
}
// Cleanup.
await closeStream(true);
PermissionTestUtils.remove(uri, "camera");
PermissionTestUtils.remove(uri, "microphone");
SitePermissions.removeFromPrincipal(
gBrowser.contentPrincipal,
"camera",
gBrowser.selectedBrowser
);
SitePermissions.removeFromPrincipal(
gBrowser.contentPrincipal,
"microphone",
gBrowser.selectedBrowser
);
}
info("request audio+video, stop sharing resets both");

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

@ -98,7 +98,9 @@ var gTests = [
await Promise.all(promises);
await indicator;
await checkSharingUI({ video: true });
await checkSharingUI({ video: true }, undefined, undefined, {
video: { scope: SitePermissions.SCOPE_PERSISTENT },
});
await closeStream();
PermissionTestUtils.remove(browser.currentURI, "camera");
@ -193,7 +195,9 @@ var gTests = [
await Promise.all(promises);
await indicator;
await checkSharingUI({ audio: true });
await checkSharingUI({ audio: true }, undefined, undefined, {
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
});
await closeStream();
PermissionTestUtils.remove(browser.currentURI, "microphone");

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

@ -114,7 +114,10 @@ var gTests = [
);
await indicator;
await checkSharingUI({ video: true, audio: true });
await checkSharingUI({ video: true, audio: true }, undefined, undefined, {
video: { scope: SitePermissions.SCOPE_PERSISTENT },
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
});
let uri = Services.io.newURI("https://example.com/");
is(

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

@ -72,7 +72,10 @@ async function promptNoDelegate(aThirdPartyOrgin, audio = true, video = true) {
`expected camera to be ${video ? "" : "not"} shared`
);
await indicator;
await checkSharingUI({ audio, video });
await checkSharingUI({ audio, video }, undefined, undefined, {
video: { scope: SitePermissions.SCOPE_PERSISTENT },
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
});
// Cleanup.
await closeStream(false, "frame4");
@ -144,7 +147,9 @@ async function promptNoDelegateScreenSharing(aThirdPartyOrgin) {
);
await indicator;
await checkSharingUI({ screen: "Screen" });
await checkSharingUI({ screen: "Screen" }, undefined, undefined, {
screen: { scope: SitePermissions.SCOPE_PERSISTENT },
});
await closeStream(false, "frame4");
PermissionTestUtils.remove(uri, "screen");
@ -728,7 +733,10 @@ var gTests = [
"expected camera and microphone to be shared"
);
await indicator;
await checkSharingUI({ audio: true, video: true });
await checkSharingUI({ audio: true, video: true }, undefined, undefined, {
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
video: { scope: SitePermissions.SCOPE_PERSISTENT },
});
await closeStream(true);
// Check that we get a prompt.

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

@ -131,7 +131,10 @@ add_task(async function test_keep_permissions() {
await stopSharingPromise;
// Ensure that we're still sharing the other streams.
await checkSharingUI({ audio: true, video: true });
await checkSharingUI({ audio: true, video: true }, undefined, undefined, {
audio: { scope: SitePermissions.SCOPE_PERSISTENT },
video: { scope: SitePermissions.SCOPE_PERSISTENT },
});
// Ensure that the "display-share" section of the indicator is now hidden
Assert.ok(

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

@ -827,12 +827,20 @@ function checkDeviceSelectors(aAudio, aVideo, aScreen, aWindow = window) {
}
}
// aExpected is for the current tab,
// aExpectedGlobal is for all tabs.
/**
* Tests the siteIdentity icons, the permission panel and the global indicator
* UI state.
* @param {Object} aExpected - Expected state for the current tab.
* @param {window} [aWin] - Top level chrome window to test state of.
* @param {Object} [aExpectedGlobal] - Expected state for all tabs.
* @param {Object} [aExpectedPerm] - Expected permission states keyed by device
* type.
*/
async function checkSharingUI(
aExpected,
aWin = window,
aExpectedGlobal = null
aExpectedGlobal = null,
aExpectedPerm = null
) {
function isPaused(streamState) {
if (typeof streamState == "string") {
@ -887,29 +895,58 @@ async function checkSharingUI(
return idToConvert;
};
let expected = aExpected[convertId(id)];
// Extract the expected permission for the device type.
// Defaults to temporary allow.
let { state, scope } = aExpectedPerm?.[convertId(id)] || {};
if (state == null) {
state = SitePermissions.ALLOW;
}
if (scope == null) {
scope = SitePermissions.SCOPE_TEMPORARY;
}
is(
!!aWin.gPermissionPanel._sharingState.webRTC[id],
!!expected,
"sharing state for " + id + " as expected"
);
let item = permissions.querySelectorAll(
".permission-popup-permission-item-" + id
);
let stateLabel = item?.[0]?.querySelector(
".permission-popup-permission-state-label"
);
let icon = permissions.querySelectorAll(
".permission-popup-permission-icon." + id + "-icon"
);
if (expected) {
is(item.length, 1, "should show " + id + " item in permission panel");
is(
stateLabel?.textContent,
SitePermissions.getCurrentStateLabel(state, id, scope),
"should show correct item label for " + id
);
is(icon.length, 1, "should show " + id + " icon in permission panel");
is(
icon[0].classList.contains("in-use"),
expected && !isPaused(expected),
"icon should have the in-use class, unless paused"
);
} else if (!icon.length) {
} else if (!icon.length && !item.length && !stateLabel) {
ok(true, "should not show " + id + " item in the permission panel");
ok(true, "should not show " + id + " icon in the permission panel");
ok(
true,
"should not show " + id + " state label in the permission panel"
);
} else {
// This will happen if there are persistent permissions set.
ok(
!icon[0].classList.contains("in-use"),
"if shown, the " + id + " icon should not have the in-use class"
);
is(item.length, 1, "should not show more than 1 " + id + " item");
is(icon.length, 1, "should not show more than 1 " + id + " icon");
}
}

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

@ -514,7 +514,7 @@ var webrtcUI = {
* select the browser.
*
* For camera and microphone streams, this will also revoke any associated
* persistent permissions from SitePermissions.
* permissions from SitePermissions.
*
* @param {Array<Object>} activeStreams - An array of streams obtained via webrtcUI.getActiveStreams.
* @param {boolean} stopCameras - True to stop the camera streams (defaults to true)
@ -533,8 +533,16 @@ var webrtcUI = {
return;
}
let mostRecentStream = activeStreams[activeStreams.length - 1];
let { browser: browserToSelect } = mostRecentStream;
let ids = [];
if (stopCameras) {
ids.push("camera");
}
if (stopMics) {
ids.push("microphone");
}
if (stopScreens || stopWindows) {
ids.push("screen");
}
for (let stream of activeStreams) {
let { browser } = stream;
@ -551,88 +559,13 @@ var webrtcUI = {
continue;
}
let permissions = SitePermissions.getAllPermissionDetailsForBrowser(
browser
);
let webrtcState = tab._sharingState.webRTC;
let clearRequested = {
camera: stopCameras,
microphone: stopMics,
screen: stopScreens || stopWindows,
};
for (let id of ["camera", "microphone", "screen"]) {
if (webrtcState[id] && clearRequested[id]) {
let found = false;
for (let permission of permissions) {
if (permission.id != id) {
continue;
}
found = true;
permission.sharingState = webrtcState[id];
break;
}
if (!found) {
// If the permission item we were looking for doesn't exist,
// the user has temporarily allowed sharing and we need to add
// an item in the permissions array to reflect this.
permissions.push({
id,
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_REQUEST,
sharingState: webrtcState[id],
});
}
}
}
for (let permission of permissions) {
if (clearRequested[permission.id]) {
let windowId = tab._sharingState.webRTC.windowId;
if (permission.id == "screen") {
windowId = `screen:${webrtcState.windowId}`;
} else if (
permission.id == "camera" ||
permission.id == "microphone"
) {
// It's not possible to stop sharing one of camera/microphone
// without the other.
for (let id of ["camera", "microphone"]) {
if (webrtcState[id]) {
let perm = SitePermissions.getForPrincipal(
gBrowser.contentPrincipal,
id
);
if (
perm.state == SitePermissions.ALLOW &&
perm.scope == SitePermissions.SCOPE_PERSISTENT
) {
SitePermissions.removeFromPrincipal(
gBrowser.contentPrincipal,
id
);
}
}
}
}
let bc = webrtcState.browsingContext;
bc.currentWindowGlobal
.getActor("WebRTC")
.sendAsyncMessage("webrtc:StopSharing", windowId);
webrtcUI.forgetActivePermissionsFromBrowser(browser);
SitePermissions.removeFromPrincipal(
browser.contentPrincipal,
permission.id,
browser
);
}
}
this.clearPermissionsAndStopSharing(ids, tab);
}
// Switch to the newest stream's browser.
let mostRecentStream = activeStreams[activeStreams.length - 1];
let { browser: browserToSelect } = mostRecentStream;
let window = browserToSelect.ownerGlobal;
let gBrowser = browserToSelect.getTabBrowser();
let tab = gBrowser.getTabForBrowser(browserToSelect);
@ -640,6 +573,83 @@ var webrtcUI = {
gBrowser.selectedTab = tab;
},
/**
* Clears permissions and stops sharing (if active) for a list of device types
* and a specific tab.
* @param {("camera"|"microphone"|"screen")[]} types - Device types to stop
* and clear permissions for.
* @param tab - Tab of the devices to stop and clear permissions.
*/
clearPermissionsAndStopSharing(types, tab) {
let invalidTypes = types.filter(
type => type != "camera" && type != "screen" && type != "microphone"
);
if (invalidTypes.length) {
throw new Error(`Invalid device types ${invalidTypes.join(",")}`);
}
let browser = tab.linkedBrowser;
let sharingState = tab._sharingState?.webRTC;
// If we clear a WebRTC permission we need to remove all permissions of
// the same type across device ids. We also need to stop active WebRTC
// devices related to the permission.
let perms = SitePermissions.getAllForBrowser(browser);
let sharingCameraAndMic =
sharingState?.camera &&
sharingState?.microphone &&
(types.includes("camera") || types.includes("microphone"));
perms
.filter(perm => {
let [permId] = perm.id.split(SitePermissions.PERM_KEY_DELIMITER);
// It's not possible to stop sharing one of camera/microphone
// without the other.
if (
sharingCameraAndMic &&
(permId == "camera" || permId == "microphone")
) {
return true;
}
return types.includes(permId);
})
.forEach(perm => {
SitePermissions.removeFromPrincipal(
browser.contentPrincipal,
perm.id,
browser
);
});
if (!sharingState?.windowId) {
return;
}
// If the device of the permission we're clearing is currently active,
// tell the WebRTC implementation to stop sharing it.
let { windowId } = sharingState;
let windowIds = [];
if (types.includes("screen") && sharingState.screen) {
windowIds.push(`screen:${windowId}`);
}
if (
(types.includes("camera") && sharingState.camera) ||
(types.includes("microphone") && sharingState.microphone)
) {
windowIds.push(windowId);
}
if (!windowIds.length) {
return;
}
let actor = sharingState.browsingContext.currentWindowGlobal.getActor(
"WebRTC"
);
windowIds.forEach(id => actor.sendAsyncMessage("webrtc:StopSharing", id));
webrtcUI.forgetActivePermissionsFromBrowser(browser);
},
updateIndicators(aTopBrowsingContext) {
let tabState = this.getCombinedStateForBrowser(aTopBrowsingContext);

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

@ -228,8 +228,8 @@ async function testDoorHanger(
);
gPermissionPanel._identityPermissionBox.click();
await permissionPopupPromise;
let permissionItem = document.getElementById(
`permission-popup-permission-label-3rdPartyStorage^https://tracking.example.org`
let permissionItem = document.querySelector(
".permission-popup-permission-item-3rdPartyStorage"
);
ok(permissionItem, "Permission item exists");
ok(