Bug 1325049 - Fix the global webrtc sharing indicator to work with multiple content processes, r=felipe.

This commit is contained in:
Florian Quèze 2017-03-15 22:47:06 +01:00
Родитель c7c5b5fa85
Коммит f55f3ce4a0
4 изменённых файлов: 193 добавлений и 7 удалений

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

@ -9,6 +9,8 @@ support-files =
skip-if = (os == "linux" && debug) # linux: bug 976544
[browser_devices_get_user_media_anim.js]
[browser_devices_get_user_media_in_frame.js]
[browser_devices_get_user_media_multi_process.js]
skip-if = (e10s && debug) # bug 1347625
[browser_devices_get_user_media_screen.js]
skip-if = (e10s && debug) || (os == "linux") || (os == "win" && !debug) # bug 1320754 for e10s debug, and bug 1320994 for linux opt, bug 1338038 for windows and linux debug
[browser_devices_get_user_media_tear_off_tab.js]

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

@ -0,0 +1,153 @@
/* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
desc: "getUserMedia audio in a first process + video in a second process",
run: function* checkMultiProcess() {
// The main purpose of this test is to ensure webrtc sharing indicators
// work with multiple content processes, but it makes sense to run this
// test without e10s too to ensure using webrtc devices in two different
// tabs is handled correctly.
// Request audio in the first tab.
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(true);
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(true);
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true},
"expected microphone to be shared");
yield indicator;
yield checkSharingUI({audio: true});
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
yield expectNoObserverCalled();
// If we have reached the max process count already, increase it to ensure
// our new tab can have its own content process.
var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
let childCount = ppmm.childCount;
let maxContentProcess = Services.prefs.getIntPref("dom.ipc.processCount");
// The first check is because if we are on a branch where e10s-multi is
// disabled, we want to keep testing e10s with a single content process.
// The + 1 is because ppmm.childCount also counts the chrome process
// (which also runs process scripts).
if (maxContentProcess > 1 && childCount == maxContentProcess + 1) {
yield SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount",
childCount]]});
}
// Open a new tab with a different hostname.
let url = gBrowser.currentURI.spec.replace("https://example.com/",
"http://127.0.0.1:8888/");
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
tab.linkedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
// Request video.
promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true);
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, true);
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
yield checkSharingUI({video: true}, window, {audio: true, video: true});
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
is(webrtcUI.getActiveStreams(true).length, 1, "1 active video stream");
is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
info("removing the second tab");
yield BrowserTestUtils.removeTab(tab);
// Check that we still show the sharing indicators for the first tab's stream.
yield promiseWaitForCondition(() => !webrtcUI.showCameraIndicator);
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
yield checkSharingUI({audio: true});
// When both tabs use the same content process, the frame script for the
// first tab receives observer notifications for things happening in the
// second tab, so let's clear the observer call counts before we cleanup
// in the first tab.
yield ignoreObserversCalled();
// Close the first tab's stream and verify that all indicators are removed.
yield closeStream();
ok(!webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator hidden");
is(webrtcUI.getActiveStreams(true, true, true).length, 0, "0 active streams");
}
},
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}

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

@ -241,6 +241,18 @@ function expectNoObserverCalled(aIgnoreDeviceEvents = false) {
});
}
function ignoreObserversCalled() {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:ExpectNoObserverCalled:Reply",
function listener() {
mm.removeMessageListener("Test:ExpectNoObserverCalled:Reply", listener);
resolve();
});
mm.sendAsyncMessage("Test:ExpectNoObserverCalled");
});
}
function promiseMessageReceived() {
return new Promise((resolve, reject) => {
let mm = _mm();
@ -439,7 +451,9 @@ function checkDeviceSelectors(aAudio, aVideo, aScreen) {
ok(screenSelector.hidden, "screen selector hidden");
}
function* checkSharingUI(aExpected, aWin = window) {
// aExpected is for the current tab,
// aExpectedGlobal is for all tabs.
function* checkSharingUI(aExpected, aWin = window, aExpectedGlobal = null) {
let doc = aWin.document;
// First check the icon above the control center (i) icon.
let identityBox = doc.getElementById("identity-box");
@ -483,7 +497,7 @@ function* checkSharingUI(aExpected, aWin = window) {
aWin.gIdentityHandler._identityPopup.hidden = true;
// Check the global indicators.
yield* assertWebRTCIndicatorStatus(aExpected);
yield* assertWebRTCIndicatorStatus(aExpectedGlobal || aExpected);
}
function* checkNotSharing() {

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

@ -154,6 +154,13 @@ this.webrtcUI = {
webrtcUI.forgetActivePermissionsFromBrowser(aBrowser);
},
forgetStreamsFromProcess(aProcessMM) {
// stream.processMM is null when e10s is disabled.
this._streams =
this._streams.filter(stream => stream.processMM &&
stream.processMM != aProcessMM);
},
showSharingDoorhanger(aActiveStream) {
let browserWindow = aActiveStream.browser.ownerGlobal;
if (aActiveStream.tab) {
@ -279,30 +286,40 @@ this.webrtcUI = {
removePrompt(aMessage.target, aMessage.data);
break;
case "webrtc:UpdatingIndicators":
webrtcUI._streams = [];
webrtcUI.forgetStreamsFromProcess(aMessage.target);
break;
case "webrtc:UpdateGlobalIndicators":
updateIndicators(aMessage.data, aMessage.target);
break;
case "webrtc:UpdateBrowserIndicators":
let id = aMessage.data.windowId;
aMessage.targetFrameLoader.QueryInterface(Ci.nsIFrameLoader);
let processMM =
aMessage.targetFrameLoader.messageManager.processMessageManager;
let index;
for (index = 0; index < webrtcUI._streams.length; ++index) {
if (webrtcUI._streams[index].state.windowId == id)
let stream = webrtcUI._streams[index];
if (stream.state.windowId == id && stream.processMM == processMM)
break;
}
// If there's no documentURI, the update is actually a removal of the
// stream, triggered by the recording-window-ended notification.
if (!aMessage.data.documentURI && index < webrtcUI._streams.length)
if (!aMessage.data.documentURI && index < webrtcUI._streams.length) {
webrtcUI._streams.splice(index, 1);
else
webrtcUI._streams[index] = {browser: aMessage.target, state: aMessage.data};
} else {
webrtcUI._streams[index] = {
browser: aMessage.target,
processMM,
state: aMessage.data
};
}
let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
if (tabbrowser)
tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
break;
case "child-process-shutdown":
webrtcUI.processIndicators.delete(aMessage.target);
webrtcUI.forgetStreamsFromProcess(aMessage.target);
updateIndicators(null, null);
break;
}