зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1636207 - Create a new WebRTC global indicator. r=fluent-reviewers,flod,johannh
Differential Revision: https://phabricator.services.mozilla.com/D74333
This commit is contained in:
Родитель
55cdaf6040
Коммит
baa852c82b
|
@ -233,6 +233,10 @@ var whitelist = [
|
|||
// file gets moved to the locales directory, this should get removed.
|
||||
{ file: "resource://app/localization/en-US/preview/popup-notifications.ftl" },
|
||||
|
||||
// This file is referenced from webrtcIndicator.xhtml. Once this localization
|
||||
// file gets moved to the locales directory, this should get removed.
|
||||
{ file: "chrome://browser/content/webrtcIndicator.ftl" },
|
||||
|
||||
// services/fxaccounts/RustFxAccount.js
|
||||
{ file: "resource://gre/modules/RustFxAccount.js" },
|
||||
];
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# 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/.
|
||||
|
||||
# Note: This is currently placed under browser/base/content so that we can
|
||||
# get the strings to appear without having our localization community need
|
||||
# to go through and translate everything. Once these strings are ready for
|
||||
# translation, we'll move it to the locales folder.
|
||||
|
||||
# This string is used so that the window has a title in tools that enumerate/look for window
|
||||
# titles. It is not normally visible anywhere.
|
||||
webrtc-indicator-title = { -brand-short-name } - Sharing Indicator
|
||||
|
||||
webrtc-sharing-window = You are sharing another application window.
|
||||
webrtc-sharing-browser-window = You are sharing { -brand-short-name }.
|
||||
webrtc-sharing-screen = You are sharing your entire screen.
|
||||
webrtc-stop-sharing-button = Stop Sharing
|
||||
webrtc-microphone-button =
|
||||
.title = Your microphone is being shared. Click to control sharing.
|
||||
webrtc-camera-button =
|
||||
.title = Your camera is being shared. Click to control sharing.
|
||||
webrtc-minimize =
|
||||
.title = Minimize indicator
|
|
@ -3,211 +3,381 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { webrtcUI } = ChromeUtils.import("resource:///modules/webrtcUI.jsm");
|
||||
|
||||
const BUNDLE_URL = "chrome://browser/locale/webrtcIndicator.properties";
|
||||
var gStringBundle;
|
||||
|
||||
function init(event) {
|
||||
gStringBundle = Services.strings.createBundle(BUNDLE_URL);
|
||||
|
||||
let brand = Services.strings.createBundle(
|
||||
"chrome://branding/locale/brand.properties"
|
||||
);
|
||||
let brandShortName = brand.GetStringFromName("brandShortName");
|
||||
document.title = gStringBundle.formatStringFromName(
|
||||
"webrtcIndicator.windowtitle",
|
||||
[brandShortName]
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"SitePermissions",
|
||||
"resource:///modules/SitePermissions.jsm"
|
||||
);
|
||||
|
||||
for (let id of ["audioVideoButton", "screenSharePopup"]) {
|
||||
let popup = document.getElementById(id);
|
||||
popup.addEventListener("popupshowing", onPopupMenuShowing);
|
||||
popup.addEventListener("popuphiding", onPopupMenuHiding);
|
||||
popup.addEventListener("command", onPopupMenuCommand);
|
||||
}
|
||||
|
||||
let fxButton = document.getElementById("firefoxButton");
|
||||
fxButton.addEventListener("click", onFirefoxButtonClick);
|
||||
fxButton.addEventListener("mousedown", PositionHandler);
|
||||
|
||||
updateIndicatorState();
|
||||
|
||||
// Alert accessibility implementations stuff just changed. We only need to do
|
||||
// this initially, because changes after this will automatically fire alert
|
||||
// events if things change materially.
|
||||
let ev = new CustomEvent("AlertActive", { bubbles: true, cancelable: true });
|
||||
document.documentElement.dispatchEvent(ev);
|
||||
}
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"gScreenManager",
|
||||
"@mozilla.org/gfx/screenmanager;1",
|
||||
"nsIScreenManager"
|
||||
);
|
||||
|
||||
/**
|
||||
* Public function called by webrtcUI to update the indicator
|
||||
* display when the active streams change.
|
||||
*/
|
||||
function updateIndicatorState() {
|
||||
// If gStringBundle isn't set, the window hasn't finished loading.
|
||||
if (!gStringBundle) {
|
||||
WebRTCIndicator.updateIndicatorState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main control object for the WebRTC global indicator
|
||||
*/
|
||||
const WebRTCIndicator = {
|
||||
init(event) {
|
||||
addEventListener("load", this);
|
||||
|
||||
// If the user customizes the position of the indicator, we will
|
||||
// not try to re-center it on the primary display after indicator
|
||||
// state updates.
|
||||
this.positionCustomized = false;
|
||||
|
||||
this.updatingIndicatorState = false;
|
||||
this.loaded = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposed externally so that webrtcUI can alert the indicator to
|
||||
* update itself when sharing states have changed.
|
||||
*/
|
||||
updateIndicatorState(initialLayout = false) {
|
||||
// It's possible that we were called externally before the indicator
|
||||
// finished loading. If so, then bail out - we're going to call
|
||||
// updateIndicatorState ourselves automatically once the load
|
||||
// event fires.
|
||||
if (!this.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateWindowAttr("sharingvideo", webrtcUI.showCameraIndicator);
|
||||
updateWindowAttr("sharingaudio", webrtcUI.showMicrophoneIndicator);
|
||||
updateWindowAttr("sharingscreen", webrtcUI.showScreenSharingIndicator);
|
||||
// We've started to update the indicator state. We set this flag so
|
||||
// that the MozUpdateWindowPos event handler doesn't interpret indicator
|
||||
// state updates as window movement caused by the user.
|
||||
this.updatingIndicatorState = true;
|
||||
|
||||
// Camera and microphone button tooltip.
|
||||
let shareTypes = [];
|
||||
if (webrtcUI.showCameraIndicator) {
|
||||
shareTypes.push("Camera");
|
||||
}
|
||||
if (webrtcUI.showMicrophoneIndicator) {
|
||||
shareTypes.push("Microphone");
|
||||
}
|
||||
|
||||
let audioVideoButton = document.getElementById("audioVideoButton");
|
||||
if (shareTypes.length) {
|
||||
let stringId =
|
||||
"webrtcIndicator.sharing" + shareTypes.join("And") + ".tooltip";
|
||||
audioVideoButton.setAttribute(
|
||||
"tooltiptext",
|
||||
gStringBundle.GetStringFromName(stringId)
|
||||
this.updateWindowAttr("sharingvideo", webrtcUI.showCameraIndicator);
|
||||
this.updateWindowAttr("sharingaudio", webrtcUI.showMicrophoneIndicator);
|
||||
this.updateWindowAttr(
|
||||
"sharingscreen",
|
||||
webrtcUI.showScreenSharingIndicator.startsWith("Screen")
|
||||
);
|
||||
} else {
|
||||
audioVideoButton.removeAttribute("tooltiptext");
|
||||
}
|
||||
|
||||
// Screen sharing button tooltip.
|
||||
let screenShareButton = document.getElementById("screenShareButton");
|
||||
if (webrtcUI.showScreenSharingIndicator) {
|
||||
let stringId =
|
||||
"webrtcIndicator.sharing" +
|
||||
webrtcUI.showScreenSharingIndicator +
|
||||
".tooltip";
|
||||
screenShareButton.setAttribute(
|
||||
"tooltiptext",
|
||||
gStringBundle.GetStringFromName(stringId)
|
||||
// We don't currently support the browser-tab sharing case, so we don't
|
||||
// check if the screen sharing indicator starts with "Browser".
|
||||
|
||||
// We special-case sharing a window, because we want to have a slightly
|
||||
// different UI if we're sharing a browser window.
|
||||
let sharingWindow = webrtcUI.showScreenSharingIndicator.startsWith(
|
||||
"Window"
|
||||
);
|
||||
this.updateWindowAttr("sharingwindow", sharingWindow);
|
||||
|
||||
if (sharingWindow) {
|
||||
// Get the active window streams and see if any of them are "scary".
|
||||
// If so, then we're sharing a browser window.
|
||||
let activeStreams = webrtcUI.getActiveStreams(
|
||||
false /* camera */,
|
||||
false /* microphone */,
|
||||
false /* screen */,
|
||||
true /* window */
|
||||
);
|
||||
let hasBrowserWindow = activeStreams.some(stream => {
|
||||
return stream.devices.some(device => device.scary);
|
||||
});
|
||||
|
||||
this.updateWindowAttr("sharingbrowserwindow", hasBrowserWindow);
|
||||
this.sharingBrowserWindow = hasBrowserWindow;
|
||||
} else {
|
||||
screenShareButton.removeAttribute("tooltiptext");
|
||||
this.updateWindowAttr("sharingbrowserwindow");
|
||||
this.sharingBrowserWindow = false;
|
||||
}
|
||||
|
||||
// Resize and ensure the window position is correct
|
||||
// (sizeToContent messes with our position).
|
||||
window.sizeToContent();
|
||||
PositionHandler.adjustPosition();
|
||||
|
||||
this.ensureOnScreen();
|
||||
|
||||
if (!this.positionCustomized) {
|
||||
this.centerOnPrimaryDisplay();
|
||||
}
|
||||
this.updatingIndicatorState = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* After the indicator has been updated, checks to see if it has expanded
|
||||
* such that part of the indicator is now outside of the screen. If so,
|
||||
* it then adjusts the position to put the entire indicator on screen.
|
||||
*/
|
||||
ensureOnScreen() {
|
||||
let desiredX = Math.max(window.screenX, screen.availLeft);
|
||||
let maxX =
|
||||
screen.availLeft +
|
||||
screen.availWidth -
|
||||
document.documentElement.clientWidth;
|
||||
window.moveTo(Math.min(desiredX, maxX), window.screenY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the primary display and moves the indicator at the bottom,
|
||||
* horizontally centered.
|
||||
*/
|
||||
centerOnPrimaryDisplay() {
|
||||
// This should be called in initialize right after we've just called
|
||||
// updateIndicatorState. Since updateIndicatorState uses
|
||||
// window.sizeToContent, the layout information should be up to date,
|
||||
// and so the numbers that we get without flushing should be sufficient.
|
||||
let {
|
||||
height: windowHeight,
|
||||
width: windowWidth,
|
||||
} = window.windowUtils.getBoundsWithoutFlushing(document.documentElement);
|
||||
|
||||
// The initial position of the fwindow should be at the bottom of the
|
||||
// primary screen, above any OS UI (like the task bar or dock), centered
|
||||
// horizontally.
|
||||
let screen = gScreenManager.primaryScreen;
|
||||
let scaleFactor = screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
|
||||
|
||||
let widthDevPix = {};
|
||||
screen.GetRectDisplayPix({}, {}, widthDevPix, {});
|
||||
let screenWidth = widthDevPix.value * scaleFactor;
|
||||
|
||||
let availTopDevPix = {};
|
||||
let availHeightDevPix = {};
|
||||
screen.GetAvailRectDisplayPix({}, availTopDevPix, {}, availHeightDevPix);
|
||||
|
||||
let availHeight =
|
||||
(availTopDevPix.value + availHeightDevPix.value) * scaleFactor;
|
||||
// To center the window, we subtract the window width from the screen
|
||||
// width, and divide by 2.
|
||||
//
|
||||
// To put the window at the bottom of the screen, just above any OS UI,
|
||||
// we subtract the window height from the available height.
|
||||
window.moveTo((screenWidth - windowWidth) / 2, availHeight - windowHeight);
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "load": {
|
||||
this.onLoad(event);
|
||||
break;
|
||||
}
|
||||
case "click": {
|
||||
this.onClick(event);
|
||||
break;
|
||||
}
|
||||
case "MozUpdateWindowPos": {
|
||||
if (!this.updatingIndicatorState) {
|
||||
// The window moved while not updating the indicator state,
|
||||
// so the user probably moved it.
|
||||
this.positionCustomized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loaded = true;
|
||||
|
||||
this.updateIndicatorState(true /* initialLayout */);
|
||||
this.centerOnPrimaryDisplay();
|
||||
|
||||
window.addEventListener("click", this);
|
||||
window.windowRoot.addEventListener("MozUpdateWindowPos", this);
|
||||
|
||||
// Alert accessibility implementations stuff just changed. We only need to do
|
||||
// this initially, because changes after this will automatically fire alert
|
||||
// events if things change materially.
|
||||
let ev = new CustomEvent("AlertActive", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
document.documentElement.dispatchEvent(ev);
|
||||
this.loaded = true;
|
||||
},
|
||||
|
||||
onClick(event) {
|
||||
switch (event.target.id) {
|
||||
case "stop-sharing-screen": {
|
||||
let activeStreams = webrtcUI.getActiveStreams(
|
||||
false /* camera */,
|
||||
false /* microphone */,
|
||||
true /* screen */,
|
||||
false /* window */
|
||||
);
|
||||
this.stopSharingScreen(activeStreams);
|
||||
break;
|
||||
}
|
||||
case "stop-sharing-window": {
|
||||
let activeStreams = webrtcUI.getActiveStreams(
|
||||
false /* camera */,
|
||||
false /* microphone */,
|
||||
false /* screen */,
|
||||
true /* window */
|
||||
);
|
||||
if (this.sharingBrowserWindow) {
|
||||
let browserWindowStreams = activeStreams.filter(stream => {
|
||||
return stream.devices.some(device => device.scary);
|
||||
});
|
||||
this.stopSharingScreen(browserWindowStreams);
|
||||
} else {
|
||||
this.stopSharingScreen(activeStreams);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "microphone-button":
|
||||
// Intentional fall-through
|
||||
case "camera-button": {
|
||||
// Revoking the microphone also revokes the camera and vice-versa.
|
||||
let activeStreams = webrtcUI.getActiveStreams(
|
||||
true /* camera */,
|
||||
true /* microphone */,
|
||||
false /* screen */
|
||||
);
|
||||
this.showSharingDoorhanger(activeStreams);
|
||||
break;
|
||||
}
|
||||
case "minimize": {
|
||||
window.minimize();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the most recent share in the set of active streams passed,
|
||||
* and ends it.
|
||||
*
|
||||
* @param activeStreams (Array<Object>)
|
||||
* An array of streams obtained via webrtcUI.getActiveStreams.
|
||||
* It is presumed that one or more of those streams includes
|
||||
* one that is sharing a screen or window.
|
||||
*/
|
||||
stopSharingScreen(activeStreams) {
|
||||
if (!activeStreams.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
function updateWindowAttr(attr, value) {
|
||||
// We'll default to choosing the most recent active stream to
|
||||
// revoke the permissions from.
|
||||
let chosenStream = activeStreams[activeStreams.length - 1];
|
||||
let { browser } = chosenStream;
|
||||
|
||||
// This intentionally copies its approach from browser-siteIdentity.js,
|
||||
// which powers the permission revocation from the Permissions Panel.
|
||||
// Ideally, we would de-duplicate this with a shared revocation mechanism,
|
||||
// but to lower the risk of uplifting this change, we keep it separate for
|
||||
// now.
|
||||
let gBrowser = browser.getTabBrowser();
|
||||
if (!gBrowser) {
|
||||
Cu.reportError("Can't stop sharing screen - cannot find gBrowser.");
|
||||
return;
|
||||
}
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (!tab) {
|
||||
Cu.reportError("Can't stop sharing screen - cannot find tab.");
|
||||
return;
|
||||
}
|
||||
|
||||
let permissions = SitePermissions.getAllPermissionDetailsForBrowser(
|
||||
browser
|
||||
);
|
||||
|
||||
let webrtcState = tab._sharingState.webRTC;
|
||||
let windowId = `screen:${webrtcState.windowId}`;
|
||||
// If WebRTC device or screen permissions are in use, we need to find
|
||||
// the associated permission item to set the sharingState field.
|
||||
if (webrtcState.screen) {
|
||||
let found = false;
|
||||
for (let permission of permissions) {
|
||||
if (permission.id != "screen") {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
permission.sharingState = webrtcState.screen;
|
||||
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: "screen",
|
||||
state: SitePermissions.ALLOW,
|
||||
scope: SitePermissions.SCOPE_REQUEST,
|
||||
sharingState: webrtcState.screen,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let permission = permissions.find(perm => {
|
||||
return perm.id == "screen";
|
||||
});
|
||||
|
||||
if (!permission) {
|
||||
Cu.reportError(
|
||||
"Can't stop sharing screen - cannot find screen permission."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let bc = webrtcState.browsingContext;
|
||||
bc.currentWindowGlobal
|
||||
.getActor("WebRTC")
|
||||
.sendAsyncMessage("webrtc:StopSharing", windowId);
|
||||
webrtcUI.forgetActivePermissionsFromBrowser(browser);
|
||||
|
||||
SitePermissions.removeFromPrincipal(
|
||||
browser.contentPrincipal,
|
||||
permission.id,
|
||||
browser
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the most recent share in the set of active streams passed,
|
||||
* and opens up the Permissions Panel for the associated tab to
|
||||
* let the user revoke the streaming permission.
|
||||
*
|
||||
* @param activeStreams (Array<Object>)
|
||||
* An array of streams obtained via webrtcUI.getActiveStreams.
|
||||
*/
|
||||
showSharingDoorhanger(activeStreams) {
|
||||
if (!activeStreams.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let index = activeStreams.length - 1;
|
||||
webrtcUI.showSharingDoorhanger(activeStreams[index]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates an attribute on the <window> element.
|
||||
*
|
||||
* @param attr (String)
|
||||
* The name of the attribute to update.
|
||||
* @param value (String?)
|
||||
* A string to set the attribute to. If the value is false-y,
|
||||
* the attribute is removed.
|
||||
*/
|
||||
updateWindowAttr(attr, value) {
|
||||
let docEl = document.documentElement;
|
||||
if (value) {
|
||||
docEl.setAttribute(attr, "true");
|
||||
} else {
|
||||
docEl.removeAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
function onPopupMenuShowing(event) {
|
||||
let popup = event.target;
|
||||
|
||||
let activeStreams;
|
||||
if (popup.getAttribute("type") == "Devices") {
|
||||
activeStreams = webrtcUI.getActiveStreams(true, true, false);
|
||||
} else {
|
||||
activeStreams = webrtcUI.getActiveStreams(false, false, true);
|
||||
}
|
||||
if (activeStreams.length) {
|
||||
let index = activeStreams.length - 1;
|
||||
webrtcUI.showSharingDoorhanger(activeStreams[index]);
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let stream of activeStreams) {
|
||||
let item = document.createElement("menuitem");
|
||||
item.setAttribute("label", stream.browser.contentTitle || stream.uri);
|
||||
item.setAttribute("tooltiptext", stream.uri);
|
||||
item.stream = stream;
|
||||
popup.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
function onPopupMenuHiding(event) {
|
||||
let popup = event.target;
|
||||
while (popup.firstChild) {
|
||||
popup.firstChild.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function onPopupMenuCommand(event) {
|
||||
webrtcUI.showSharingDoorhanger(event.target.stream);
|
||||
}
|
||||
|
||||
function onFirefoxButtonClick(event) {
|
||||
event.target.blur();
|
||||
let activeStreams = webrtcUI.getActiveStreams(true, true, true);
|
||||
activeStreams[0].browser.ownerGlobal.focus();
|
||||
}
|
||||
|
||||
var PositionHandler = {
|
||||
positionCustomized: false,
|
||||
threshold: 10,
|
||||
adjustPosition() {
|
||||
if (!this.positionCustomized) {
|
||||
// Center the window horizontally on the screen (not the available area).
|
||||
// Until we have moved the window to y=0, 'screen.width' may give a value
|
||||
// for a secondary screen, so use values from the screen manager instead.
|
||||
let primaryScreen = Cc["@mozilla.org/gfx/screenmanager;1"].getService(
|
||||
Ci.nsIScreenManager
|
||||
).primaryScreen;
|
||||
let widthDevPix = {};
|
||||
primaryScreen.GetRect({}, {}, widthDevPix, {});
|
||||
let availTopDevPix = {};
|
||||
primaryScreen.GetAvailRect({}, availTopDevPix, {}, {});
|
||||
let scaleFactor = primaryScreen.defaultCSSScaleFactor;
|
||||
let widthCss = widthDevPix.value / scaleFactor;
|
||||
window.moveTo(
|
||||
(widthCss - document.documentElement.clientWidth) / 2,
|
||||
availTopDevPix.value / scaleFactor
|
||||
);
|
||||
} else {
|
||||
// This will ensure we're at y=0.
|
||||
this.setXPosition(window.screenX);
|
||||
}
|
||||
},
|
||||
setXPosition(desiredX) {
|
||||
// Ensure the indicator isn't moved outside the available area of the screen.
|
||||
desiredX = Math.max(desiredX, screen.availLeft);
|
||||
let maxX =
|
||||
screen.availLeft +
|
||||
screen.availWidth -
|
||||
document.documentElement.clientWidth;
|
||||
window.moveTo(Math.min(desiredX, maxX), screen.availTop);
|
||||
},
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "mousedown":
|
||||
if (aEvent.button != 0 || aEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._startMouseX = aEvent.screenX;
|
||||
this._startWindowX = window.screenX;
|
||||
this._deltaX = this._startMouseX - this._startWindowX;
|
||||
|
||||
window.addEventListener("mousemove", this);
|
||||
window.addEventListener("mouseup", this);
|
||||
break;
|
||||
|
||||
case "mousemove":
|
||||
let moveOffset = Math.abs(aEvent.screenX - this._startMouseX);
|
||||
if (this._dragFullyStarted || moveOffset > this.threshold) {
|
||||
this.setXPosition(aEvent.screenX - this._deltaX);
|
||||
this._dragFullyStarted = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "mouseup":
|
||||
this._dragFullyStarted = false;
|
||||
window.removeEventListener("mousemove", this);
|
||||
window.removeEventListener("mouseup", this);
|
||||
this.positionCustomized =
|
||||
Math.abs(this._startWindowX - window.screenX) >= this.threshold;
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
WebRTCIndicator.init();
|
||||
|
|
|
@ -7,29 +7,42 @@
|
|||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/webRTC-indicator.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<window xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="webrtcIndicator"
|
||||
role="alert"
|
||||
windowtype="Browser:WebRTCGlobalIndicator"
|
||||
onload="init(event);"
|
||||
#ifdef XP_MACOSX
|
||||
inwindowmenu="false"
|
||||
#endif
|
||||
sizemode="normal"
|
||||
hidechrome="true"
|
||||
orient="horizontal"
|
||||
>
|
||||
<script src="chrome://browser/content/webrtcIndicator.js"/>
|
||||
chromemargin="0,0,0,0">
|
||||
|
||||
<button id="firefoxButton"/>
|
||||
<button id="audioVideoButton" type="menu">
|
||||
<menupopup id="audioVideoPopup" type="Devices"/>
|
||||
</button>
|
||||
<separator id="shareSeparator"/>
|
||||
<button id="screenShareButton" type="menu">
|
||||
<menupopup id="screenSharePopup" type="Screen"/>
|
||||
</button>
|
||||
</window>
|
||||
<head>
|
||||
<link rel="localization" href="branding/brand.ftl"/>
|
||||
<link rel="localization" href="preview/webrtcIndicator.ftl"/>
|
||||
#ifndef XP_MACOSX
|
||||
<title data-l10n-id="webrtc-indicator-title"></title>
|
||||
#endif
|
||||
<script src="chrome://browser/content/webrtcIndicator.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="display-share" class="row-item">
|
||||
<image id="display-share-icon" />
|
||||
|
||||
<span id="window-share-info" data-l10n-id="webrtc-sharing-window"/>
|
||||
<span id="browser-window-share-info" data-l10n-id="webrtc-sharing-browser-window"/>
|
||||
<button id="stop-sharing-window" class="stop-button" data-l10n-id="webrtc-stop-sharing-button"/>
|
||||
|
||||
<span id="screen-share-info" data-l10n-id="webrtc-sharing-screen"/>
|
||||
<button id="stop-sharing-screen" class="stop-button" data-l10n-id="webrtc-stop-sharing-button"/>
|
||||
</div>
|
||||
<button id="microphone-button" class="row-item control-icon" type="microphone" data-l10n-id="webrtc-microphone-button"/>
|
||||
<button id="camera-button" class="row-item control-icon" type="camera" data-l10n-id="webrtc-camera-button"/>
|
||||
<div id="window-controls" class="row-item">
|
||||
<button id="minimize" class="control-icon" data-l10n-id="webrtc-minimize"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
preview/protections.ftl (../components/protections/content/protections.ftl)
|
||||
preview/interventions.ftl (../components/urlbar/content/interventions.ftl)
|
||||
preview/popup-notifications.ftl (../base/content/popup-notifications.ftl)
|
||||
preview/webrtcIndicator.ftl (../base/content/webrtcIndicator.ftl)
|
||||
browser (%browser/**/*.ftl)
|
||||
|
||||
@AB_CD@.jar:
|
||||
|
|
|
@ -562,7 +562,12 @@ function getGlobalIndicator() {
|
|||
if (!webrtcUI.useLegacyGlobalIndicator) {
|
||||
const INDICATOR_CHROME_URI =
|
||||
"chrome://browser/content/webrtcIndicator.xhtml";
|
||||
const features = "chrome,dialog=yes,titlebar=no,popup=yes";
|
||||
let features = "chrome,titlebar=yes,alwaysontop,minimizable=yes";
|
||||
|
||||
/* Don't use dialog on Gtk as it adds extra border and titlebar to indicator */
|
||||
if (!AppConstants.MOZ_WIDGET_GTK) {
|
||||
features += ",dialog=yes";
|
||||
}
|
||||
|
||||
return Services.ww.openWindow(
|
||||
null,
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
skin/classic/browser/notification-icons/microphone-blocked.svg (../shared/notification-icons/microphone-blocked.svg)
|
||||
skin/classic/browser/notification-icons/microphone-detailed.svg (../shared/notification-icons/microphone-detailed.svg)
|
||||
skin/classic/browser/notification-icons/microphone.svg (../shared/notification-icons/microphone.svg)
|
||||
skin/classic/browser/notification-icons/minimize.svg (../shared/notification-icons/minimize.svg)
|
||||
skin/classic/browser/notification-icons/persistent-storage-blocked.svg (../shared/notification-icons/persistent-storage-blocked.svg)
|
||||
skin/classic/browser/notification-icons/persistent-storage.svg (../shared/notification-icons/persistent-storage.svg)
|
||||
skin/classic/browser/notification-icons/plugin-badge.svg (../shared/notification-icons/plugin-badge.svg)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M3 12h10v2H3z"/></svg>
|
После Ширина: | Высота: | Размер: 362 B |
|
@ -2,121 +2,125 @@
|
|||
* 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/. */
|
||||
|
||||
window {
|
||||
border: 1px solid #ff9500;
|
||||
:root {
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
--indicator-height: 60px;
|
||||
--indicator-background-color: rgb(249,249,250);
|
||||
--indicator-icon-fill: rgba(12,12,13,0.8);
|
||||
--indicator-item-separator: 1px solid hsla(210,4%,10%,.14);
|
||||
--indicator-stop-button-background-color: rgb(0,96,223);
|
||||
--indicator-stop-button-hover-background-color: rgb(0,62,170);
|
||||
--indicator-stop-button-color: rgb(255,255,255);
|
||||
--minimize-background-color: rgb(215,215,219);
|
||||
--control-icon-hover-background-color: rgb(215,215,219);
|
||||
}
|
||||
|
||||
#audioVideoButton,
|
||||
#screenShareButton,
|
||||
#firefoxButton {
|
||||
height: 29px;
|
||||
body {
|
||||
display: inline-flex;
|
||||
background-color: var(--indicator-background-color);
|
||||
max-height: var(--indicator-height);
|
||||
margin: 0;
|
||||
user-select: none;
|
||||
-moz-window-dragging: drag;
|
||||
}
|
||||
|
||||
button {
|
||||
-moz-appearance: none;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
#audioVideoButton,
|
||||
#screenShareButton {
|
||||
margin: 0;
|
||||
-moz-context-properties: fill;
|
||||
fill: white;
|
||||
}
|
||||
|
||||
#firefoxButton {
|
||||
background-image: url("chrome://branding/content/icon48.png");
|
||||
fill: var(--indicator-icon-fill);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 22px;
|
||||
-moz-window-dragging: no-drag;
|
||||
}
|
||||
|
||||
.row-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 50px;
|
||||
height: var(--indicator-height);
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.row-item:not(:last-child) {
|
||||
border-right: var(--indicator-item-separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* For display sharing, if we happen to be sharing both
|
||||
* a window and a screen, we want to show the UI for sharing
|
||||
* the screen, since that's the more privacy-sensitive one.
|
||||
*/
|
||||
:root[sharingwindow]:not([sharingscreen]) > body > #display-share > #screen-share-info,
|
||||
:root[sharingwindow]:not([sharingscreen]) > body > #display-share > #stop-sharing-screen,
|
||||
:root[sharingwindow]:not([sharingbrowserwindow]) > body > #display-share > #browser-window-share-info,
|
||||
:root[sharingwindow][sharingbrowserwindow] > body > #display-share > #window-share-info,
|
||||
:root[sharingscreen] > body > #display-share > #window-share-info,
|
||||
:root[sharingscreen] > body > #display-share > #browser-window-share-info,
|
||||
:root[sharingscreen] > body > #display-share > #stop-sharing-window,
|
||||
/**
|
||||
* If we're not sharing either the screen or the window, we can
|
||||
* hide the entire display sharing section.
|
||||
*/
|
||||
:root:not(:-moz-any([sharingscreen],[sharingwindow])) > body > #display-share,
|
||||
:root:not([sharingvideo]) > body > #camera-button,
|
||||
:root:not([sharingaudio]) > body > #microphone-button {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.control-icon {
|
||||
background-position: center center;
|
||||
min-width: 29px;
|
||||
background-color: white;
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
#firefoxButton:hover {
|
||||
background-color: #f2f2f2;
|
||||
.control-icon:hover {
|
||||
background-color: var(--control-icon-hover-background-color);
|
||||
}
|
||||
|
||||
#screenShareButton {
|
||||
#display-share-icon {
|
||||
background-image: url("chrome://browser/skin/notification-icons/screen.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px;
|
||||
min-width: 27px;
|
||||
display: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-inline-start: 5px;
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
|
||||
window[sharingscreen] > #screenShareButton {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
#audioVideoButton {
|
||||
display: none;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* When screen sharing, need to pull in the separator: */
|
||||
window[sharingscreen] > #audioVideoButton {
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
/* Single icon button: */
|
||||
window[sharingvideo] > #audioVideoButton,
|
||||
window[sharingaudio] > #audioVideoButton {
|
||||
display: -moz-box;
|
||||
background-position: center center;
|
||||
background-size: 16px;
|
||||
min-width: 26px;
|
||||
}
|
||||
|
||||
window[sharingvideo] > #audioVideoButton {
|
||||
#camera-button {
|
||||
background-image: url("chrome://browser/skin/notification-icons/camera.svg");
|
||||
}
|
||||
|
||||
window[sharingaudio] > #audioVideoButton {
|
||||
#microphone-button {
|
||||
background-image: url("chrome://browser/skin/notification-icons/microphone.svg");
|
||||
}
|
||||
|
||||
/* Multi-icon button: */
|
||||
window[sharingaudio][sharingvideo] > #audioVideoButton {
|
||||
background-image: url("chrome://browser/skin/notification-icons/camera.svg"),
|
||||
url("chrome://browser/skin/notification-icons/microphone.svg");
|
||||
background-position: 6px center, 26px center;
|
||||
background-size: 16px, 16px;
|
||||
min-width: 46px;
|
||||
.stop-button {
|
||||
background-color: var(--indicator-stop-button-background-color);
|
||||
color: var(--indicator-stop-button-color);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin-inline-start: 15px;
|
||||
}
|
||||
|
||||
/* Hover styles */
|
||||
#audioVideoButton,
|
||||
#screenShareButton {
|
||||
background-color: #ffaa33;
|
||||
.stop-button:hover {
|
||||
background-color: var(--indicator-stop-button-hover-background-color);
|
||||
}
|
||||
|
||||
#audioVideoButton:hover,
|
||||
#screenShareButton:hover {
|
||||
background-color: #ff9500;
|
||||
#window-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: self-end;
|
||||
}
|
||||
|
||||
/* Don't show the dropmarker for the type="menu" case */
|
||||
#audioVideoButton > .box-inherit > .button-menu-dropmarker,
|
||||
#screenShareButton > .box-inherit > .button-menu-dropmarker {
|
||||
display: none;
|
||||
#minimize {
|
||||
padding: 10px;
|
||||
min-width: unset;
|
||||
background-image: url("chrome://browser/skin/notification-icons/minimize.svg");
|
||||
}
|
||||
|
||||
/* Separator in case of screen sharing + video/audio sharing */
|
||||
#shareSeparator {
|
||||
width: 1px;
|
||||
margin: 4px -1px 4px 0;
|
||||
background-color: #FFCA80;
|
||||
/* Separator needs to show above either button when they're hovered: */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
}
|
||||
|
||||
window[sharingscreen][sharingvideo] > #shareSeparator,
|
||||
window[sharingscreen][sharingaudio] > #shareSeparator {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
:-moz-any(#audioVideoButton, #screenShareButton,
|
||||
#firefoxButton):-moz-focusring {
|
||||
:-moz-any(#microphone-button, #camera-button, .stop-button):-moz-focusring {
|
||||
outline: none;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче