Bug 630614 - Show an indicator when geolocation is in use. r=johannh

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Paul Zuehlcke 2019-07-29 08:37:55 +00:00
Родитель 741e60d13d
Коммит 15afab0390
14 изменённых файлов: 597 добавлений и 58 удалений

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

@ -256,6 +256,18 @@ var gIdentityHandler = {
));
},
get _geoSharingIcon() {
delete this._geoSharingIcon;
return (this._geoSharingIcon = document.getElementById("geo-sharing-icon"));
},
get _webRTCSharingIcon() {
delete this._webRTCSharingIcon;
return (this._webRTCSharingIcon = document.getElementById(
"webrtc-sharing-icon"
));
},
get _insecureConnectionIconEnabled() {
delete this._insecureConnectionIconEnabled;
XPCOMUtils.defineLazyPreferenceGetter(
@ -513,12 +525,27 @@ var gIdentityHandler = {
let tab = gBrowser.selectedTab;
this._sharingState = tab._sharingState;
this._identityBox.removeAttribute("paused");
this._identityBox.removeAttribute("sharing");
if (this._sharingState && this._sharingState.sharing) {
this._identityBox.setAttribute("sharing", this._sharingState.sharing);
if (this._sharingState.paused) {
this._identityBox.setAttribute("paused", "true");
this._webRTCSharingIcon.removeAttribute("paused");
this._webRTCSharingIcon.removeAttribute("sharing");
this._geoSharingIcon.removeAttribute("sharing");
if (this._sharingState) {
if (
this._sharingState &&
this._sharingState.webRTC &&
this._sharingState.webRTC.sharing
) {
this._webRTCSharingIcon.setAttribute(
"sharing",
this._sharingState.webRTC.sharing
);
if (this._sharingState.webRTC.paused) {
this._webRTCSharingIcon.setAttribute("paused", "true");
}
}
if (this._sharingState.geo) {
this._geoSharingIcon.setAttribute("sharing", this._sharingState.geo);
}
}
@ -1183,18 +1210,33 @@ var gIdentityHandler = {
gBrowser.selectedBrowser
);
if (this._sharingState) {
if (this._sharingState && this._sharingState.geo) {
let geoPermission = permissions.find(perm => perm.id === "geo");
if (geoPermission) {
geoPermission.sharingState = true;
} else {
permissions.push({
id: "geo",
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_REQUEST,
sharingState: true,
});
}
}
if (this._sharingState && this._sharingState.webRTC) {
let webrtcState = this._sharingState.webRTC;
// If WebRTC device or screen permissions are in use, we need to find
// the associated permission item to set the sharingState field.
for (let id of ["camera", "microphone", "screen"]) {
if (this._sharingState[id]) {
if (webrtcState[id]) {
let found = false;
for (let permission of permissions) {
if (permission.id != id) {
continue;
}
found = true;
permission.sharingState = this._sharingState[id];
permission.sharingState = webrtcState[id];
break;
}
if (!found) {
@ -1205,7 +1247,7 @@ var gIdentityHandler = {
id,
state: SitePermissions.ALLOW,
scope: SitePermissions.SCOPE_REQUEST,
sharingState: this._sharingState[id],
sharingState: webrtcState[id],
});
}
}
@ -1232,6 +1274,11 @@ var gIdentityHandler = {
) {
this._createBlockedPopupIndicator();
hasBlockedPopupIndicator = true;
} else if (
permission.id == "geo" &&
permission.state === SitePermissions.ALLOW
) {
this._createGeoLocationLastAccessIndicator();
}
}
@ -1288,9 +1335,7 @@ var gIdentityHandler = {
// Synchronize control center and identity block blinking animations.
window
.promiseDocumentFlushed(() => {
let sharingIconBlink = document
.getElementById("sharing-icon")
.getAnimations()[0];
let sharingIconBlink = this._webRTCSharingIcon.getAnimations()[0];
let imgBlink = img.getAnimations()[0];
return [sharingIconBlink, imgBlink];
})
@ -1403,6 +1448,24 @@ var gIdentityHandler = {
return container;
}
if (aPermission.id == "geo") {
let block = document.createXULElement("vbox");
block.setAttribute("id", "identity-popup-geo-container");
let button = this._createPermissionClearButton(aPermission, block);
container.appendChild(button);
block.appendChild(container);
return block;
}
let button = this._createPermissionClearButton(aPermission, container);
container.appendChild(button);
return container;
},
_createPermissionClearButton(aPermission, container) {
let button = document.createXULElement("button");
button.setAttribute("class", "identity-popup-permission-remove-button");
let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
@ -1414,7 +1477,7 @@ var gIdentityHandler = {
aPermission.sharingState &&
["camera", "microphone", "screen"].includes(aPermission.id)
) {
let windowId = this._sharingState.windowId;
let windowId = this._sharingState.webRTC.windowId;
if (aPermission.id == "screen") {
windowId = "screen:" + windowId;
} else {
@ -1426,7 +1489,7 @@ var gIdentityHandler = {
// It's not possible to stop sharing one of camera/microphone
// without the other.
for (let id of ["camera", "microphone"]) {
if (this._sharingState[id]) {
if (this._sharingState.webRTC[id]) {
let perm = SitePermissions.getForPrincipal(principal, id);
if (
perm.state == SitePermissions.ALLOW &&
@ -1451,11 +1514,71 @@ var gIdentityHandler = {
PanelView.forNode(
this._identityPopupMainView
).descriptionHeightWorkaround();
if (aPermission.id === "geo") {
gBrowser.updateBrowserSharing(browser, { geo: false });
}
});
container.appendChild(button);
return button;
},
return container;
_getGeoLocationLastAccess() {
return new Promise(resolve => {
let lastAccess = null;
ContentPrefService2.getByDomainAndName(
gBrowser.currentURI.spec,
"permissions.geoLocation.lastAccess",
gBrowser.selectedBrowser.loadContext,
{
handleResult(pref) {
lastAccess = pref.value;
},
handleCompletion() {
resolve(lastAccess);
},
}
);
});
},
async _createGeoLocationLastAccessIndicator() {
let lastAccessStr = await this._getGeoLocationLastAccess();
if (lastAccessStr == null) {
return;
}
let lastAccess = new Date(lastAccessStr);
if (isNaN(lastAccess)) {
Cu.reportError("Invalid timestamp for last geolocation access");
return;
}
let icon = document.createXULElement("image");
icon.setAttribute("class", "popup-subitem");
let indicator = document.createXULElement("hbox");
indicator.setAttribute("class", "identity-popup-permission-item");
indicator.setAttribute("align", "center");
indicator.setAttribute("id", "geo-access-indicator-item");
let timeFormat = new Services.intl.RelativeTimeFormat(undefined, {});
let text = document.createXULElement("label");
text.setAttribute("flex", "1");
text.setAttribute("class", "identity-popup-permission-label");
text.textContent = gNavigatorBundle.getFormattedString(
"geolocationLastAccessIndicatorText",
[timeFormat.formatBestUnit(lastAccess)]
);
indicator.appendChild(icon);
indicator.appendChild(text);
document
.getElementById("identity-popup-geo-container")
.appendChild(indicator);
},
_createBlockedPopupIndicator() {

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

@ -242,6 +242,10 @@ XPCOMUtils.defineLazyScriptGetter(
// lazy service getters
XPCOMUtils.defineLazyServiceGetters(this, {
ContentPrefService2: [
"@mozilla.org/content-pref/service;1",
"nsIContentPrefService2",
],
classifierService: [
"@mozilla.org/url-classifier/dbservice;1",
"nsIURIClassifier",
@ -6300,7 +6304,7 @@ var TabsProgressListener = {
let tab = gBrowser.getTabForBrowser(aBrowser);
if (tab && tab._sharingState) {
gBrowser.setBrowserSharing(aBrowser, {});
gBrowser.resetBrowserSharing(aBrowser);
}
webrtcUI.forgetStreamsFromBrowser(aBrowser);

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

@ -865,7 +865,10 @@
<image id="identity-icon"
consumeanchor="identity-box"
onclick="PageProxyClickHandler(event);"/>
<image id="sharing-icon" mousethrough="always"/>
<box mousethrough="always">
<image class="sharing-icon" id="webrtc-sharing-icon"/>
<image class="sharing-icon geo-icon" id="geo-sharing-icon"/>
</box>
<box id="blocked-permissions-container" align="center">
<image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
tooltiptext="&urlbar.geolocationBlocked.tooltip;"/>

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

@ -1403,21 +1403,35 @@
aTab.dispatchEvent(event);
},
setBrowserSharing(aBrowser, aState) {
resetBrowserSharing(aBrowser) {
let tab = this.getTabForBrowser(aBrowser);
if (!tab) {
return;
}
tab._sharingState = {};
tab.removeAttribute("sharing");
this._tabAttrModified(tab, ["sharing"]);
if (aBrowser == this.selectedBrowser) {
gIdentityHandler.updateSharingIndicator();
}
},
if (aState.sharing) {
tab._sharingState = aState;
if (aState.paused) {
updateBrowserSharing(aBrowser, aState) {
let tab = this.getTabForBrowser(aBrowser);
if (!tab) {
return;
}
if (tab._sharingState == null) {
tab._sharingState = {};
}
tab._sharingState = Object.assign(tab._sharingState, aState);
if (aState.webRTC && aState.webRTC.sharing) {
if (aState.webRTC.paused) {
tab.removeAttribute("sharing");
} else {
tab.setAttribute("sharing", aState.sharing);
tab.setAttribute("sharing", aState.webRTC.sharing);
}
} else {
tab._sharingState = null;
tab.removeAttribute("sharing");
}
this._tabAttrModified(tab, ["sharing"]);
@ -1429,7 +1443,10 @@
getTabSharingState(aTab) {
// Normalize the state object for consumers (ie.extensions).
let state = Object.assign({}, aTab._sharingState);
let state = Object.assign(
{},
aTab._sharingState && aTab._sharingState.webRTC
);
return {
camera: !!state.camera,
microphone: !!state.microphone,
@ -2445,9 +2462,9 @@
return false;
}
// Reset webrtc sharing state.
// Reset sharing state.
if (aTab._sharingState) {
this.setBrowserSharing(browser, {});
this.resetBrowserSharing(browser);
}
webrtcUI.forgetStreamsFromBrowser(browser);

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

@ -5,6 +5,7 @@ support-files =
!/image/test/mochitest/blue.png
skip-if = fission && debug # Causes shutdown leaks on automation under Fission.
[browser_geolocation_indicator.js]
[browser_bug822367.js]
tags = mcb
support-files =

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

@ -0,0 +1,329 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.import("resource:///modules/PermissionUI.jsm", this);
ChromeUtils.import("resource:///modules/SitePermissions.jsm", this);
const CP = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
const EXAMPLE_PAGE_URL = "https://example.com";
const EXAMPLE_PAGE_URI = Services.io.newURI(EXAMPLE_PAGE_URL);
const GEO_CONTENT_PREF_KEY = "permissions.geoLocation.lastAccess";
const POLL_INTERVAL_FALSE_STATE = 50;
async function testGeoSharingIconVisible(state = true) {
let sharingIcon = document.getElementById("geo-sharing-icon");
ok(sharingIcon, "Geo sharing icon exists");
try {
await BrowserTestUtils.waitForCondition(
() => sharingIcon.hasAttribute("sharing") === true,
"Waiting for geo sharing icon visibility state",
// If we wait for sharing icon to *not* show, waitForCondition will always timeout on correct state.
// In these cases we want to reduce the wait time from 5 seconds to 2.5 seconds to prevent test duration timeouts
!state ? POLL_INTERVAL_FALSE_STATE : undefined
);
} catch (e) {
ok(!state, "Geo sharing icon not showing");
return;
}
ok(state, "Geo sharing icon showing");
}
async function checkForDOMElement(state, id) {
info(`Testing state ${state} of element ${id}`);
try {
await BrowserTestUtils.waitForCondition(
() => {
let el = document.getElementById(id);
return el != null;
},
`Waiting for ${id}`,
!state ? POLL_INTERVAL_FALSE_STATE : undefined
);
} catch (e) {
ok(!state, `${id} has correct state`);
return;
}
ok(state, `${id} has correct state`);
}
async function testIdentityPopupGeoContainer(
containerVisible,
timestampVisible
) {
// Only call openIdentityPopup if popup is closed, otherwise it does not resolve
if (!gIdentityHandler._identityBox.hasAttribute("open")) {
await openIdentityPopup();
}
let checkContainer = checkForDOMElement(
containerVisible,
"identity-popup-geo-container"
);
let checkAccessIndicator = checkForDOMElement(
timestampVisible,
"geo-access-indicator-item"
);
return Promise.all([checkContainer, checkAccessIndicator]);
}
function openExamplePage(tabbrowser = gBrowser) {
return BrowserTestUtils.openNewForegroundTab(tabbrowser, EXAMPLE_PAGE_URL);
}
function requestGeoLocation(browser) {
return ContentTask.spawn(browser, null, () => {
return new Promise(resolve => {
content.navigator.geolocation.getCurrentPosition(
() => resolve(true),
error => resolve(error.code !== 1) // PERMISSION_DENIED = 1
);
});
});
}
function answerGeoLocationPopup(allow, remember = false) {
let notification = PopupNotifications.getNotification("geolocation");
ok(
PopupNotifications.isPanelOpen && notification,
"Geolocation notification is open"
);
let rememberCheck = PopupNotifications.panel.querySelector(
".popup-notification-checkbox"
);
rememberCheck.checked = remember;
let popupHidden = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popuphidden"
);
if (allow) {
let allowBtn = PopupNotifications.panel.querySelector(
".popup-notification-primary-button"
);
allowBtn.click();
} else {
let denyBtn = PopupNotifications.panel.querySelector(
".popup-notification-secondary-button"
);
denyBtn.click();
}
return popupHidden;
}
function setGeoLastAccess(browser, state) {
return new Promise(resolve => {
let host = browser.currentURI.host;
let handler = {
handleCompletion: () => resolve(),
};
if (!state) {
CP.removeByDomainAndName(
host,
GEO_CONTENT_PREF_KEY,
browser.loadContext,
handler
);
return;
}
CP.set(
host,
GEO_CONTENT_PREF_KEY,
new Date().toString(),
browser.loadContext,
handler
);
});
}
async function testGeoLocationLastAccessSet(browser) {
let timestamp = await new Promise(resolve => {
let lastAccess = null;
CP.getByDomainAndName(
gBrowser.currentURI.spec,
GEO_CONTENT_PREF_KEY,
browser.loadContext,
{
handleResult(pref) {
lastAccess = pref.value;
},
handleCompletion() {
resolve(lastAccess);
},
}
);
});
ok(timestamp != null, "Geo last access timestamp set");
let parseSuccess = true;
try {
timestamp = new Date(timestamp);
} catch (e) {
parseSuccess = false;
}
ok(
parseSuccess && !isNaN(timestamp),
"Geo last access timestamp is valid Date"
);
}
async function cleanup(tab) {
await setGeoLastAccess(tab.linkedBrowser, false);
SitePermissions.removeFromPrincipal(
tab.linkedBrowser.contentPrincipal,
"geo",
tab.linkedBrowser
);
gBrowser.resetBrowserSharing(tab.linkedBrowser);
BrowserTestUtils.removeTab(tab);
}
async function testIndicatorGeoSharingState(active) {
let tab = await openExamplePage();
gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: active });
await testGeoSharingIconVisible(active);
await cleanup(tab);
}
async function testIndicatorExplicitAllow(persistent) {
let tab = await openExamplePage();
let popupShown = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
info("Requesting geolocation");
let request = requestGeoLocation(tab.linkedBrowser);
await popupShown;
info("Allowing geolocation via popup");
answerGeoLocationPopup(true, persistent);
await request;
await Promise.all([
testGeoSharingIconVisible(true),
testIdentityPopupGeoContainer(true, true),
testGeoLocationLastAccessSet(tab.linkedBrowser),
]);
await cleanup(tab);
}
// Indicator and identity popup entry shown after explicit PermissionUI geolocation allow
add_task(function test_indicator_and_timestamp_after_explicit_allow() {
return testIndicatorExplicitAllow(false);
});
add_task(function test_indicator_and_timestamp_after_explicit_allow_remember() {
return testIndicatorExplicitAllow(true);
});
// Indicator and identity popup entry shown after auto PermissionUI geolocation allow
add_task(async function test_indicator_and_timestamp_after_implicit_allow() {
SitePermissions.set(
EXAMPLE_PAGE_URI,
"geo",
SitePermissions.ALLOW,
SitePermissions.SCOPE_PERSISTENT
);
let tab = await openExamplePage();
let result = await requestGeoLocation(tab.linkedBrowser);
ok(result, "Request should be allowed");
await Promise.all([
testGeoSharingIconVisible(true),
testIdentityPopupGeoContainer(true, true),
testGeoLocationLastAccessSet(tab.linkedBrowser),
]);
await cleanup(tab);
});
// Indicator shown when manually setting sharing state to true
add_task(function test_indicator_sharing_state_active() {
return testIndicatorGeoSharingState(true);
});
// Indicator not shown when manually setting sharing state to false
add_task(function test_indicator_sharing_state_inactive() {
return testIndicatorGeoSharingState(false);
});
// Identity popup shows permission if geo permission is set to persistent allow
add_task(async function test_identity_popup_permission_scope_permanent() {
SitePermissions.set(
EXAMPLE_PAGE_URI,
"geo",
SitePermissions.ALLOW,
SitePermissions.SCOPE_PERSISTENT
);
let tab = await openExamplePage();
await testIdentityPopupGeoContainer(true, false); // Expect permission to be visible, but not lastAccess indicator
await cleanup(tab);
});
// Sharing state set, but no permission
add_task(async function test_identity_popup_permission_sharing_state() {
let tab = await openExamplePage();
gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
await testIdentityPopupGeoContainer(true, false);
await cleanup(tab);
});
// Identity popup has correct state if sharing state and last geo access timestamp are set
add_task(
async function test_identity_popup_permission_sharing_state_timestamp() {
let tab = await openExamplePage();
gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
await setGeoLastAccess(tab.linkedBrowser, true);
await testIdentityPopupGeoContainer(true, true);
await cleanup(tab);
}
);
// Clicking permission clear button clears permission and resets geo sharing state
add_task(async function test_identity_popup_permission_clear() {
SitePermissions.set(
EXAMPLE_PAGE_URI,
"geo",
SitePermissions.ALLOW,
SitePermissions.SCOPE_PERSISTENT
);
let tab = await openExamplePage();
gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
await openIdentityPopup();
let clearButton = document.querySelector(
"#identity-popup-geo-container button"
);
ok(clearButton, "Clear button is visible");
clearButton.click();
await Promise.all([
testGeoSharingIconVisible(false),
testIdentityPopupGeoContainer(false, false),
BrowserTestUtils.waitForCondition(() => {
let sharingState = tab._sharingState;
return (
sharingState == null ||
sharingState.geo == null ||
sharingState.geo === false
);
}, "Waiting for geo sharing state to reset"),
]);
await cleanup(tab);
});

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

@ -2,6 +2,13 @@ var { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
function openIdentityPopup() {
let mainView = document.getElementById("identity-popup-mainView");
let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
gIdentityHandler._identityBox.click();
return viewShown;
}
/**
* Waits for a load (or custom) event to finish in a given tab. If provided
* load an uri into the tab.

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

@ -88,7 +88,7 @@ var gTests = [
// wait for it to avoid intermittents.
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.camera ==
window.gIdentityHandler._sharingState.webRTC.camera ==
STATE_CAPTURE_DISABLED,
"video should be disabled"
);
@ -106,7 +106,7 @@ var gTests = [
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.microphone ==
window.gIdentityHandler._sharingState.webRTC.microphone ==
STATE_CAPTURE_ENABLED,
"audio should be enabled"
);
@ -124,7 +124,8 @@ var gTests = [
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.camera == STATE_CAPTURE_ENABLED,
window.gIdentityHandler._sharingState.webRTC.camera ==
STATE_CAPTURE_ENABLED,
"video should be enabled"
);
@ -181,9 +182,9 @@ var gTests = [
// wait for it to avoid intermittents.
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.camera ==
window.gIdentityHandler._sharingState.webRTC.camera ==
STATE_CAPTURE_DISABLED &&
window.gIdentityHandler._sharingState.microphone ==
window.gIdentityHandler._sharingState.webRTC.microphone ==
STATE_CAPTURE_DISABLED,
"video and audio should be disabled"
);
@ -201,7 +202,7 @@ var gTests = [
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.microphone ==
window.gIdentityHandler._sharingState.webRTC.microphone ==
STATE_CAPTURE_ENABLED,
"audio should be enabled"
);
@ -219,7 +220,8 @@ var gTests = [
await BrowserTestUtils.waitForCondition(
() =>
window.gIdentityHandler._sharingState.camera == STATE_CAPTURE_ENABLED,
window.gIdentityHandler._sharingState.webRTC.camera ==
STATE_CAPTURE_ENABLED,
"video should be enabled"
);
@ -276,7 +278,8 @@ var gTests = [
// It sometimes takes a bit longer before the change propagates to the UI,
// wait for it to avoid intermittents.
await BrowserTestUtils.waitForCondition(
() => window.gIdentityHandler._sharingState.screen == "ScreenPaused",
() =>
window.gIdentityHandler._sharingState.webRTC.screen == "ScreenPaused",
"screen should be disabled"
);
await expectObserverCalled("recording-device-events");
@ -287,7 +290,7 @@ var gTests = [
await setTrackEnabled(null, true);
await BrowserTestUtils.waitForCondition(
() => window.gIdentityHandler._sharingState.screen == "Screen",
() => window.gIdentityHandler._sharingState.webRTC.screen == "Screen",
"screen should be enabled"
);
await expectObserverCalled("recording-device-events");

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

@ -567,8 +567,9 @@ async function checkSharingUI(
let doc = aWin.document;
// First check the icon above the control center (i) icon.
let identityBox = doc.getElementById("identity-box");
ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
let sharing = identityBox.getAttribute("sharing");
let webrtcSharingIcon = doc.getElementById("webrtc-sharing-icon");
ok(webrtcSharingIcon.hasAttribute("sharing"), "sharing attribute is set");
let sharing = webrtcSharingIcon.getAttribute("sharing");
if (aExpected.screen) {
is(sharing, "screen", "showing screen icon in the identity block");
} else if (aExpected.video == STATE_CAPTURE_ENABLED) {
@ -583,7 +584,7 @@ async function checkSharingUI(
let allStreamsPaused = Object.values(aExpected).every(isPaused);
is(
identityBox.hasAttribute("paused"),
webrtcSharingIcon.hasAttribute("paused"),
allStreamsPaused,
"sharing icon(s) should be in paused state when paused"
);
@ -603,7 +604,7 @@ async function checkSharingUI(
};
let expected = aExpected[convertId(id)];
is(
!!aWin.gIdentityHandler._sharingState[id],
!!aWin.gIdentityHandler._sharingState.webRTC[id],
!!expected,
"sharing state for " + id + " as expected"
);
@ -642,7 +643,7 @@ async function checkNotSharing() {
);
ok(
!document.getElementById("identity-box").hasAttribute("sharing"),
!document.getElementById("webrtc-sharing-icon").hasAttribute("sharing"),
"no sharing indicator on the control center icon"
);

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

@ -9,21 +9,23 @@ add_task(async function test_tabs_mediaIndicators() {
gBrowser,
"http://example.com/"
);
// setBrowserSharing is called when a request for media icons occurs. We're
// updateBrowserSharing is called when a request for media icons occurs. We're
// just testing that extension tabs get the info and are updated when it is
// called.
gBrowser.setBrowserSharing(tab.linkedBrowser, {
sharing: "screen",
screen: "Window",
microphone: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
camera: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
gBrowser.updateBrowserSharing(tab.linkedBrowser, {
webRTC: {
sharing: "screen",
screen: "Window",
microphone: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
camera: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
},
});
async function background() {
let tabs = await browser.tabs.query({ microphone: true });
let testTab = tabs[0];
let state = testTab.sharingState;
let state = testTab.sharingState.webRTC;
browser.test.assertTrue(state.camera, "sharing camera was turned on");
browser.test.assertTrue(state.microphone, "sharing mic was turned on");
browser.test.assertEq(state.screen, "Window", "sharing screen is window");
@ -51,7 +53,7 @@ add_task(async function test_tabs_mediaIndicators() {
if (testTab.id !== tabId) {
return;
}
let state = tab.sharingState;
let state = tab.sharingState.webRTC;
browser.test.assertFalse(state.camera, "sharing camera was turned off");
browser.test.assertFalse(state.microphone, "sharing mic was turned off");
browser.test.assertFalse(state.screen, "sharing screen was turned off");
@ -71,7 +73,7 @@ add_task(async function test_tabs_mediaIndicators() {
// Test that onUpdated is called after the sharing state is changed from
// chrome code.
await extension.awaitMessage("ready");
gBrowser.setBrowserSharing(tab.linkedBrowser, {});
gBrowser.resetBrowserSharing(tab.linkedBrowser);
await extension.awaitFinish("done");
await extension.unload();

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

@ -284,6 +284,9 @@ popupShowPopupPrefix=Show %S
# #1 is the number of pop-ups blocked.
popupShowBlockedPopupsIndicatorText=Show #1 blocked pop-up…;Show #1 blocked pop-ups…
# LOCALIZATION NOTE (geolocationLastAccessIndicatorText): %S is the relative time of the most recent geolocation access (e.g. 5 min. ago)
geolocationLastAccessIndicatorText=Last access %S
# Bad Content Blocker Doorhanger Notification
# %S is brandShortName
badContentBlocked.blocked.message=%S is blocking content on this page.

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

@ -94,6 +94,13 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIIDNService"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"ContentPrefService2",
"@mozilla.org/content-pref/service;1",
"nsIContentPrefService2"
);
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
@ -809,6 +816,39 @@ GeolocationPermissionPrompt.prototype = {
},
];
},
_updateGeoSharing(state) {
let gBrowser = this.browser.ownerGlobal.gBrowser;
gBrowser.updateBrowserSharing(this.browser, { geo: state });
if (!state) {
return;
}
let host;
try {
host = this.browser.currentURI.host;
} catch (e) {
return;
}
if (host == null || host == "") {
return;
}
ContentPrefService2.set(
this.browser.currentURI.host,
"permissions.geoLocation.lastAccess",
new Date().toString(),
this.browser.loadContext
);
},
allow(...args) {
this._updateGeoSharing(true);
PermissionPromptForRequestPrototype.allow.apply(this, args);
},
cancel(...args) {
this._updateGeoSharing(false);
PermissionPromptForRequestPrototype.cancel.apply(this, args);
},
};
PermissionUI.GeolocationPermissionPrompt = GeolocationPermissionPrompt;

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

@ -336,7 +336,9 @@ var webrtcUI = {
}
let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
if (tabbrowser) {
tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
tabbrowser.updateBrowserSharing(aMessage.target, {
webRTC: aMessage.data,
});
}
break;
case "child-process-shutdown":

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

@ -105,7 +105,7 @@
border-image-slice: 1;
}
#sharing-icon,
.sharing-icon,
#identity-icon,
#tracking-protection-icon,
.notification-anchor-icon,
@ -167,23 +167,27 @@
/* SHARING ICON */
#identity-box[sharing="camera"] > #sharing-icon {
#webrtc-sharing-icon[sharing="camera"] {
list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
}
#identity-box[sharing="microphone"] > #sharing-icon {
#webrtc-sharing-icon[sharing="microphone"] {
list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
}
#identity-box[sharing="screen"] > #sharing-icon {
#webrtc-sharing-icon[sharing="screen"] {
list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
}
#identity-box:not([sharing]) > #sharing-icon {
#geo-sharing-icon[sharing] {
list-style-image: url("chrome://browser/skin/notification-icons/geo.svg");
}
.sharing-icon:not([sharing]) {
display: none;
}
#identity-box[sharing]:not([paused]) > #sharing-icon {
#webrtc-sharing-icon[sharing]:not([paused]) {
animation: 1.5s ease in-use-blink infinite;
-moz-context-properties: fill;
fill: rgb(224, 41, 29);