gecko-dev/browser/base/content/webrtcIndicator.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

415 строки
12 KiB
JavaScript
Исходник Обычный вид История

/* 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/. */
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 21:18:31 +03:00
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 21:18:31 +03:00
const { webrtcUI } = ChromeUtils.import("resource:///modules/webrtcUI.jsm");
ChromeUtils.defineModuleGetter(
this,
"SitePermissions",
"resource:///modules/SitePermissions.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AppConstants",
"resource://gre/modules/AppConstants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"MacOSWebRTCStatusbarIndicator",
"resource:///modules/webrtcUI.jsm"
);
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() {
WebRTCIndicator.updateIndicatorState();
}
/**
* Main control object for the WebRTC global indicator
*/
const WebRTCIndicator = {
init(event) {
addEventListener("load", this);
addEventListener("unload", 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;
if (AppConstants.platform == "macosx") {
this.macOSIndicator = new MacOSWebRTCStatusbarIndicator();
}
},
/**
* Exposed externally so that webrtcUI can alert the indicator to
* update itself when sharing states have changed.
*/
updateIndicatorState(initialLayout = false) {
if (this.macOSIndicator) {
this.macOSIndicator.updateIndicatorState();
}
// 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;
}
// 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;
this.updateWindowAttr("sharingvideo", webrtcUI.showCameraIndicator);
this.updateWindowAttr("sharingaudio", webrtcUI.showMicrophoneIndicator);
this.updateWindowAttr(
"sharingscreen",
webrtcUI.showScreenSharingIndicator.startsWith("Screen")
);
// 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 {
this.updateWindowAttr("sharingbrowserwindow");
this.sharingBrowserWindow = false;
}
// Resize and ensure the window position is correct
// (sizeToContent messes with our position).
window.sizeToContent();
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();
break;
}
case "unload": {
this.onUnload();
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;
},
onUnload() {
if (this.macOSIndicator) {
this.macOSIndicator.close();
}
},
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;
}
// 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);
}
},
};
WebRTCIndicator.init();