Loading browser/actors/WebRTCChild.jsm +7 −1 Original line number Diff line number Diff line Loading @@ -240,7 +240,13 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec } function denyGUMRequest(aData) { Services.obs.notifyObservers(null, "getUserMedia:response:deny", aData.callID); let subject; if (aData.noOSPermission) { subject = "getUserMedia:response:noOSPermission"; } else { subject = "getUserMedia:response:deny"; } Services.obs.notifyObservers(null, subject, aData.callID); if (!aData.windowID) return; Loading browser/base/content/test/urlbar/browser.ini +4 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,10 @@ support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs [browser_urlbarOneOffs_searchSuggestions.js] support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs [browser_urlbarOneOffs_settings.js] support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs Loading browser/base/content/test/urlbar/browser_urlbarOneOffs_settings.js 0 → 100644 +78 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; let gMaxResults; add_task(async function init() { Services.prefs.setBoolPref("browser.urlbar.oneOffSearches", true); gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); // Add a search suggestion engine and move it to the front so that it appears // as the first one-off. let engine = await SearchTestUtils.promiseNewSearchEngine( getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME); Services.search.moveEngine(engine, 0); registerCleanupFunction(async function() { await hidePopup(); await PlacesUtils.history.clear(); }); await PlacesUtils.history.clear(); let visits = []; for (let i = 0; i < gMaxResults; i++) { visits.push({ uri: makeURI("http://example.com/browser_urlbarOneOffs.js/?" + i), // TYPED so that the visit shows up when the urlbar's drop-down arrow is // pressed. transition: Ci.nsINavHistoryService.TRANSITION_TYPED, }); } await PlacesTestUtils.addVisits(visits); }); async function selectSettings(activateFn) { await BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, async browser => { gURLBar.focus(); EventUtils.synthesizeKey("KEY_ArrowDown"); await promisePopupShown(gURLBar.popup); await waitForAutocompleteResultAt(gMaxResults - 1); let promiseHidden = promisePopupHidden(gURLBar.popup); let prefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded", () => true); activateFn(); await prefPaneLoaded; await promiseHidden; Assert.equal(gBrowser.contentWindow.history.state, "paneSearch", "Should have opened the search preferences pane"); }); } add_task(async function test_open_settings_with_enter() { await selectSettings(() => { EventUtils.synthesizeKey("KEY_ArrowUp"); Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButton.getAttribute("anonid"), "search-settings-compact", "Should have selected the settings button"); EventUtils.synthesizeKey("KEY_Enter"); }); }); add_task(async function test_open_settings_with_click() { await selectSettings(() => { gURLBar.popup.oneOffSearchButtons.settingsButton.click(); }); }); async function hidePopup() { EventUtils.synthesizeKey("KEY_Escape"); await promisePopupHidden(gURLBar.popup); } browser/base/content/urlbarBindings.xml +3 −3 Original line number Diff line number Diff line Loading @@ -652,9 +652,6 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. return; } BrowserUsageTelemetry.recordUrlbarSelectedResultMethod( event, this.userSelectionBehavior); // Determine whether to use the selected one-off search button. In // one-off search buttons parlance, "selected" means that the button // has been navigated to via the keyboard. So we want to use it if Loading @@ -673,6 +670,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. return; } BrowserUsageTelemetry.recordUrlbarSelectedResultMethod( event, this.userSelectionBehavior); let where = openUILinkWhere || this._whereToOpen(event); let url = this.value; Loading browser/modules/webrtcUI.jsm +87 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() { return Services.strings.createBundle("chrome://branding/locale/brand.properties"); }); XPCOMUtils.defineLazyServiceGetter(this, "OSPermissions", "@mozilla.org/ospermissionrequest;1", "nsIOSPermissionRequest"); var webrtcUI = { peerConnectionBlockers: new Set(), emitter: new EventEmitter(), Loading Loading @@ -300,6 +304,67 @@ function denyRequest(aBrowser, aRequest) { windowID: aRequest.windowID}); } // // Deny the request because the browser does not have access to the // camera or microphone due to OS security restrictions. The user may // have granted camera/microphone access to the site, but not have // allowed the browser access in OS settings. // function denyRequestNoPermission(aBrowser, aRequest) { aBrowser.messageManager.sendAsyncMessage("webrtc:Deny", {callID: aRequest.callID, windowID: aRequest.windowID, noOSPermission: true}); } // // Check if we have permission to access the camera and or microphone at the // OS level. Triggers a request to access the device if access is needed and // the permission state has not yet been determined. // async function checkOSPermission(camNeeded, micNeeded) { let camStatus = {}, micStatus = {}; OSPermissions.getMediaCapturePermissionState(camStatus, micStatus); if (camNeeded) { let camPermission = camStatus.value; let camAccessible = await checkAndGetOSPermission(camPermission, OSPermissions.requestVideoCapturePermission); if (!camAccessible) { return false; } } if (micNeeded) { let micPermission = micStatus.value; let micAccessible = await checkAndGetOSPermission(micPermission, OSPermissions.requestAudioCapturePermission); if (!micAccessible) { return false; } } return true; } // // Given a device's permission, return true if the device is accessible. If // the device's permission is not yet determined, request access to the device. // |requestPermissionFunc| must return a promise that resolves with true // if the device is accessible and false otherwise. // async function checkAndGetOSPermission(devicePermission, requestPermissionFunc) { if (devicePermission == OSPermissions.PERMISSION_STATE_DENIED || devicePermission == OSPermissions.PERMISSION_STATE_RESTRICTED) { return false; } if (devicePermission == OSPermissions.PERMISSION_STATE_NOTDETERMINED) { let deviceAllowed = await requestPermissionFunc(); if (!deviceAllowed) { return false; } } return true; } function getHostOrExtensionName(uri, href) { let host; try { Loading Loading @@ -517,10 +582,19 @@ function prompt(aBrowser, aRequest) { browser._devicePermissionURIs = browser._devicePermissionURIs || []; browser._devicePermissionURIs.push(uri); let camNeeded = videoDevices.length > 0; let micNeeded = audioDevices.length > 0; checkOSPermission(camNeeded, micNeeded).then((havePermission) => { if (havePermission) { let mm = browser.messageManager; mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID, windowID: aRequest.windowID, devices: allowedDevices}); } else { denyRequestNoPermission(browser, aRequest); } }); this.remove(); return true; } Loading Loading @@ -717,7 +791,7 @@ function prompt(aBrowser, aRequest) { if (!sharingAudio) listDevices(micMenupopup, audioDevices); this.mainAction.callback = function(aState) { this.mainAction.callback = async function(aState) { let remember = aState && aState.checkboxChecked; let allowedDevices = []; let perms = Services.perms; Loading Loading @@ -784,6 +858,14 @@ function prompt(aBrowser, aRequest) { aBrowser._devicePermissionURIs.push(uri); } let camNeeded = videoDevices.length > 0; let micNeeded = audioDevices.length > 0; let havePermission = await checkOSPermission(camNeeded, micNeeded); if (!havePermission) { denyRequestNoPermission(notification.browser, aRequest); return; } let mm = notification.browser.messageManager; mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID, windowID: aRequest.windowID, Loading Loading
browser/actors/WebRTCChild.jsm +7 −1 Original line number Diff line number Diff line Loading @@ -240,7 +240,13 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec } function denyGUMRequest(aData) { Services.obs.notifyObservers(null, "getUserMedia:response:deny", aData.callID); let subject; if (aData.noOSPermission) { subject = "getUserMedia:response:noOSPermission"; } else { subject = "getUserMedia:response:deny"; } Services.obs.notifyObservers(null, subject, aData.callID); if (!aData.windowID) return; Loading
browser/base/content/test/urlbar/browser.ini +4 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,10 @@ support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs [browser_urlbarOneOffs_searchSuggestions.js] support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs [browser_urlbarOneOffs_settings.js] support-files = searchSuggestionEngine.xml searchSuggestionEngine.sjs Loading
browser/base/content/test/urlbar/browser_urlbarOneOffs_settings.js 0 → 100644 +78 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; let gMaxResults; add_task(async function init() { Services.prefs.setBoolPref("browser.urlbar.oneOffSearches", true); gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); // Add a search suggestion engine and move it to the front so that it appears // as the first one-off. let engine = await SearchTestUtils.promiseNewSearchEngine( getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME); Services.search.moveEngine(engine, 0); registerCleanupFunction(async function() { await hidePopup(); await PlacesUtils.history.clear(); }); await PlacesUtils.history.clear(); let visits = []; for (let i = 0; i < gMaxResults; i++) { visits.push({ uri: makeURI("http://example.com/browser_urlbarOneOffs.js/?" + i), // TYPED so that the visit shows up when the urlbar's drop-down arrow is // pressed. transition: Ci.nsINavHistoryService.TRANSITION_TYPED, }); } await PlacesTestUtils.addVisits(visits); }); async function selectSettings(activateFn) { await BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, async browser => { gURLBar.focus(); EventUtils.synthesizeKey("KEY_ArrowDown"); await promisePopupShown(gURLBar.popup); await waitForAutocompleteResultAt(gMaxResults - 1); let promiseHidden = promisePopupHidden(gURLBar.popup); let prefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded", () => true); activateFn(); await prefPaneLoaded; await promiseHidden; Assert.equal(gBrowser.contentWindow.history.state, "paneSearch", "Should have opened the search preferences pane"); }); } add_task(async function test_open_settings_with_enter() { await selectSettings(() => { EventUtils.synthesizeKey("KEY_ArrowUp"); Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButton.getAttribute("anonid"), "search-settings-compact", "Should have selected the settings button"); EventUtils.synthesizeKey("KEY_Enter"); }); }); add_task(async function test_open_settings_with_click() { await selectSettings(() => { gURLBar.popup.oneOffSearchButtons.settingsButton.click(); }); }); async function hidePopup() { EventUtils.synthesizeKey("KEY_Escape"); await promisePopupHidden(gURLBar.popup); }
browser/base/content/urlbarBindings.xml +3 −3 Original line number Diff line number Diff line Loading @@ -652,9 +652,6 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. return; } BrowserUsageTelemetry.recordUrlbarSelectedResultMethod( event, this.userSelectionBehavior); // Determine whether to use the selected one-off search button. In // one-off search buttons parlance, "selected" means that the button // has been navigated to via the keyboard. So we want to use it if Loading @@ -673,6 +670,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. return; } BrowserUsageTelemetry.recordUrlbarSelectedResultMethod( event, this.userSelectionBehavior); let where = openUILinkWhere || this._whereToOpen(event); let url = this.value; Loading
browser/modules/webrtcUI.jsm +87 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() { return Services.strings.createBundle("chrome://branding/locale/brand.properties"); }); XPCOMUtils.defineLazyServiceGetter(this, "OSPermissions", "@mozilla.org/ospermissionrequest;1", "nsIOSPermissionRequest"); var webrtcUI = { peerConnectionBlockers: new Set(), emitter: new EventEmitter(), Loading Loading @@ -300,6 +304,67 @@ function denyRequest(aBrowser, aRequest) { windowID: aRequest.windowID}); } // // Deny the request because the browser does not have access to the // camera or microphone due to OS security restrictions. The user may // have granted camera/microphone access to the site, but not have // allowed the browser access in OS settings. // function denyRequestNoPermission(aBrowser, aRequest) { aBrowser.messageManager.sendAsyncMessage("webrtc:Deny", {callID: aRequest.callID, windowID: aRequest.windowID, noOSPermission: true}); } // // Check if we have permission to access the camera and or microphone at the // OS level. Triggers a request to access the device if access is needed and // the permission state has not yet been determined. // async function checkOSPermission(camNeeded, micNeeded) { let camStatus = {}, micStatus = {}; OSPermissions.getMediaCapturePermissionState(camStatus, micStatus); if (camNeeded) { let camPermission = camStatus.value; let camAccessible = await checkAndGetOSPermission(camPermission, OSPermissions.requestVideoCapturePermission); if (!camAccessible) { return false; } } if (micNeeded) { let micPermission = micStatus.value; let micAccessible = await checkAndGetOSPermission(micPermission, OSPermissions.requestAudioCapturePermission); if (!micAccessible) { return false; } } return true; } // // Given a device's permission, return true if the device is accessible. If // the device's permission is not yet determined, request access to the device. // |requestPermissionFunc| must return a promise that resolves with true // if the device is accessible and false otherwise. // async function checkAndGetOSPermission(devicePermission, requestPermissionFunc) { if (devicePermission == OSPermissions.PERMISSION_STATE_DENIED || devicePermission == OSPermissions.PERMISSION_STATE_RESTRICTED) { return false; } if (devicePermission == OSPermissions.PERMISSION_STATE_NOTDETERMINED) { let deviceAllowed = await requestPermissionFunc(); if (!deviceAllowed) { return false; } } return true; } function getHostOrExtensionName(uri, href) { let host; try { Loading Loading @@ -517,10 +582,19 @@ function prompt(aBrowser, aRequest) { browser._devicePermissionURIs = browser._devicePermissionURIs || []; browser._devicePermissionURIs.push(uri); let camNeeded = videoDevices.length > 0; let micNeeded = audioDevices.length > 0; checkOSPermission(camNeeded, micNeeded).then((havePermission) => { if (havePermission) { let mm = browser.messageManager; mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID, windowID: aRequest.windowID, devices: allowedDevices}); } else { denyRequestNoPermission(browser, aRequest); } }); this.remove(); return true; } Loading Loading @@ -717,7 +791,7 @@ function prompt(aBrowser, aRequest) { if (!sharingAudio) listDevices(micMenupopup, audioDevices); this.mainAction.callback = function(aState) { this.mainAction.callback = async function(aState) { let remember = aState && aState.checkboxChecked; let allowedDevices = []; let perms = Services.perms; Loading Loading @@ -784,6 +858,14 @@ function prompt(aBrowser, aRequest) { aBrowser._devicePermissionURIs.push(uri); } let camNeeded = videoDevices.length > 0; let micNeeded = audioDevices.length > 0; let havePermission = await checkOSPermission(camNeeded, micNeeded); if (!havePermission) { denyRequestNoPermission(notification.browser, aRequest); return; } let mm = notification.browser.messageManager; mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID, windowID: aRequest.windowID, Loading