2014-07-19 04:49:19 +04:00
|
|
|
/* 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/. */
|
|
|
|
|
2019-01-17 21:18:31 +03:00
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
2020-05-18 21:57:40 +03:00
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
|
|
);
|
2019-01-17 21:18:31 +03:00
|
|
|
const { webrtcUI } = ChromeUtils.import("resource:///modules/webrtcUI.jsm");
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-05-26 21:00:36 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"AppConstants",
|
|
|
|
"resource://gre/modules/AppConstants.jsm"
|
|
|
|
);
|
|
|
|
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"MacOSWebRTCStatusbarIndicator",
|
|
|
|
"resource:///modules/webrtcUI.jsm"
|
|
|
|
);
|
|
|
|
|
2020-06-16 16:19:16 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"BrowserWindowTracker",
|
|
|
|
"resource:///modules/BrowserWindowTracker.jsm"
|
|
|
|
);
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"PluralForm",
|
|
|
|
"resource://gre/modules/PluralForm.jsm"
|
|
|
|
);
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(
|
|
|
|
this,
|
|
|
|
"gScreenManager",
|
|
|
|
"@mozilla.org/gfx/screenmanager;1",
|
|
|
|
"nsIScreenManager"
|
|
|
|
);
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
/**
|
|
|
|
* Public function called by webrtcUI to update the indicator
|
|
|
|
* display when the active streams change.
|
|
|
|
*/
|
2014-07-19 04:49:19 +04:00
|
|
|
function updateIndicatorState() {
|
2020-05-18 21:57:40 +03:00
|
|
|
WebRTCIndicator.updateIndicatorState();
|
2014-07-19 04:49:19 +04:00
|
|
|
}
|
|
|
|
|
2020-07-17 04:22:10 +03:00
|
|
|
/**
|
|
|
|
* Public function called by webrtcUI to indicate that webrtcUI
|
|
|
|
* is about to close the indicator. This is so that we can differentiate
|
|
|
|
* between closes that are caused by webrtcUI, and closes that are
|
|
|
|
* caused by other reasons (like the user closing the window via the
|
|
|
|
* OS window controls somehow).
|
|
|
|
*
|
|
|
|
* If the window is closed without having called this method first, the
|
|
|
|
* indicator will ask webrtcUI to shutdown any remaining streams and then
|
|
|
|
* select and focus the most recent browser tab that a stream was shared
|
|
|
|
* with.
|
|
|
|
*/
|
|
|
|
function closingInternally() {
|
|
|
|
WebRTCIndicator.closingInternally();
|
|
|
|
}
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
/**
|
|
|
|
* Main control object for the WebRTC global indicator
|
|
|
|
*/
|
|
|
|
const WebRTCIndicator = {
|
|
|
|
init(event) {
|
|
|
|
addEventListener("load", this);
|
2020-05-26 21:00:36 +03:00
|
|
|
addEventListener("unload", this);
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
// 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;
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
this.updatingIndicatorState = false;
|
|
|
|
this.loaded = false;
|
2020-07-17 04:22:10 +03:00
|
|
|
this.isClosingInternally = false;
|
2020-05-26 21:00:36 +03:00
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
this.statusBar = null;
|
|
|
|
this.statusBarMenus = new Set();
|
2020-09-29 09:51:43 +03:00
|
|
|
|
2020-10-01 00:14:10 +03:00
|
|
|
this.showGlobalMuteToggles = Services.prefs.getBoolPref(
|
|
|
|
"privacy.webrtc.globalMuteToggles",
|
|
|
|
false
|
|
|
|
);
|
|
|
|
|
|
|
|
this.hideGlobalIndicator = Services.prefs.getBoolPref(
|
|
|
|
"privacy.webrtc.hideGlobalIndicator",
|
|
|
|
false
|
|
|
|
);
|
|
|
|
|
|
|
|
if (this.hideGlobalIndicator) {
|
|
|
|
this.setVisibility(false);
|
2020-10-01 00:08:50 +03:00
|
|
|
}
|
2020-09-30 18:15:58 +03:00
|
|
|
},
|
|
|
|
|
2020-10-01 00:14:10 +03:00
|
|
|
/**
|
|
|
|
* Controls the visibility of the global indicator. Also sets the value of
|
|
|
|
* a "visible" attribute on the document element to "true" or "false".
|
|
|
|
*
|
|
|
|
* @param isVisible (boolean)
|
|
|
|
* Whether or not the global indicator should be visible.
|
|
|
|
*/
|
|
|
|
setVisibility(isVisible) {
|
|
|
|
let baseWin = window.docShell.treeOwner.QueryInterface(Ci.nsIBaseWindow);
|
|
|
|
baseWin.visibility = isVisible;
|
|
|
|
// AppWindow::GetVisibility _always_ returns true (see
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=306245), so we'll set an
|
|
|
|
// attribute on the document to make it easier for tests to know that the
|
|
|
|
// indicator is not visible.
|
|
|
|
document.documentElement.setAttribute("visible", isVisible);
|
|
|
|
// This will hide the indicator from the Window menu on macOS when
|
|
|
|
// not visible.
|
|
|
|
document.documentElement.setAttribute("inwindowmenu", isVisible);
|
|
|
|
},
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
/**
|
|
|
|
* Exposed externally so that webrtcUI can alert the indicator to
|
|
|
|
* update itself when sharing states have changed.
|
|
|
|
*/
|
2020-08-11 13:03:07 +03:00
|
|
|
updateIndicatorState() {
|
2020-05-18 21:57:40 +03:00
|
|
|
// 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;
|
|
|
|
}
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
// 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;
|
2014-07-19 04:49:19 +04:00
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
let showCameraIndicator = webrtcUI.showCameraIndicator;
|
|
|
|
let showMicrophoneIndicator = webrtcUI.showMicrophoneIndicator;
|
|
|
|
let showScreenSharingIndicator = webrtcUI.showScreenSharingIndicator;
|
|
|
|
if (this.statusBar) {
|
|
|
|
let statusMenus = new Map([
|
|
|
|
["Camera", showCameraIndicator],
|
|
|
|
["Microphone", showMicrophoneIndicator],
|
|
|
|
["Screen", showScreenSharingIndicator],
|
|
|
|
]);
|
|
|
|
|
|
|
|
for (let [name, shouldShow] of statusMenus) {
|
|
|
|
let menu = document.getElementById(`webRTC-sharing${name}-menu`);
|
|
|
|
if (shouldShow && !this.statusBarMenus.has(menu)) {
|
|
|
|
this.statusBar.addItem(menu);
|
|
|
|
this.statusBarMenus.add(menu);
|
|
|
|
} else if (!shouldShow && this.statusBarMenus.has(menu)) {
|
|
|
|
this.statusBar.removeItem(menu);
|
|
|
|
this.statusBarMenus.delete(menu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-30 18:15:58 +03:00
|
|
|
|
2020-10-01 00:14:10 +03:00
|
|
|
if (!this.showGlobalMuteToggles && !webrtcUI.showScreenSharingIndicator) {
|
|
|
|
this.setVisibility(false);
|
|
|
|
} else if (!this.hideGlobalIndicator) {
|
|
|
|
this.setVisibility(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.showGlobalMuteToggles) {
|
|
|
|
this.updateWindowAttr("sharingvideo", showCameraIndicator);
|
|
|
|
this.updateWindowAttr("sharingaudio", showMicrophoneIndicator);
|
|
|
|
}
|
2020-10-01 00:14:07 +03:00
|
|
|
|
|
|
|
let sharingScreen = showScreenSharingIndicator.startsWith("Screen");
|
2020-06-16 14:20:57 +03:00
|
|
|
this.updateWindowAttr("sharingscreen", sharingScreen);
|
2014-08-01 17:15:49 +04:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
// 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.
|
2020-10-01 00:14:07 +03:00
|
|
|
let sharingWindow = showScreenSharingIndicator.startsWith("Window");
|
2020-05-18 21:57:40 +03:00
|
|
|
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 */
|
2016-03-04 19:12:40 +03:00
|
|
|
);
|
2020-05-18 21:57:40 +03:00
|
|
|
let hasBrowserWindow = activeStreams.some(stream => {
|
|
|
|
return stream.devices.some(device => device.scary);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.updateWindowAttr("sharingbrowserwindow", hasBrowserWindow);
|
|
|
|
this.sharingBrowserWindow = hasBrowserWindow;
|
2014-08-01 17:15:49 +04:00
|
|
|
} else {
|
2020-05-18 21:57:40 +03:00
|
|
|
this.updateWindowAttr("sharingbrowserwindow");
|
|
|
|
this.sharingBrowserWindow = false;
|
2014-08-01 17:15:49 +04:00
|
|
|
}
|
2020-05-18 21:57:40 +03:00
|
|
|
|
2020-06-16 14:20:57 +03:00
|
|
|
// The label that's displayed when sharing a display followed a priority.
|
|
|
|
// The more "risky" we deem the display is for sharing, the higher priority.
|
|
|
|
// This gives us the following priorities, from highest to lowest.
|
|
|
|
//
|
|
|
|
// 1. Screen
|
|
|
|
// 2. Browser window
|
|
|
|
// 3. Other application window
|
|
|
|
// 4. Browser tab (unimplemented)
|
|
|
|
//
|
|
|
|
// The CSS for the indicator does the work of showing or hiding these labels
|
|
|
|
// for us, but we need to update the aria-labelledby attribute on the container
|
|
|
|
// of those labels to make it clearer for screenreaders which one the user cares
|
|
|
|
// about.
|
|
|
|
let displayShare = document.getElementById("display-share");
|
|
|
|
let labelledBy;
|
|
|
|
if (sharingScreen) {
|
|
|
|
labelledBy = "screen-share-info";
|
|
|
|
} else if (this.sharingBrowserWindow) {
|
|
|
|
labelledBy = "browser-window-share-info";
|
|
|
|
} else if (sharingWindow) {
|
|
|
|
labelledBy = "window-share-info";
|
|
|
|
}
|
|
|
|
displayShare.setAttribute("aria-labelledby", labelledBy);
|
|
|
|
|
2020-07-08 12:34:39 +03:00
|
|
|
if (window.windowState != window.STATE_MINIMIZED) {
|
|
|
|
// Resize and ensure the window position is correct
|
|
|
|
// (sizeToContent messes with our position).
|
|
|
|
let docElStyle = document.documentElement.style;
|
|
|
|
docElStyle.minWidth = docElStyle.maxWidth = "unset";
|
|
|
|
docElStyle.minHeight = docElStyle.maxHeight = "unset";
|
|
|
|
window.sizeToContent();
|
|
|
|
|
|
|
|
// On Linux GTK, the style of window we're using by default is resizable. We
|
|
|
|
// workaround this by setting explicit limits on the height and width of the
|
|
|
|
// window.
|
|
|
|
if (AppConstants.platform == "linux") {
|
|
|
|
let { width, height } = window.windowUtils.getBoundsWithoutFlushing(
|
|
|
|
document.documentElement
|
|
|
|
);
|
|
|
|
|
|
|
|
docElStyle.minWidth = docElStyle.maxWidth = `${width}px`;
|
|
|
|
docElStyle.minHeight = docElStyle.maxHeight = `${height}px`;
|
|
|
|
}
|
2020-06-17 20:04:51 +03:00
|
|
|
|
2020-07-08 12:34:39 +03:00
|
|
|
this.ensureOnScreen();
|
2020-05-18 21:57:40 +03:00
|
|
|
|
2020-07-08 12:34:39 +03:00
|
|
|
if (!this.positionCustomized) {
|
2020-08-11 13:03:07 +03:00
|
|
|
this.centerOnLatestBrowser();
|
2020-07-08 12:34:39 +03:00
|
|
|
}
|
2020-05-18 21:57:40 +03:00
|
|
|
}
|
2020-07-08 12:34:39 +03:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
this.updatingIndicatorState = false;
|
2014-08-01 17:15:49 +04:00
|
|
|
},
|
2020-05-18 21:57:40 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2014-08-01 17:15:49 +04:00
|
|
|
let maxX =
|
|
|
|
screen.availLeft +
|
|
|
|
screen.availWidth -
|
|
|
|
document.documentElement.clientWidth;
|
2020-05-18 21:57:40 +03:00
|
|
|
window.moveTo(Math.min(desiredX, maxX), window.screenY);
|
2014-08-01 17:15:49 +04:00
|
|
|
},
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
/**
|
2020-08-11 13:03:07 +03:00
|
|
|
* If the indicator is first being opened, we'll find the browser window
|
|
|
|
* associated with the most recent share, and pin the indicator to the
|
|
|
|
* very top of the content area.
|
2020-05-18 21:57:40 +03:00
|
|
|
*/
|
2020-08-11 13:03:07 +03:00
|
|
|
centerOnLatestBrowser() {
|
|
|
|
let activeStreams = webrtcUI.getActiveStreams(
|
|
|
|
true /* camera */,
|
|
|
|
true /* microphone */,
|
|
|
|
true /* screen */,
|
|
|
|
true /* window */
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!activeStreams.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let browser = activeStreams[activeStreams.length - 1].browser;
|
|
|
|
let browserWindow = browser.ownerGlobal;
|
|
|
|
let browserRect = browserWindow.windowUtils.getBoundsWithoutFlushing(
|
|
|
|
browser
|
|
|
|
);
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
// 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.
|
2020-08-11 13:03:07 +03:00
|
|
|
let { width: windowWidth } = window.windowUtils.getBoundsWithoutFlushing(
|
|
|
|
document.documentElement
|
|
|
|
);
|
2020-06-16 16:19:16 +03:00
|
|
|
|
2020-06-12 21:59:59 +03:00
|
|
|
window.moveTo(
|
2020-08-11 13:03:07 +03:00
|
|
|
browserWindow.mozInnerScreenX +
|
|
|
|
browserRect.left +
|
|
|
|
(browserRect.width - windowWidth) / 2,
|
|
|
|
browserWindow.mozInnerScreenY + browserRect.top
|
2020-06-12 21:59:59 +03:00
|
|
|
);
|
2020-05-18 21:57:40 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent(event) {
|
|
|
|
switch (event.type) {
|
|
|
|
case "load": {
|
2020-05-26 21:00:36 +03:00
|
|
|
this.onLoad();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "unload": {
|
|
|
|
this.onUnload();
|
2014-08-01 17:15:49 +04:00
|
|
|
break;
|
2020-05-18 21:57:40 +03:00
|
|
|
}
|
|
|
|
case "click": {
|
|
|
|
this.onClick(event);
|
|
|
|
break;
|
|
|
|
}
|
2020-08-27 20:03:43 +03:00
|
|
|
case "change": {
|
|
|
|
this.onChange(event);
|
|
|
|
break;
|
|
|
|
}
|
2020-05-18 21:57:40 +03:00
|
|
|
case "MozUpdateWindowPos": {
|
|
|
|
if (!this.updatingIndicatorState) {
|
|
|
|
// The window moved while not updating the indicator state,
|
|
|
|
// so the user probably moved it.
|
|
|
|
this.positionCustomized = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-07-08 12:34:39 +03:00
|
|
|
case "sizemodechange": {
|
|
|
|
if (window.windowState != window.STATE_MINIMIZED) {
|
|
|
|
this.updateIndicatorState();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-10-01 00:14:07 +03:00
|
|
|
case "popupshowing": {
|
|
|
|
this.onPopupShowing(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "popuphiding": {
|
|
|
|
this.onPopupHiding(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "command": {
|
|
|
|
this.onCommand(event);
|
|
|
|
break;
|
|
|
|
}
|
2020-10-27 19:28:25 +03:00
|
|
|
case "DOMWindowClose":
|
|
|
|
case "close": {
|
|
|
|
this.onClose(event);
|
|
|
|
break;
|
|
|
|
}
|
2020-05-18 21:57:40 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onLoad() {
|
|
|
|
this.loaded = true;
|
2014-08-01 17:15:49 +04:00
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
if (AppConstants.platform == "macosx" || AppConstants.platform == "win") {
|
|
|
|
this.statusBar = Cc["@mozilla.org/widget/systemstatusbar;1"].getService(
|
|
|
|
Ci.nsISystemStatusBar
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-11 13:03:07 +03:00
|
|
|
this.updateIndicatorState();
|
2020-05-18 21:57:40 +03:00
|
|
|
|
|
|
|
window.addEventListener("click", this);
|
2020-08-27 20:03:43 +03:00
|
|
|
window.addEventListener("change", this);
|
2020-07-08 12:34:39 +03:00
|
|
|
window.addEventListener("sizemodechange", this);
|
2020-10-01 00:14:07 +03:00
|
|
|
|
2020-10-27 19:28:25 +03:00
|
|
|
// There are two ways that the dialog can close - either via the
|
|
|
|
// .close() window method, or via the OS. We handle both of those
|
|
|
|
// cases here.
|
|
|
|
window.addEventListener("DOMWindowClose", this);
|
|
|
|
window.addEventListener("close", this);
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
if (this.statusBar) {
|
|
|
|
// We only want these events for the system status bar menus.
|
|
|
|
window.addEventListener("popupshowing", this);
|
|
|
|
window.addEventListener("popuphiding", this);
|
|
|
|
window.addEventListener("command", this);
|
|
|
|
}
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
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);
|
2020-05-26 21:00:36 +03:00
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
this.loaded = true;
|
|
|
|
},
|
|
|
|
|
2020-10-27 19:28:25 +03:00
|
|
|
onClose(event) {
|
|
|
|
// This event is fired from when the indicator window tries to be closed.
|
|
|
|
// If we preventDefault() the event, we are able to cancel that close
|
|
|
|
// attempt.
|
|
|
|
//
|
|
|
|
// We want to do that if we're not showing the global mute toggles
|
|
|
|
// and we're still sharing a camera or a microphone so that we can
|
|
|
|
// keep the status bar indicators present (since those status bar
|
|
|
|
// indicators are bound to this window).
|
|
|
|
if (
|
|
|
|
!this.showGlobalMuteToggles &&
|
|
|
|
(webrtcUI.showCameraIndicator || webrtcUI.showMicrophoneIndicator)
|
|
|
|
) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.setVisibility(false);
|
2020-07-17 04:22:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.isClosingInternally) {
|
2020-10-27 19:28:25 +03:00
|
|
|
// Something has tried to close the indicator, but it wasn't webrtcUI.
|
|
|
|
// This means we might still have some streams being shared. To protect
|
2020-07-17 04:22:10 +03:00
|
|
|
// the user from unknowingly sharing streams, we shut those streams
|
|
|
|
// down.
|
2020-10-27 19:28:25 +03:00
|
|
|
//
|
|
|
|
// This only includes the camera and microphone streams if the user
|
|
|
|
// has the global mute toggles enabled, since these toggles visually
|
|
|
|
// associate the indicator with those streams.
|
2020-07-17 04:22:10 +03:00
|
|
|
let activeStreams = webrtcUI.getActiveStreams(
|
2020-10-27 19:28:25 +03:00
|
|
|
this.showGlobalMuteToggles /* camera */,
|
|
|
|
this.showGlobalMuteToggles /* microphone */,
|
2020-07-17 04:22:10 +03:00
|
|
|
true /* screen */,
|
|
|
|
true /* window */
|
|
|
|
);
|
2020-10-27 19:28:25 +03:00
|
|
|
webrtcUI.stopSharingStreams(
|
|
|
|
activeStreams,
|
|
|
|
this.showGlobalMuteToggles /* camera */,
|
|
|
|
this.showGlobalMuteToggles /* microphone */,
|
|
|
|
true /* screen */,
|
|
|
|
true /* window */
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onUnload() {
|
|
|
|
Services.ppmm.sharedData.set("WebRTC:GlobalCameraMute", false);
|
|
|
|
Services.ppmm.sharedData.set("WebRTC:GlobalMicrophoneMute", false);
|
|
|
|
Services.ppmm.sharedData.flush();
|
|
|
|
|
|
|
|
if (this.statusBar) {
|
|
|
|
for (let menu of this.statusBarMenus) {
|
|
|
|
this.statusBar.removeItem(menu);
|
|
|
|
}
|
2020-05-26 21:00:36 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
onClick(event) {
|
|
|
|
switch (event.target.id) {
|
2020-08-05 06:38:19 +03:00
|
|
|
case "stop-sharing": {
|
2020-05-18 21:57:40 +03:00
|
|
|
let activeStreams = webrtcUI.getActiveStreams(
|
|
|
|
false /* camera */,
|
|
|
|
false /* microphone */,
|
|
|
|
true /* screen */,
|
2020-08-05 01:00:42 +03:00
|
|
|
true /* window */
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!activeStreams.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// getActiveStreams is filtering for streams that have screen
|
|
|
|
// sharing, but those streams might _also_ be sharing other
|
|
|
|
// devices like camera or microphone. This is why we need to
|
|
|
|
// tell stopSharingStreams explicitly which device type we want
|
|
|
|
// to stop.
|
|
|
|
webrtcUI.stopSharingStreams(
|
2020-08-05 06:38:19 +03:00
|
|
|
activeStreams,
|
2020-08-05 01:00:42 +03:00
|
|
|
false /* camera */,
|
|
|
|
false /* microphone */,
|
2020-08-05 06:38:19 +03:00
|
|
|
true /* screen */,
|
2020-07-24 18:14:17 +03:00
|
|
|
true /* window */
|
|
|
|
);
|
2014-08-01 17:15:49 +04:00
|
|
|
break;
|
2020-05-18 21:57:40 +03:00
|
|
|
}
|
2020-08-27 18:39:49 +03:00
|
|
|
case "minimize": {
|
|
|
|
window.minimize();
|
2020-08-27 00:31:29 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-08-27 20:03:43 +03:00
|
|
|
onChange(event) {
|
|
|
|
switch (event.target.id) {
|
|
|
|
case "microphone-mute-toggle": {
|
|
|
|
this.toggleMicrophoneMute(event.target);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "camera-mute-toggle": {
|
|
|
|
this.toggleCameraMute(event.target);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
onPopupShowing(event) {
|
2020-10-05 12:23:40 +03:00
|
|
|
if (!this.eventIsForDeviceMenuPopup(event)) {
|
2020-10-01 00:14:07 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-05 12:23:40 +03:00
|
|
|
let menupopup = event.target;
|
|
|
|
let type = menupopup.getAttribute("type");
|
|
|
|
|
2020-10-01 00:14:10 +03:00
|
|
|
// When the indicator is hidden by default, opening the menu from the
|
|
|
|
// system tray _might_ cause the indicator to try to become visible again.
|
|
|
|
// We work around this by re-hiding it if it wasn't already visible.
|
|
|
|
if (document.documentElement.getAttribute("visible") != "true") {
|
|
|
|
let baseWin = window.docShell.treeOwner.QueryInterface(Ci.nsIBaseWindow);
|
|
|
|
baseWin.visibility = false;
|
|
|
|
}
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
let activeStreams;
|
|
|
|
if (type == "Camera") {
|
|
|
|
activeStreams = webrtcUI.getActiveStreams(true, false, false);
|
|
|
|
} else if (type == "Microphone") {
|
|
|
|
activeStreams = webrtcUI.getActiveStreams(false, true, false);
|
|
|
|
} else if (type == "Screen") {
|
2020-10-13 04:13:04 +03:00
|
|
|
activeStreams = webrtcUI.getActiveStreams(false, false, true, true);
|
2020-10-01 00:14:07 +03:00
|
|
|
type = webrtcUI.showScreenSharingIndicator;
|
|
|
|
}
|
|
|
|
|
|
|
|
let bundle = Services.strings.createBundle(
|
|
|
|
"chrome://browser/locale/webrtcIndicator.properties"
|
|
|
|
);
|
|
|
|
|
2020-10-13 04:13:04 +03:00
|
|
|
if (!activeStreams.length) {
|
|
|
|
event.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
if (activeStreams.length == 1) {
|
|
|
|
let stream = activeStreams[0];
|
|
|
|
|
|
|
|
let menuitem = document.createXULElement("menuitem");
|
|
|
|
let labelId = `webrtcIndicator.sharing${type}With.menuitem`;
|
|
|
|
let label = stream.browser.contentTitle || stream.uri;
|
|
|
|
menuitem.setAttribute(
|
|
|
|
"label",
|
|
|
|
bundle.formatStringFromName(labelId, [label])
|
|
|
|
);
|
|
|
|
menuitem.setAttribute("disabled", "true");
|
|
|
|
menupopup.appendChild(menuitem);
|
|
|
|
|
|
|
|
menuitem = document.createXULElement("menuitem");
|
|
|
|
menuitem.setAttribute(
|
|
|
|
"label",
|
|
|
|
bundle.GetStringFromName("webrtcIndicator.controlSharing.menuitem")
|
|
|
|
);
|
|
|
|
menuitem.stream = stream;
|
|
|
|
menuitem.addEventListener("command", this);
|
|
|
|
|
|
|
|
menupopup.appendChild(menuitem);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We show a different menu when there are several active streams.
|
|
|
|
let menuitem = document.createXULElement("menuitem");
|
|
|
|
let labelId = `webrtcIndicator.sharing${type}WithNTabs.menuitem`;
|
|
|
|
let count = activeStreams.length;
|
|
|
|
let label = PluralForm.get(
|
|
|
|
count,
|
|
|
|
bundle.GetStringFromName(labelId)
|
|
|
|
).replace("#1", count);
|
|
|
|
menuitem.setAttribute("label", label);
|
|
|
|
menuitem.setAttribute("disabled", "true");
|
|
|
|
menupopup.appendChild(menuitem);
|
|
|
|
|
|
|
|
for (let stream of activeStreams) {
|
|
|
|
let item = document.createXULElement("menuitem");
|
|
|
|
labelId = "webrtcIndicator.controlSharingOn.menuitem";
|
|
|
|
label = stream.browser.contentTitle || stream.uri;
|
|
|
|
item.setAttribute("label", bundle.formatStringFromName(labelId, [label]));
|
|
|
|
item.stream = stream;
|
|
|
|
item.addEventListener("command", this);
|
|
|
|
menupopup.appendChild(item);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onPopupHiding(event) {
|
2020-10-05 12:23:40 +03:00
|
|
|
if (!this.eventIsForDeviceMenuPopup(event)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-01 00:14:07 +03:00
|
|
|
let menu = event.target;
|
|
|
|
while (menu.firstChild) {
|
|
|
|
menu.firstChild.remove();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onCommand(event) {
|
|
|
|
webrtcUI.showSharingDoorhanger(event.target.stream);
|
|
|
|
},
|
|
|
|
|
2020-10-05 12:23:40 +03:00
|
|
|
/**
|
|
|
|
* Returns true if an event was fired for one of the shared device
|
|
|
|
* menupopups.
|
|
|
|
*
|
|
|
|
* @param event (Event)
|
|
|
|
* The event to check.
|
|
|
|
* @returns True if the event was for one of the shared device
|
|
|
|
* menupopups.
|
|
|
|
*/
|
|
|
|
eventIsForDeviceMenuPopup(event) {
|
|
|
|
let menupopup = event.target;
|
|
|
|
let type = menupopup.getAttribute("type");
|
|
|
|
|
|
|
|
return ["Camera", "Microphone", "Screen"].includes(type);
|
|
|
|
},
|
|
|
|
|
2020-05-18 21:57:40 +03:00
|
|
|
/**
|
2020-08-27 20:03:43 +03:00
|
|
|
* Mutes or unmutes the microphone globally based on the checked
|
|
|
|
* state of toggleEl. Also updates the tooltip of toggleEl once
|
|
|
|
* the state change is done.
|
2020-05-18 21:57:40 +03:00
|
|
|
*
|
2020-08-27 20:03:43 +03:00
|
|
|
* @param toggleEl (Element)
|
|
|
|
* The input[type="checkbox"] for toggling the microphone mute
|
|
|
|
* state.
|
2020-05-18 21:57:40 +03:00
|
|
|
*/
|
2020-08-27 20:03:43 +03:00
|
|
|
toggleMicrophoneMute(toggleEl) {
|
|
|
|
Services.ppmm.sharedData.set(
|
|
|
|
"WebRTC:GlobalMicrophoneMute",
|
|
|
|
toggleEl.checked
|
|
|
|
);
|
|
|
|
Services.ppmm.sharedData.flush();
|
|
|
|
let l10nId =
|
|
|
|
"webrtc-microphone-" + (toggleEl.checked ? "muted" : "unmuted");
|
|
|
|
document.l10n.setAttributes(toggleEl, l10nId);
|
|
|
|
},
|
2020-05-18 21:57:40 +03:00
|
|
|
|
2020-08-27 20:03:43 +03:00
|
|
|
/**
|
|
|
|
* Mutes or unmutes the camera globally based on the checked
|
|
|
|
* state of toggleEl. Also updates the tooltip of toggleEl once
|
|
|
|
* the state change is done.
|
|
|
|
*
|
|
|
|
* @param toggleEl (Element)
|
|
|
|
* The input[type="checkbox"] for toggling the camera mute
|
|
|
|
* state.
|
|
|
|
*/
|
|
|
|
toggleCameraMute(toggleEl) {
|
|
|
|
Services.ppmm.sharedData.set("WebRTC:GlobalCameraMute", toggleEl.checked);
|
|
|
|
Services.ppmm.sharedData.flush();
|
|
|
|
let l10nId = "webrtc-camera-" + (toggleEl.checked ? "muted" : "unmuted");
|
|
|
|
document.l10n.setAttributes(toggleEl, l10nId);
|
2020-05-18 21:57:40 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2014-08-01 17:15:49 +04:00
|
|
|
}
|
|
|
|
},
|
2020-07-17 04:22:10 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* See the documentation on the script global closingInternally() function.
|
|
|
|
*/
|
|
|
|
closingInternally() {
|
|
|
|
this.isClosingInternally = true;
|
|
|
|
},
|
2014-08-01 17:15:49 +04:00
|
|
|
};
|
2020-05-18 21:57:40 +03:00
|
|
|
|
|
|
|
WebRTCIndicator.init();
|