Commit 4d49808e authored by Florian Quèze's avatar Florian Quèze
Browse files

Bug 1000253 - Background tabs with persistent device permissions can access...

Bug 1000253 - Background tabs with persistent device permissions can access devices without the user noticing, r=jesup,MattN, a=sylvestre.
parent d6381841
Loading
Loading
Loading
Loading
+8 −4
Changes for browser/base/content/test/general/browser_devices_get_user_media.js: 8 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -589,11 +589,13 @@ let gTests = [
        expectObserverCalled("recording-window-ended");
      }
      else {
        let allow = (aAllowVideo && aRequestVideo) || (aAllowAudio && aRequestAudio);
        let expectedMessage = allow ? "ok" : "error: PERMISSION_DENIED";
        let expectedMessage = aExpectStream ? "ok" : "error: PERMISSION_DENIED";
        yield promiseMessage(expectedMessage, gum);

        if (expectedMessage == "ok") {
          expectObserverCalled("getUserMedia:request");
          yield promiseNoPopupNotification("webRTC-shareDevices");
          expectObserverCalled("getUserMedia:response:allow");
          expectObserverCalled("recording-device-events");

          // Check what's actually shared.
@@ -635,9 +637,9 @@ let gTests = [
    info("deny audio, allow video, request audio+video, expect ok (video)");
    yield usePerm(false, true, true, true, true);
    info("deny audio, allow video, request audio, expect denied");
    yield usePerm(false, true, true, false, true);
    yield usePerm(false, true, true, false, false);
    info("deny audio, allow video, request video, expect ok (video)");
    yield usePerm(false, true, false, true, false);
    yield usePerm(false, true, false, true, true);

    // Allow audio, video not set.
    info("allow audio, request audio+video, expect prompt");
@@ -688,6 +690,8 @@ let gTests = [
      yield promiseMessage("ok", () => {
        content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
      });
      expectObserverCalled("getUserMedia:request");
      expectObserverCalled("getUserMedia:response:allow");
      expectObserverCalled("recording-device-events");
      yield checkSharingUI();

+32 −0
Changes for browser/modules/webrtcUI.jsm: 32 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -192,6 +192,38 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
      if (aTopic != "showing")
        return false;

      // DENY_ACTION is handled immediately by MediaManager, but handling
      // of ALLOW_ACTION is delayed until the popupshowing event
      // to avoid granting permissions automatically to background tabs.
      if (aSecure) {
        let perms = Services.perms;

        let micPerm = perms.testExactPermission(uri, "microphone");
        if (micPerm == perms.PROMPT_ACTION)
          micPerm = perms.UNKNOWN_ACTION;

        let camPerm = perms.testExactPermission(uri, "camera");
        if (camPerm == perms.PROMPT_ACTION)
          camPerm = perms.UNKNOWN_ACTION;

        // We don't check that permissions are set to ALLOW_ACTION in this
        // test; only that they are set. This is because if audio is allowed
        // and video is denied persistently, we don't want to show the prompt,
        // and will grant audio access immediately.
        if ((!audioDevices.length || micPerm) && (!videoDevices.length || camPerm)) {
          // All permissions we were about to request are already persistently set.
          let allowedDevices = Cc["@mozilla.org/supports-array;1"]
                                 .createInstance(Ci.nsISupportsArray);
          if (videoDevices.length && camPerm == perms.ALLOW_ACTION)
            allowedDevices.AppendElement(videoDevices[0]);
          if (audioDevices.length && micPerm == perms.ALLOW_ACTION)
            allowedDevices.AppendElement(audioDevices[0]);
          Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
          this.remove();
          return true;
        }
      }

      function listDevices(menupopup, devices) {
        while (menupopup.lastChild)
          menupopup.removeChild(menupopup.lastChild);
+2 −32
Changes for dom/media/MediaManager.cpp: 2 added lines, 32 removed lines.
Original line number Diff line number Diff line
@@ -1471,14 +1471,6 @@ MediaManager::GetUserMedia(bool aPrivileged,
      rv = permManager->TestExactPermissionFromPrincipal(
        aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm);
      NS_ENSURE_SUCCESS(rv, rv);
      if (audioPerm == nsIPermissionManager::PROMPT_ACTION) {
        audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
      }
      if (audioPerm == nsIPermissionManager::ALLOW_ACTION) {
        if (!isHTTPS) {
          audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
        }
      }
    }

    uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
@@ -1486,35 +1478,13 @@ MediaManager::GetUserMedia(bool aPrivileged,
      rv = permManager->TestExactPermissionFromPrincipal(
        aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm);
      NS_ENSURE_SUCCESS(rv, rv);
      if (videoPerm == nsIPermissionManager::PROMPT_ACTION) {
        videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
      }
      if (videoPerm == nsIPermissionManager::ALLOW_ACTION) {
        if (!isHTTPS) {
          videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
        }
      }
    }

    if ((!IsOn(c.mAudio) || audioPerm != nsIPermissionManager::UNKNOWN_ACTION) &&
        (!IsOn(c.mVideo) || videoPerm != nsIPermissionManager::UNKNOWN_ACTION)) {
      // All permissions we were about to request already have a saved value.
      if (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) {
        c.mAudio.SetAsBoolean() = false;
        runnable->SetContraints(c);
      }
      if (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION) {
        c.mVideo.SetAsBoolean() = false;
        runnable->SetContraints(c);
      }

      if (!IsOn(c.mAudio) && !IsOn(c.mVideo)) {
    if ((!IsOn(c.mAudio) || audioPerm == nsIPermissionManager::DENY_ACTION) &&
        (!IsOn(c.mVideo) || videoPerm == nsIPermissionManager::DENY_ACTION)) {
      return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
    }

      return mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
    }

    // Ask for user permission, and dispatch runnable (or not) when a response
    // is received via an observer notification. Each call is paired with its
    // runnable by a GUID.