From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- ...wser_devices_get_user_media_in_xorigin_frame.js | 798 +++++++++++++++++++++ 1 file changed, 798 insertions(+) create mode 100644 browser/base/content/test/webrtc/browser_devices_get_user_media_in_xorigin_frame.js (limited to 'browser/base/content/test/webrtc/browser_devices_get_user_media_in_xorigin_frame.js') diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_in_xorigin_frame.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_xorigin_frame.js new file mode 100644 index 0000000000..8c0b0476f3 --- /dev/null +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_xorigin_frame.js @@ -0,0 +1,798 @@ +/* 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 permissionError = + "error: NotAllowedError: The request is not allowed " + + "by the user agent or the platform in the current context."; + +const PromptResult = { + ALLOW: "allow", + DENY: "deny", + PROMPT: "prompt", +}; + +const Perms = Services.perms; + +async function promptNoDelegate(aThirdPartyOrgin, audio = true, video = true) { + // Persistent allowed first party origin + const uri = gBrowser.selectedBrowser.documentURI; + if (audio) { + PermissionTestUtils.add(uri, "microphone", Services.perms.ALLOW_ACTION); + } + if (video) { + PermissionTestUtils.add(uri, "camera", Services.perms.ALLOW_ACTION); + } + + // Check that we get a prompt. + const observerPromise = expectObserverCalled("getUserMedia:request"); + const promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(audio, video, "frame4"); + await promise; + await observerPromise; + + // The 'Remember this decision' checkbox is hidden. + const notification = PopupNotifications.panel.firstElementChild; + const checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + ok(checkbox.hidden, "checkbox is not visible"); + ok(!checkbox.checked, "checkbox not checked"); + + // Check the label of the notification should be the first party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options.name, + uri.host, + "Use first party's origin" + ); + + // Check the secondName of the notification should be the third party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options + .secondName, + aThirdPartyOrgin, + "Use third party's origin as secondName" + ); + + let indicator = promiseIndicatorWindow(); + let observerPromise1 = expectObserverCalled("getUserMedia:response:allow"); + let observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + let state = await getMediaCaptureState(); + is( + !!state.audio, + audio, + `expected microphone to be ${audio ? "" : "not"} shared` + ); + is( + !!state.video, + video, + `expected camera to be ${video ? "" : "not"} shared` + ); + await indicator; + await checkSharingUI({ audio, video }, undefined, undefined, { + video: { scope: SitePermissions.SCOPE_PERSISTENT }, + audio: { scope: SitePermissions.SCOPE_PERSISTENT }, + }); + + // Cleanup. + await closeStream(false, "frame4"); + + PermissionTestUtils.remove(uri, "camera"); + PermissionTestUtils.remove(uri, "microphone"); +} + +async function promptNoDelegateScreenSharing(aThirdPartyOrgin) { + // Persistent allow screen sharing + const uri = gBrowser.selectedBrowser.documentURI; + PermissionTestUtils.add(uri, "screen", Services.perms.ALLOW_ACTION); + + const observerPromise = expectObserverCalled("getUserMedia:request"); + const promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(false, true, "frame4", "screen"); + await promise; + await observerPromise; + + checkDeviceSelectors(["screen"]); + const notification = PopupNotifications.panel.firstElementChild; + + // The 'Remember this decision' checkbox is hidden. + const checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + + if (ALLOW_SILENCING_NOTIFICATIONS) { + ok(!checkbox.hidden, "Notification silencing checkbox is visible"); + } else { + ok(checkbox.hidden, "checkbox is not visible"); + } + + ok(!checkbox.checked, "checkbox not checked"); + + // Check the label of the notification should be the first party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options.name, + uri.host, + "Use first party's origin" + ); + + // Check the secondName of the notification should be the third party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options + .secondName, + aThirdPartyOrgin, + "Use third party's origin as secondName" + ); + + const menulist = document.getElementById("webRTC-selectWindow-menulist"); + const count = menulist.itemCount; + menulist.getItemAtIndex(count - 1).doCommand(); + ok(!notification.button.disabled, "Allow button is enabled"); + + const indicator = promiseIndicatorWindow(); + const observerPromise1 = expectObserverCalled("getUserMedia:response:allow"); + const observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + Assert.deepEqual( + await getMediaCaptureState(), + { screen: "Screen" }, + "expected screen to be shared" + ); + + await indicator; + await checkSharingUI({ screen: "Screen" }, undefined, undefined, { + screen: { scope: SitePermissions.SCOPE_PERSISTENT }, + }); + await closeStream(false, "frame4"); + + PermissionTestUtils.remove(uri, "screen"); +} + +var gTests = [ + { + desc: "'Always Allow' enabled on third party pages, when origin is explicitly allowed", + run: async function checkNoAlwaysOnThirdParty() { + // Initially set both permissions to 'prompt'. + const uri = gBrowser.selectedBrowser.documentURI; + PermissionTestUtils.add(uri, "microphone", Services.perms.PROMPT_ACTION); + PermissionTestUtils.add(uri, "camera", Services.perms.PROMPT_ACTION); + + const observerPromise = expectObserverCalled("getUserMedia:request"); + const promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(true, true, "frame1"); + await promise; + await observerPromise; + checkDeviceSelectors(["microphone", "camera"]); + + // The 'Remember this decision' checkbox is visible. + const notification = PopupNotifications.panel.firstElementChild; + const checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + ok(!checkbox.hidden, "checkbox is visible"); + ok(!checkbox.checked, "checkbox not checked"); + + // Check the label of the notification should be the first party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options.name, + uri.host, + "Use first party's origin" + ); + + const indicator = promiseIndicatorWindow(); + const observerPromise1 = expectObserverCalled( + "getUserMedia:response:allow" + ); + const observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + Assert.deepEqual( + await getMediaCaptureState(), + { audio: true, video: true }, + "expected camera and microphone to be shared" + ); + await indicator; + await checkSharingUI({ audio: true, video: true }); + + // Cleanup. + await closeStream(false, "frame1"); + PermissionTestUtils.remove(uri, "camera"); + PermissionTestUtils.remove(uri, "microphone"); + }, + }, + { + desc: "'Always Allow' disabled when sharing screen in third party iframes, when origin is explicitly allowed", + run: async function checkScreenSharing() { + const observerPromise = expectObserverCalled("getUserMedia:request"); + const promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(false, true, "frame1", "screen"); + await promise; + await observerPromise; + + checkDeviceSelectors(["screen"]); + const notification = PopupNotifications.panel.firstElementChild; + + // The 'Remember this decision' checkbox is visible. + const checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + ok(!checkbox.hidden, "checkbox is visible"); + ok(!checkbox.checked, "checkbox not checked"); + + const menulist = document.getElementById("webRTC-selectWindow-menulist"); + const count = menulist.itemCount; + ok( + count >= 4, + "There should be the 'Select Window or Screen' item, a separator and at least one window and one screen" + ); + + const noWindowOrScreenItem = menulist.getItemAtIndex(0); + ok( + noWindowOrScreenItem.hasAttribute("selected"), + "the 'Select Window or Screen' item is selected" + ); + is( + menulist.selectedItem, + noWindowOrScreenItem, + "'Select Window or Screen' is the selected item" + ); + is(menulist.value, "-1", "no window or screen is selected by default"); + ok( + noWindowOrScreenItem.disabled, + "'Select Window or Screen' item is disabled" + ); + ok(notification.button.disabled, "Allow button is disabled"); + ok( + notification.hasAttribute("invalidselection"), + "Notification is marked as invalid" + ); + + menulist.getItemAtIndex(count - 1).doCommand(); + ok(!notification.button.disabled, "Allow button is enabled"); + + const indicator = promiseIndicatorWindow(); + const observerPromise1 = expectObserverCalled( + "getUserMedia:response:allow" + ); + const observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + Assert.deepEqual( + await getMediaCaptureState(), + { screen: "Screen" }, + "expected screen to be shared" + ); + + await indicator; + await checkSharingUI({ screen: "Screen" }); + await closeStream(false, "frame1"); + }, + }, + + { + desc: "getUserMedia use persistent permissions from first party", + run: async function checkUsePersistentPermissionsFirstParty() { + async function checkPersistentPermission( + aPermission, + aRequestType, + aIframeId, + aExpect + ) { + info( + `Test persistent permission ${aPermission} type ${aRequestType} expect ${aExpect}` + ); + const uri = gBrowser.selectedBrowser.documentURI; + // Persistent allow/deny for first party uri + PermissionTestUtils.add(uri, aRequestType, aPermission); + + let audio = aRequestType == "microphone"; + let video = aRequestType == "camera"; + const screen = aRequestType == "screen" ? "screen" : undefined; + if (screen) { + audio = false; + video = true; + } + if (aExpect == PromptResult.PROMPT) { + // Check that we get a prompt. + const observerPromise = expectObserverCalled("getUserMedia:request"); + const observerPromise1 = expectObserverCalled( + "getUserMedia:response:deny" + ); + const observerPromise2 = expectObserverCalled( + "recording-window-ended" + ); + const promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(audio, video, aIframeId, screen); + await promise; + await observerPromise; + + // Check the label of the notification should be the first party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options + .name, + uri.host, + "Use first party's origin" + ); + + // Deny the request to cleanup... + await promiseMessage(permissionError, () => { + activateSecondaryAction(kActionDeny); + }); + await observerPromise1; + await observerPromise2; + let browser = gBrowser.selectedBrowser; + SitePermissions.removeFromPrincipal(null, aRequestType, browser); + } else if (aExpect == PromptResult.ALLOW) { + const observerPromise = expectObserverCalled("getUserMedia:request"); + const observerPromise1 = expectObserverCalled( + "getUserMedia:response:allow" + ); + const observerPromise2 = expectObserverCalled( + "recording-device-events" + ); + const promise = promiseMessage("ok"); + await promiseRequestDevice(audio, video, aIframeId, screen); + await promise; + await observerPromise; + + await promiseNoPopupNotification("webRTC-shareDevices"); + await observerPromise1; + await observerPromise2; + + let expected = {}; + if (audio) { + expected.audio = audio; + } + if (video) { + expected.video = video; + } + + Assert.deepEqual( + await getMediaCaptureState(), + expected, + "expected " + Object.keys(expected).join(" and ") + " to be shared" + ); + + await closeStream(false, aIframeId); + } else if (aExpect == PromptResult.DENY) { + const promises = []; + // frame3 disallows by feature Permissions Policy before request. + if (aIframeId != "frame3") { + promises.push( + expectObserverCalled("getUserMedia:request"), + expectObserverCalled("getUserMedia:response:deny") + ); + } + promises.push( + expectObserverCalled("recording-window-ended"), + promiseMessage(permissionError), + promiseRequestDevice(audio, video, aIframeId, screen) + ); + await Promise.all(promises); + } + + PermissionTestUtils.remove(uri, aRequestType); + } + + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "camera", + "frame1", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "camera", + "frame1", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "camera", + "frame1", + PromptResult.ALLOW + ); + + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "microphone", + "frame1", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "microphone", + "frame1", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "microphone", + "frame1", + PromptResult.ALLOW + ); + + // Wildcard attributes still get delegation when their src is unchanged. + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "camera", + "frame4", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "camera", + "frame4", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "camera", + "frame4", + PromptResult.ALLOW + ); + + // Wildcard attributes still get delegation when their src is unchanged. + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "microphone", + "frame4", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "microphone", + "frame4", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "microphone", + "frame4", + PromptResult.ALLOW + ); + + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "screen", + "frame1", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "screen", + "frame1", + PromptResult.DENY + ); + // Always prompt screen sharing + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "screen", + "frame1", + PromptResult.PROMPT + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "screen", + "frame4", + PromptResult.PROMPT + ); + + // Denied by default if allow is not defined + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "camera", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "camera", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "camera", + "frame3", + PromptResult.DENY + ); + + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "microphone", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "microphone", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "microphone", + "frame3", + PromptResult.DENY + ); + + await checkPersistentPermission( + Perms.PROMPT_ACTION, + "screen", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.DENY_ACTION, + "screen", + "frame3", + PromptResult.DENY + ); + await checkPersistentPermission( + Perms.ALLOW_ACTION, + "screen", + "frame3", + PromptResult.DENY + ); + }, + }, + + { + desc: "getUserMedia use temporary blocked permissions from first party", + run: async function checkUseTempPermissionsBlockFirstParty() { + async function checkTempPermission(aRequestType) { + let browser = gBrowser.selectedBrowser; + let observerPromise = expectObserverCalled("getUserMedia:request"); + let observerPromise1 = expectObserverCalled( + "getUserMedia:response:deny" + ); + let observerPromise2 = expectObserverCalled("recording-window-ended"); + let promise = promisePopupNotificationShown("webRTC-shareDevices"); + let audio = aRequestType == "microphone"; + let video = aRequestType == "camera"; + const screen = aRequestType == "screen" ? "screen" : undefined; + if (screen) { + audio = false; + video = true; + } + + await promiseRequestDevice(audio, video, null, screen); + await promise; + await observerPromise; + + // Temporarily grant/deny from top level + // Only need to check allow and deny temporary permissions + await promiseMessage(permissionError, () => { + activateSecondaryAction(kActionDeny); + }); + await observerPromise1; + await observerPromise2; + await checkNotSharing(); + + observerPromise = expectObserverCalled("getUserMedia:request"); + observerPromise1 = expectObserverCalled("getUserMedia:response:deny"); + observerPromise2 = expectObserverCalled("recording-window-ended"); + promise = promiseMessage(permissionError); + await promiseRequestDevice(audio, video, "frame1", screen); + await promise; + + await observerPromise; + await observerPromise1; + await observerPromise2; + + SitePermissions.removeFromPrincipal(null, aRequestType, browser); + } + + // At the moment we only save temporary deny + await checkTempPermission("camera"); + await checkTempPermission("microphone"); + await checkTempPermission("screen"); + }, + }, + { + desc: "Don't reprompt while actively sharing in maybe unsafe permission delegation", + run: async function checkNoRepromptNoDelegate() { + // Change location to ensure that we're treated as potentially unsafe. + await promiseChangeLocationFrame( + "frame4", + "https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html" + ); + + // Check that we get a prompt. + let observerPromise = expectObserverCalled("getUserMedia:request"); + let promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(true, true, "frame4"); + await promise; + await observerPromise; + + // Check the secondName of the notification should be the third party + is( + PopupNotifications.getNotification("webRTC-shareDevices").options + .secondName, + "test2.example.com", + "Use third party's origin as secondName" + ); + + const notification = PopupNotifications.panel.firstElementChild; + let indicator = promiseIndicatorWindow(); + let observerPromise1 = expectObserverCalled( + "getUserMedia:response:allow" + ); + let observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + + let state = await getMediaCaptureState(); + is(!!state.audio, true, "expected microphone to be shared"); + is(!!state.video, true, "expected camera to be shared"); + await indicator; + await checkSharingUI({ audio: true, video: true }); + + // Check that we now don't get a prompt. + observerPromise = expectObserverCalled("getUserMedia:request"); + observerPromise1 = expectObserverCalled("getUserMedia:response:allow"); + observerPromise2 = expectObserverCalled("recording-device-events"); + promise = promiseMessage("ok"); + await promiseRequestDevice(true, true, "frame4"); + await promise; + await observerPromise; + + await promiseNoPopupNotification("webRTC-shareDevices"); + await observerPromise1; + await observerPromise2; + + state = await getMediaCaptureState(); + is(!!state.audio, true, "expected microphone to be shared"); + is(!!state.video, true, "expected camera to be shared"); + await checkSharingUI({ audio: true, video: true }); + + // Cleanup. + await closeStream(false, "frame4"); + }, + }, + { + desc: "Change location, prompt and display both first party and third party origin in maybe unsafe permission delegation", + run: async function checkPromptNoDelegateChangeLoxation() { + await promiseChangeLocationFrame( + "frame4", + "https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html" + ); + await promptNoDelegate("test2.example.com"); + }, + }, + { + desc: "Change location, prompt and display both first party and third party origin when sharing screen in unsafe permission delegation", + run: async function checkPromptNoDelegateScreenSharingChangeLocation() { + await promiseChangeLocationFrame( + "frame4", + "https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html" + ); + await promptNoDelegateScreenSharing("test2.example.com"); + }, + }, + { + desc: "Prompt and display both first party and third party origin and temporary deny in frame does not change permission scope", + skipObserverVerification: true, + run: async function checkPromptBothOriginsTempDenyFrame() { + // Change location to ensure that we're treated as potentially unsafe. + await promiseChangeLocationFrame( + "frame4", + "https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html" + ); + + // Persistent allowed first party origin + let browser = gBrowser.selectedBrowser; + let uri = gBrowser.selectedBrowser.documentURI; + let principal = Services.scriptSecurityManager.createContentPrincipal( + uri, + {} + ); + + let observerPromise = expectObserverCalled("getUserMedia:request"); + let promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(true, true); + await promise; + await observerPromise; + + // Ensure that checking the 'Remember this decision' + let notification = PopupNotifications.panel.firstElementChild; + let checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + ok(!checkbox.checked, "checkbox is not checked"); + checkbox.click(); + + let indicator = promiseIndicatorWindow(); + let observerPromise1 = expectObserverCalled( + "getUserMedia:response:allow" + ); + let observerPromise2 = expectObserverCalled("recording-device-events"); + await promiseMessage("ok", () => + EventUtils.synthesizeMouseAtCenter(notification.button, {}) + ); + await observerPromise1; + await observerPromise2; + Assert.deepEqual( + await getMediaCaptureState(), + { audio: true, video: true }, + "expected camera and microphone to be shared" + ); + await indicator; + await checkSharingUI({ audio: true, video: true }, undefined, undefined, { + audio: { scope: SitePermissions.SCOPE_PERSISTENT }, + video: { scope: SitePermissions.SCOPE_PERSISTENT }, + }); + await closeStream(true); + + // Check that we get a prompt. + observerPromise = expectObserverCalled("getUserMedia:request"); + promise = promisePopupNotificationShown("webRTC-shareDevices"); + await promiseRequestDevice(true, true, "frame4"); + await promise; + await observerPromise; + + // The 'Remember this decision' checkbox is hidden. + notification = PopupNotifications.panel.firstElementChild; + checkbox = notification.checkbox; + ok(!!checkbox, "checkbox is present"); + ok(checkbox.hidden, "checkbox is not visible"); + + observerPromise1 = expectObserverCalled("getUserMedia:response:deny"); + observerPromise2 = expectObserverCalled("recording-window-ended"); + await promiseMessage(permissionError, () => { + activateSecondaryAction(kActionDeny); + }); + + await observerPromise1; + await observerPromise2; + await checkNotSharing(); + + // Make sure we are not changing the scope and state of persistent + // permission + let { state, scope } = SitePermissions.getForPrincipal( + principal, + "camera", + browser + ); + Assert.equal(state, SitePermissions.ALLOW); + Assert.equal(scope, SitePermissions.SCOPE_PERSISTENT); + + ({ state, scope } = SitePermissions.getForPrincipal( + principal, + "microphone", + browser + )); + Assert.equal(state, SitePermissions.ALLOW); + Assert.equal(scope, SitePermissions.SCOPE_PERSISTENT); + + PermissionTestUtils.remove(uri, "camera"); + PermissionTestUtils.remove(uri, "microphone"); + }, + }, +]; + +add_task(async function test() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["permissions.delegation.enabled", true], + ["dom.security.featurePolicy.header.enabled", true], + ["dom.security.featurePolicy.webidl.enabled", true], + ], + }); + + await runTests(gTests, { + relativeURI: "get_user_media_in_xorigin_frame.html", + }); +}); -- cgit v1.2.3