author | Adam Roach [:abr] <adam@nostrum.com> |
Thu, 10 Jul 2014 21:14:57 +0100 | |
changeset 193419 | 28cffea656488425f68f68c281d338827fe3a8bc |
parent 193418 | 5d91329df900fa0092387b14d2d3b01aaa529fa3 |
child 193420 | e1a037c085d13e5ac044e5385a23f701460ed27f |
push id | 27117 |
push user | ryanvm@gmail.com |
push date | Thu, 10 Jul 2014 22:23:14 +0000 |
treeherder | mozilla-central@e1a037c085d1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jesup, florian |
bugs | 1015486 |
milestone | 33.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -277,17 +277,19 @@ skip-if = e10s # Bug ?????? - [JavaScrip [browser_contextSearchTabPosition.js] skip-if = os == "mac" # bug 967013, bug 926729 [browser_ctrlTab.js] skip-if = e10s # Bug ????? - thumbnail captures need e10s love (tabPreviews_capture fails with Argument 1 of CanvasRenderingContext2D.drawWindow does not implement interface Window.) [browser_customize_popupNotification.js] [browser_datareporting_notification.js] run-if = datareporting [browser_devices_get_user_media.js] -skip-if = (os == "linux" && debug) || e10s # linux: bug 976544; e10s: Bug ?????? - appears user media notifications only happen in the child and don't make their way to the parent? +skip-if = (os == "linux" && debug) || e10s # linux: bug 976544; e10s: Bug 973001 - appears user media notifications only happen in the child and don't make their way to the parent? +[browser_devices_get_user_media_about_urls.js] +skip-if = e10s # Bug 973001 - appears user media notifications only happen in the child and don't make their way to the parent? [browser_discovery.js] skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome [browser_duplicateIDs.js] [browser_drag.js] skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638. [browser_findbarClose.js] skip-if = e10s # Bug ?????? - test directly manipulates content (tries to grab an iframe directly from content) [browser_fullscreen-window-open.js]
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js @@ -0,0 +1,260 @@ +/* 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 gTab; + +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(); + info("Waiting for " + aTopic); + + Services.obs.addObserver(function observer(aSubject, topic, aData) { + ok(true, "got " + aTopic + " notification"); + info("Message: " + aData); + 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 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(); + info("Waiting for " + aName + " to be removed"); + + waitForCondition(() => !PopupNotifications.getNotification(aName), + () => { + ok(!PopupNotifications.getNotification(aName), + aName + " notification removed"); + deferred.resolve(); + }, "timeout waiting for popup notification " + aName + " to disappear"); + + 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 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(aAlreadyClosed) { + expectNoObserverCalled(); + + info("closing the stream"); + content.wrappedJSObject.closeStream(); + + if (!aAlreadyClosed) + yield promiseObserverCalled("recording-device-events"); + + yield promiseNoPopupNotification("webRTC-sharingDevices"); + if (!aAlreadyClosed) + expectObserverCalled("recording-window-ended"); + + let statusButton = document.getElementById("webrtc-status-button"); + ok(statusButton.hidden, "WebRTC status button hidden"); +} + +function loadPage(aUrl) { + let deferred = Promise.defer(); + + gTab.linkedBrowser.addEventListener("load", function onload() { + gTab.linkedBrowser.removeEventListener("load", onload, true); + + is(PopupNotifications._currentNotifications.length, 0, + "should start the test without any prior popup notification"); + + deferred.resolve(); + }, true); + content.location = aUrl; + return deferred.promise; +} + +// A fake about module to map get_user_media.html to different about urls. +function fakeLoopAboutModule() { +} + +fakeLoopAboutModule.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + newChannel: function (aURI) { + let rootDir = getRootDirectory(gTestPath); + let chan = Services.io.newChannel(rootDir + "get_user_media.html", null, null); + chan.owner = Services.scriptSecurityManager.getSystemPrincipal(); + return chan; + }, + getURIFlags: function (aURI) { + return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.ALLOW_SCRIPT | + Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; + } +}; + +let factory = XPCOMUtils._getFactory(fakeLoopAboutModule); +let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + +registerCleanupFunction(function() { + gBrowser.removeCurrentTab(); + kObservedTopics.forEach(topic => { + Services.obs.removeObserver(observer, topic); + }); + Services.prefs.clearUserPref(PREF_PERMISSION_FAKE); +}); + + +let gTests = [ + +{ + desc: "getUserMedia about:loopconversation shouldn't prompt", + run: function checkAudioVideoLoop() { + let classID = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID(); + registrar.registerFactory(classID, "", + "@mozilla.org/network/protocol/about;1?what=loopconversation", + factory); + + yield loadPage("about:loopconversation"); + + yield promiseObserverCalled("recording-device-events", () => { + info("requesting devices"); + content.wrappedJSObject.requestDevice(true, true); + }); + // Wait for the devices to actually be captured and running before + // proceeding. + yield promisePopupNotification("webRTC-sharingDevices"); + + is(getMediaCaptureState(), "CameraAndMicrophone", + "expected camera and microphone to be shared"); + + yield closeStream(); + + registrar.unregisterFactory(classID, factory); + } +}, + +{ + desc: "getUserMedia about:evil should prompt", + run: function checkAudioVideoNonLoop() { + let classID = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID(); + registrar.registerFactory(classID, "", + "@mozilla.org/network/protocol/about;1?what=evil", + factory); + + yield loadPage("about:evil"); + + yield promiseObserverCalled("getUserMedia:request", () => { + info("requesting devices"); + content.wrappedJSObject.requestDevice(true, true); + }); + + isnot(getMediaCaptureState(), "CameraAndMicrophone", + "expected camera and microphone not to be shared"); + + registrar.unregisterFactory(classID, factory); + } +}, + +]; + +function test() { + waitForExplicitFinish(); + + Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true); + + gTab = gBrowser.addTab(); + gBrowser.selectedTab = gTab; + + kObservedTopics.forEach(topic => { + Services.obs.addObserver(observer, topic, false); + }); + + 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(); + }); +} + +function wait(time) { + let deferred = Promise.defer(); + setTimeout(deferred.resolve, time); + return deferred.promise; +}
--- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -19,16 +19,17 @@ EXTRA_JS_MODULES += [ 'offlineAppCache.jsm', 'PanelFrame.jsm', 'RemotePrompt.jsm', 'SharedFrame.jsm', 'SitePermissions.jsm', 'Social.jsm', 'TabCrashReporter.jsm', 'WebappManager.jsm', + 'webrtcUI.jsm', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': EXTRA_JS_MODULES += [ 'Windows8WindowFrameColor.jsm', 'WindowsJumpLists.jsm', 'WindowsPreviewPerTab.jsm', ] @@ -37,13 +38,12 @@ if CONFIG['NIGHTLY_BUILD']: EXTRA_JS_MODULES += [ 'SignInToWebsite.jsm', ] EXTRA_PP_JS_MODULES += [ 'AboutHome.jsm', 'RecentWindow.jsm', 'UITour.jsm', - 'webrtcUI.jsm', ] if CONFIG['MOZILLA_OFFICIAL']: DEFINES['MOZILLA_OFFICIAL'] = 1
--- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -123,38 +123,18 @@ function prompt(aContentWindow, aCallID, return; } let uri = aContentWindow.document.documentURIObject; let browser = getBrowserForWindow(aContentWindow); let chromeDoc = browser.ownerDocument; let chromeWin = chromeDoc.defaultView; let stringBundle = chromeWin.gNavigatorBundle; -#ifdef MOZ_LOOP - let host; - // For Loop protocols that start with about:, use brandShortName instead of the host for now. - // Bug 990678 will implement improvements/replacements for the permissions dialog, so this - // should become unnecessary. - if (uri.spec.startsWith("about:loop")) { - let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); - - host = brandBundle.GetStringFromName("brandShortName"); - } - else { - // uri.host throws for about: protocols, so we have to do this once we know - // it isn't about:loop. - host = uri.host; - } - - let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message", - [ host ]); -#else let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message", [ uri.host ]); -#endif let mainAction = { label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1, stringBundle.getString("getUserMedia.shareSelectedDevices.label")), accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"), // The real callback will be set during the "showing" event. The // empty function here is so that PopupNotifications.show doesn't // reject the action.
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -18,16 +18,17 @@ #include "nsIScriptGlobalObject.h" #include "nsIPermissionManager.h" #include "nsIPopupWindowManager.h" #include "nsISupportsArray.h" #include "nsIDocShell.h" #include "nsIDocument.h" #include "nsISupportsPrimitives.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsNetUtil.h" #include "mozilla/Types.h" #include "mozilla/PeerIdentity.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/MediaStreamBinding.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "mozilla/dom/GetUserMediaRequestBinding.h" #include "mozilla/Preferences.h" #include "MediaTrackConstraints.h" @@ -1472,23 +1473,38 @@ MediaManager::GetUserMedia(bool aPrivile #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) if (c.mPicture) { // ShowFilePickerForMimeType() must run on the Main Thread! (on Android) NS_DispatchToMainThread(runnable); return NS_OK; } #endif + nsIURI* docURI = aWindow->GetDocumentURI(); +#ifdef MOZ_LOOP + { + bool isLoop = false; + nsCOMPtr<nsIURI> loopURI; + nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation"); + NS_ENSURE_SUCCESS(rv, rv); + rv = docURI->EqualsExceptRef(loopURI, &isLoop); + NS_ENSURE_SUCCESS(rv, rv); + + if (isLoop) { + aPrivileged = true; + } + } +#endif + // XXX No full support for picture in Desktop yet (needs proper UI) if (aPrivileged || (c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) { mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); } else { bool isHTTPS = false; - nsIURI* docURI = aWindow->GetDocumentURI(); if (docURI) { docURI->SchemeIs("https", &isHTTPS); } // Check if this site has persistent permissions. nsresult rv; nsCOMPtr<nsIPermissionManager> permManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);