Bug 1634796 - Add a panel that warns users before switching tabs when sharing the window over WebRTC. r=johannh,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D73734
This commit is contained in:
Mike Conley 2020-05-18 22:41:43 +00:00
Родитель ca8cca32cb
Коммит 27a590bd9e
11 изменённых файлов: 302 добавлений и 6 удалений

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

@ -1662,6 +1662,9 @@ pref("privacy.userContext.newTabContainerOnLeftClick.enabled", false);
pref("privacy.webrtc.allowSilencingNotifications", false);
// Set to true to use the legacy WebRTC global indicator
pref("privacy.webrtc.legacyGlobalIndicator", true);
// Set to true to enable a warning displayed when attempting
// to switch tabs in a window that's being shared over WebRTC.
pref("privacy.webrtc.sharedTabWarning", false);
// Start the browser in e10s mode
pref("browser.tabs.remote.autostart", true);

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

@ -0,0 +1,140 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env mozilla/browser-window */
/**
* Utility object to handle WebRTC shared tab warnings.
*/
var gSharedTabWarning = {
/**
* Called externally by gBrowser to determine if we're
* in a state such that we'd want to cancel the tab switch
* and show the tab switch warning panel instead.
*
* @param tab (<tab>)
* The tab being switched to.
* @returns boolean
* True if the panel will be shown, and the tab switch should
* be cancelled.
*/
willShowSharedTabWarning(tab) {
if (!this._sharedTabWarningEnabled) {
return false;
}
let shareState = webrtcUI.getWindowShareState(window);
if (shareState == webrtcUI.SHARING_NONE) {
return false;
}
if (!webrtcUI.shouldShowSharedTabWarning(tab)) {
return false;
}
this._createSharedTabWarningIfNeeded();
let panel = document.getElementById("sharing-tabs-warning-panel");
let hbox = panel.firstChild;
if (shareState == webrtcUI.SHARING_SCREEN) {
hbox.setAttribute("type", "screen");
panel.setAttribute(
"aria-labelledby",
"sharing-screen-warning-panel-header-span"
);
} else {
hbox.setAttribute("type", "window");
panel.setAttribute(
"aria-labelledby",
"sharing-window-warning-panel-header-span"
);
}
let allowForSessionCheckbox = document.getElementById(
"sharing-warning-disable-for-session"
);
allowForSessionCheckbox.checked = false;
panel.openPopup(tab, "bottomcenter topleft", 0, 0);
return true;
},
/**
* Called by the tab switch warning panel after it has
* shown.
*/
sharedTabWarningShown() {
let allowButton = document.getElementById("sharing-warning-proceed-to-tab");
allowButton.focus();
},
/**
* Called by the button in the tab switch warning panel
* to allow the switch to occur.
*/
allowSharedTabSwitch() {
let panel = document.getElementById("sharing-tabs-warning-panel");
let allowForSession = document.getElementById(
"sharing-warning-disable-for-session"
).checked;
let tab = panel.anchorNode;
webrtcUI.allowSharedTabSwitch(tab, allowForSession);
this._hideSharedTabWarning();
},
/**
* Called externally by gBrowser when a tab has been added.
* When this occurs, if we're sharing this window, we notify
* the webrtcUI module to exempt the new tab from the tab switch
* warning, since the user opened it while they were already
* sharing.
*
* @param tab (<tab>)
* The tab being opened.
*/
tabAdded(tab) {
if (this._sharedTabWarningEnabled) {
let shareState = webrtcUI.getWindowShareState(window);
if (shareState != webrtcUI.SHARING_NONE) {
webrtcUI.tabAddedWhileSharing(tab);
}
}
},
get _sharedTabWarningEnabled() {
delete this._sharedTabWarningEnabled;
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_sharedTabWarningEnabled",
"privacy.webrtc.sharedTabWarning"
);
return this._sharedTabWarningEnabled;
},
/**
* Internal method for hiding the tab switch warning panel.
*/
_hideSharedTabWarning() {
let panel = document.getElementById("sharing-tabs-warning-panel");
if (panel) {
panel.hidePopup();
}
},
/**
* Inserts the tab switch warning panel into the DOM
* if it hasn't been done already yet.
*/
_createSharedTabWarningIfNeeded() {
// Lazy load the panel the first time we need to display it.
if (!document.getElementById("sharing-tabs-warning-panel")) {
let template = document.getElementById(
"sharing-tabs-warning-panel-template"
);
template.replaceWith(template.content);
}
},
};

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

@ -245,6 +245,11 @@ XPCOMUtils.defineLazyScriptGetter(
"A11yUtils",
"chrome://browser/content/browser-a11yUtils.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gSharedTabWarning",
"chrome://browser/content/browser-webrtc.js"
);
// lazy service getters

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

@ -83,6 +83,7 @@
<link rel="localization" href="browser/sidebarMenu.ftl"/>
<link rel="localization" href="browser/allTabsMenu.ftl"/>
<link rel="localization" href="browser/places.ftl"/>
<link rel="localization" href="preview/popup-notifications.ftl"/>
<title>&mainWindow.title;</title>
@ -685,6 +686,40 @@
crop="end"/>
</hbox>
</hbox>
<html:template id="sharing-tabs-warning-panel-template">
<panel id="sharing-tabs-warning-panel"
role="alert"
flip="slide"
type="arrow"
orient="vertical"
ignorekeys="true"
consumeoutsideclicks="never"
norolluponanchor="true"
onpopupshown="gSharedTabWarning.sharedTabWarningShown();">
<hbox type="window" align="start">
<image class="screen-icon popup-notification-icon"></image>
<vbox flex="1" pack="start">
<label>
<html:span id="sharing-warning-window-panel-header"
role="heading"
aria-level="1"
data-l10n-id="sharing-warning-window"/>
<html:span id="sharing-warning-screen-panel-header"
role="heading"
aria-level="1"
data-l10n-id="sharing-warning-screen"/>
</label>
<hbox align="center">
<button id="sharing-warning-proceed-to-tab" oncommand="gSharedTabWarning.allowSharedTabSwitch();" flex="1" data-l10n-id="sharing-warning-proceed-to-tab"/>
</hbox>
<hbox pack="start">
<checkbox id="sharing-warning-disable-for-session" data-l10n-id="sharing-warning-disable-for-session"/>
</hbox>
</vbox>
</hbox>
</panel>
</html:template>
</popupset>
<box id="appMenu-viewCache" hidden="true"/>

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

@ -17,3 +17,12 @@ popup-screen-sharing-never =
popup-silence-notifications-checkbox = Disable notifications from { -brand-short-name } while sharing
popup-silence-notifications-checkbox-warning = { -brand-short-name } will not display notifications while you are sharing.
## WebRTC window or screen share tab switch warning
sharing-warning-window = You are sharing { -brand-short-name }. Other people can see when you switch to a new tab.
sharing-warning-screen = You are sharing your entire screen. Other people can see when you switch to a new tab.
sharing-warning-proceed-to-tab =
.label = Proceed to Tab
sharing-warning-disable-for-session =
.label = Disable sharing protection for this session

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

@ -292,7 +292,10 @@
on_dragstart(event) {
if (event.eventPhase == Event.CAPTURING_PHASE) {
this.style.MozUserFocus = "";
} else if (this.mOverCloseButton) {
} else if (
this.mOverCloseButton ||
gSharedTabWarning.willShowSharedTabWarning(this)
) {
event.stopPropagation();
}
}
@ -354,6 +357,10 @@
}
}
if (gSharedTabWarning.willShowSharedTabWarning(this)) {
eventMaySelectTab = false;
}
if (eventMaySelectTab) {
super.on_mousedown(event);
}

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

@ -293,7 +293,10 @@
},
set selectedTab(val) {
if (gNavToolbox.collapsed && !this._allowTabChange) {
if (
gSharedTabWarning.willShowSharedTabWarning(val) ||
(gNavToolbox.collapsed && !this._allowTabChange)
) {
return this.tabbox.selectedTab;
}
// Update the tab
@ -2855,6 +2858,8 @@
this._notifyPinnedStatus(t);
}
gSharedTabWarning.tabAdded(t);
return t;
},

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

@ -229,10 +229,6 @@ var whitelist = [
// Referenced from the screenshots webextension
{ file: "resource://app/localization/en-US/browser/screenshots.ftl" },
// This file is referenced from WebRTCParent.jsm. Once this localization
// file gets moved to the locales directory, this should get removed.
{ file: "resource://app/localization/en-US/preview/popup-notifications.ftl" },
// services/fxaccounts/RustFxAccount.js
{ file: "resource://gre/modules/RustFxAccount.js" },
];

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

@ -57,6 +57,7 @@ browser.jar:
content/browser/browser-toolbarKeyNav.js (content/browser-toolbarKeyNav.js)
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
content/browser/browser-graphics-utils.js (content/browser-graphics-utils.js)
content/browser/browser-webrtc.js (content/browser-webrtc.js)
content/browser/tab-content.js (content/tab-content.js)
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
content/browser/defaultthemes/1.icon.jpg (content/defaultthemes/1.icon.jpg)

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

@ -74,6 +74,9 @@ var webrtcUI = {
// Set of browser windows that are being shared over WebRTC.
sharedWindows: new WeakSet(),
sharingScreen: false,
allowedSharedBrowsers: new WeakSet(),
allowTabSwitchesForSession: false,
tabSwitchCountForSession: 0,
// Map of browser elements to indicator data.
perTabIndicators: new Map(),
@ -298,9 +301,14 @@ var webrtcUI = {
};
}
// Reset our internal notion of whether or not we're sharing
// a screen or browser window. Now we'll go through the shared
// devices and re-determine what's being shared.
let sharingBrowserWindow = false;
let sharedWindowRawDeviceIds = new Set();
this.sharingScreen = false;
let suppressNotifications = false;
for (let stream of this._streams) {
let { state } = stream;
suppressNotifications |= state.suppressNotifications;
@ -317,6 +325,15 @@ var webrtcUI = {
} else if (mediaSource == "screen") {
this.sharingScreen = true;
}
// If the user has granted a particular site the ability
// to get a stream from a window or screen, we will
// presume that it's exempt from the tab switch warning.
//
// We use the permanentKey here so that the allowing of
// the tab survives tab tear-in and tear-out.
let browser = stream.topBrowsingContext.embedderElement;
this.allowedSharedBrowsers.add(browser.permanentKey);
}
}
@ -334,9 +351,28 @@ var webrtcUI = {
}
if (sharedWindowRawDeviceIds.has(rawDeviceId)) {
this.sharedWindows.add(win);
// If we've shared a window, then the initially selected tab
// in that window should be exempt from tab switch warnings,
// since it's already been shared.
let selectedBrowser = win.gBrowser.selectedBrowser;
this.allowedSharedBrowsers.add(selectedBrowser.permanentKey);
sharingBrowserWindow = true;
}
}
// Since we're not sharing a screen or browser window,
// we can clear these state variables, which are used
// to warn users on tab switching when sharing. These
// are safe to reset even if we hadn't been sharing
// the screen or browser window already.
if (!this.sharingScreen && !sharingBrowserWindow) {
this.allowedSharedBrowsers = new WeakSet();
this.allowTabSwitchesForSession = false;
this.tabSwitchCountForSession = 0;
}
if (
Services.prefs.getBoolPref(
"privacy.webrtc.allowSilencingNotifications",
@ -556,6 +592,40 @@ var webrtcUI = {
}
return this.SHARING_NONE;
},
tabAddedWhileSharing(tab) {
this.allowedSharedBrowsers.add(tab.linkedBrowser.permanentKey);
},
shouldShowSharedTabWarning(tab) {
let browser = tab.linkedBrowser;
// We want the user to be able to switch to one tab after starting
// to share their window or screen. The presumption here is that
// most users will have a single window with multiple tabs, where
// the selected tab will be the one with the screen or window
// sharing web application, and it's most likely that the contents
// that the user wants to share are in another tab that they'll
// switch to immediately upon sharing. These presumptions are based
// on research that our user research team did with users using
// video conferencing web applications.
if (!this.tabSwitchCountForSession) {
this.allowedSharedBrowsers.add(browser.permanentKey);
}
this.tabSwitchCountForSession++;
return (
!this.allowTabSwitchesForSession &&
!this.allowedSharedBrowsers.has(browser.permanentKey)
);
},
allowSharedTabSwitch(tab, allowForSession) {
let browser = tab.linkedBrowser;
let gBrowser = browser.getTabBrowser();
this.allowedSharedBrowsers.add(browser.permanentKey);
gBrowser.selectedTab = tab;
this.allowTabSwitchesForSession = allowForSession;
},
};
function getGlobalIndicator() {

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

@ -595,3 +595,28 @@ menupopup::part(drop-indicator) {
font-size: 1.18em;
%endif
}
#sharing-tabs-warning-panel > hbox[type="window"] > vbox > label > #sharing-warning-screen-panel-header,
#sharing-tabs-warning-panel > hbox[type="screen"] > vbox > label > #sharing-warning-window-panel-header {
display: none;
}
#sharing-tabs-warning-panel {
max-width: 400px;
}
#sharing-warning-proceed-to-tab {
-moz-appearance: none;
border-style: none;
margin: 0;
background-color: rgb(0,96,223);
color: rgb(255,255,255);
border-radius: 5px;
padding: 10px;
margin-top: 15px;
margin-bottom: 10px;
}
#sharing-warning-proceed-to-tab:hover {
background-color: rgb(0,62,170);
}