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