зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1120546 - getUserMedia used in frames needs tests, r=felipe.
This commit is contained in:
Родитель
12600e6fbc
Коммит
f094fb6f12
|
@ -303,6 +303,8 @@ run-if = datareporting
|
|||
skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623
|
||||
[browser_devices_get_user_media_about_urls.js]
|
||||
skip-if = e10s # Bug 1071623
|
||||
[browser_devices_get_user_media_in_frame.js]
|
||||
skip-if = e10s # Bug 1071623
|
||||
[browser_discovery.js]
|
||||
[browser_double_close_tab.js]
|
||||
skip-if = e10s
|
||||
|
|
|
@ -459,6 +459,50 @@ let gTests = [
|
|||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: reloading the page removes all gUM UI",
|
||||
run: function checkReloading() {
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI({video: true, audio: true});
|
||||
|
||||
yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
|
||||
|
||||
info("reloading the web page");
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
content.location.reload();
|
||||
yield deferred.promise;
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
if (gObservedTopics["recording-device-events"] == 2) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
--gObservedTopics["recording-device-events"];
|
||||
}
|
||||
expectObserverCalled("recording-device-events");
|
||||
expectObserverCalled("recording-window-ended");
|
||||
expectNoObserverCalled();
|
||||
yield checkNotSharing();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia prompt: Always/Never Share",
|
||||
run: function checkRememberCheckbox() {
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
/* 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/. */
|
||||
|
||||
const kObservedTopics = [
|
||||
"getUserMedia:response:allow",
|
||||
"getUserMedia:revoke",
|
||||
"getUserMedia:response:deny",
|
||||
"getUserMedia:request",
|
||||
"recording-device-events",
|
||||
"recording-window-ended"
|
||||
];
|
||||
|
||||
const PREF_PERMISSION_FAKE = "media.navigator.permission.fake";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
|
||||
"@mozilla.org/mediaManagerService;1",
|
||||
"nsIMediaManagerService");
|
||||
|
||||
var gObservedTopics = {};
|
||||
function observer(aSubject, aTopic, aData) {
|
||||
if (!(aTopic in gObservedTopics))
|
||||
gObservedTopics[aTopic] = 1;
|
||||
else
|
||||
++gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
function promiseObserverCalled(aTopic, aAction) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
Services.obs.addObserver(function observer() {
|
||||
ok(true, "got " + aTopic + " notification");
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
|
||||
if (kObservedTopics.indexOf(aTopic) != -1) {
|
||||
if (!(aTopic in gObservedTopics))
|
||||
gObservedTopics[aTopic] = -1;
|
||||
else
|
||||
--gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}, aTopic, false);
|
||||
|
||||
if (aAction)
|
||||
aAction();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function expectObserverCalled(aTopic) {
|
||||
is(gObservedTopics[aTopic], 1, "expected notification " + aTopic);
|
||||
if (aTopic in gObservedTopics)
|
||||
--gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
function expectNoObserverCalled() {
|
||||
for (let topic in gObservedTopics) {
|
||||
if (gObservedTopics[topic])
|
||||
is(gObservedTopics[topic], 0, topic + " notification unexpected");
|
||||
}
|
||||
gObservedTopics = {};
|
||||
}
|
||||
|
||||
function promiseMessage(aMessage, aAction) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
content.addEventListener("message", function messageListener(event) {
|
||||
content.removeEventListener("message", messageListener);
|
||||
is(event.data, aMessage, "received " + aMessage);
|
||||
if (event.data == aMessage)
|
||||
deferred.resolve();
|
||||
else
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
if (aAction)
|
||||
aAction();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promisePopupNotificationShown(aName, aAction) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
PopupNotifications.panel.addEventListener("popupshown", function popupNotifShown() {
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupNotifShown);
|
||||
|
||||
ok(!!PopupNotifications.getNotification(aName), aName + " notification shown");
|
||||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
ok(!!PopupNotifications.panel.firstChild, "notification panel populated");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
if (aAction)
|
||||
aAction();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promisePopupNotification(aName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
waitForCondition(() => PopupNotifications.getNotification(aName),
|
||||
() => {
|
||||
ok(!!PopupNotifications.getNotification(aName),
|
||||
aName + " notification appeared");
|
||||
|
||||
deferred.resolve();
|
||||
}, "timeout waiting for popup notification " + aName);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseNoPopupNotification(aName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
waitForCondition(() => !PopupNotifications.getNotification(aName),
|
||||
() => {
|
||||
ok(!PopupNotifications.getNotification(aName),
|
||||
aName + " notification removed");
|
||||
deferred.resolve();
|
||||
}, "timeout waiting for popup notification " + aName + " to disappear");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
const kActionAlways = 1;
|
||||
const kActionDeny = 2;
|
||||
const kActionNever = 3;
|
||||
|
||||
function activateSecondaryAction(aAction) {
|
||||
let notification = PopupNotifications.panel.firstChild;
|
||||
notification.button.focus();
|
||||
let popup = notification.menupopup;
|
||||
popup.addEventListener("popupshown", function () {
|
||||
popup.removeEventListener("popupshown", arguments.callee, false);
|
||||
|
||||
// Press 'down' as many time as needed to select the requested action.
|
||||
while (aAction--)
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
// Activate
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
}, false);
|
||||
|
||||
// One down event to open the popup
|
||||
EventUtils.synthesizeKey("VK_DOWN",
|
||||
{ altKey: !navigator.platform.contains("Mac") });
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
kObservedTopics.forEach(topic => {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
});
|
||||
Services.prefs.clearUserPref(PREF_PERMISSION_FAKE);
|
||||
});
|
||||
|
||||
function getMediaCaptureState() {
|
||||
let hasVideo = {};
|
||||
let hasAudio = {};
|
||||
MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio);
|
||||
if (hasVideo.value && hasAudio.value)
|
||||
return "CameraAndMicrophone";
|
||||
if (hasVideo.value)
|
||||
return "Camera";
|
||||
if (hasAudio.value)
|
||||
return "Microphone";
|
||||
return "none";
|
||||
}
|
||||
|
||||
function* closeStream(aGlobal, aAlreadyClosed) {
|
||||
expectNoObserverCalled();
|
||||
|
||||
info("closing the stream");
|
||||
aGlobal.closeStream();
|
||||
|
||||
if (!aAlreadyClosed)
|
||||
yield promiseObserverCalled("recording-device-events");
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
if (!aAlreadyClosed)
|
||||
expectObserverCalled("recording-window-ended");
|
||||
|
||||
yield* assertWebRTCIndicatorStatus(null);
|
||||
}
|
||||
|
||||
function checkDeviceSelectors(aAudio, aVideo) {
|
||||
let micSelector = document.getElementById("webRTC-selectMicrophone");
|
||||
if (aAudio)
|
||||
ok(!micSelector.hidden, "microphone selector visible");
|
||||
else
|
||||
ok(micSelector.hidden, "microphone selector hidden");
|
||||
|
||||
let cameraSelector = document.getElementById("webRTC-selectCamera");
|
||||
if (aVideo)
|
||||
ok(!cameraSelector.hidden, "camera selector visible");
|
||||
else
|
||||
ok(cameraSelector.hidden, "camera selector hidden");
|
||||
}
|
||||
|
||||
function* checkSharingUI(aExpected) {
|
||||
yield promisePopupNotification("webRTC-sharingDevices");
|
||||
|
||||
yield* assertWebRTCIndicatorStatus(aExpected);
|
||||
}
|
||||
|
||||
function* checkNotSharing() {
|
||||
is(getMediaCaptureState(), "none", "expected nothing to be shared");
|
||||
|
||||
ok(!PopupNotifications.getNotification("webRTC-sharingDevices"),
|
||||
"no webRTC-sharingDevices popup notification");
|
||||
|
||||
yield* assertWebRTCIndicatorStatus(null);
|
||||
}
|
||||
|
||||
function getFrameGlobal(aFrameId) {
|
||||
return content.wrappedJSObject.document.getElementById(aFrameId).contentWindow;
|
||||
}
|
||||
|
||||
const permissionError = "error: PermissionDeniedError: The user did not grant permission for the operation.";
|
||||
|
||||
let gTests = [
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video",
|
||||
run: function checkAudioVideo() {
|
||||
let global = getFrameGlobal("frame1");
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting devices");
|
||||
global.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
|
||||
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
|
||||
"webRTC-shareDevices-notification-icon", "anchored to device icon");
|
||||
checkDeviceSelectors(true, true);
|
||||
is(PopupNotifications.panel.firstChild.getAttribute("popupid"),
|
||||
"webRTC-shareDevices", "panel using devices icon");
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI({audio: true, video: true});
|
||||
yield closeStream(global);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: stop sharing",
|
||||
run: function checkStopSharing() {
|
||||
let global = getFrameGlobal("frame1");
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting devices");
|
||||
global.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI({video: true, audio: true});
|
||||
|
||||
yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
|
||||
activateSecondaryAction(kActionDeny);
|
||||
|
||||
yield promiseObserverCalled("recording-device-events");
|
||||
expectObserverCalled("getUserMedia:revoke");
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
expectObserverCalled("recording-window-ended");
|
||||
|
||||
if (gObservedTopics["recording-device-events"] == 1) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
gObservedTopics["recording-device-events"] = 0;
|
||||
}
|
||||
|
||||
expectNoObserverCalled();
|
||||
yield checkNotSharing();
|
||||
|
||||
// the stream is already closed, but this will do some cleanup anyway
|
||||
yield closeStream(global, true);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: reloading the frame removes all sharing UI",
|
||||
run: function checkReloading() {
|
||||
let global = getFrameGlobal("frame1");
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting devices");
|
||||
global.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI({video: true, audio: true});
|
||||
|
||||
info("reloading the frame");
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
global.location.reload();
|
||||
yield deferred.promise;
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
if (gObservedTopics["recording-device-events"] == 2) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
--gObservedTopics["recording-device-events"];
|
||||
}
|
||||
expectObserverCalled("recording-device-events");
|
||||
expectObserverCalled("recording-window-ended");
|
||||
expectNoObserverCalled();
|
||||
yield checkNotSharing();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: reloading the frame removes prompts",
|
||||
run: function checkReloadingRemovesPrompts() {
|
||||
let global = getFrameGlobal("frame1");
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting devices");
|
||||
global.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
info("reloading the frame");
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
global.location.reload();
|
||||
yield deferred.promise;
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-shareDevices");
|
||||
|
||||
expectObserverCalled("recording-window-ended");
|
||||
expectNoObserverCalled();
|
||||
yield checkNotSharing();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: reloading a frame updates the sharing UI",
|
||||
run: function checkUpdateWhenReloading() {
|
||||
// We'll share only the mic in the first frame, then share both in the
|
||||
// second frame, then reload the second frame. After each step, we'll check
|
||||
// the UI is in the correct state.
|
||||
let g1 = getFrameGlobal("frame1"), g2 = getFrameGlobal("frame2");
|
||||
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting microphone in the first frame");
|
||||
g1.requestDevice(true, false);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, false);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "Microphone", "microphone to be shared");
|
||||
|
||||
yield checkSharingUI({video: false, audio: true});
|
||||
expectNoObserverCalled();
|
||||
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
info("requesting both devices in the second frame");
|
||||
g2.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectObserverCalled("getUserMedia:response:allow");
|
||||
expectObserverCalled("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI({video: true, audio: true});
|
||||
expectNoObserverCalled();
|
||||
|
||||
info("reloading the second frame");
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
g2.location.reload();
|
||||
yield deferred.promise;
|
||||
|
||||
yield checkSharingUI({video: false, audio: true});
|
||||
expectObserverCalled("recording-window-ended");
|
||||
if (gObservedTopics["recording-device-events"] == 2) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
--gObservedTopics["recording-device-events"];
|
||||
}
|
||||
expectObserverCalled("recording-device-events");
|
||||
expectNoObserverCalled();
|
||||
|
||||
yield closeStream(g1);
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
expectNoObserverCalled();
|
||||
yield checkNotSharing();
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.linkedBrowser.addEventListener("load", function onload() {
|
||||
tab.linkedBrowser.removeEventListener("load", onload, true);
|
||||
|
||||
kObservedTopics.forEach(topic => {
|
||||
Services.obs.addObserver(observer, topic, false);
|
||||
});
|
||||
Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true);
|
||||
|
||||
is(PopupNotifications._currentNotifications.length, 0,
|
||||
"should start the test without any prior popup notification");
|
||||
|
||||
Task.spawn(function () {
|
||||
for (let test of gTests) {
|
||||
info(test.desc);
|
||||
yield test.run();
|
||||
|
||||
// Cleanup before the next test
|
||||
expectNoObserverCalled();
|
||||
}
|
||||
}).then(finish, ex => {
|
||||
ok(false, "Unexpected Exception: " + ex);
|
||||
finish();
|
||||
});
|
||||
}, true);
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
rootDir = rootDir.replace("chrome://mochitests/content/",
|
||||
"https://example.com/");
|
||||
let url = rootDir + "get_user_media.html";
|
||||
content.location = 'data:text/html,<iframe id="frame1" src="' + url + '"></iframe><iframe id="frame2" src="' + url + '"></iframe>'
|
||||
}
|
Загрузка…
Ссылка в новой задаче