зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f58f5ab06c
Коммит
d94f9c0a90
|
@ -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(
|
||||
|
|
Загрузка…
Ссылка в новой задаче