Bug 1369108 - 1. Introduce optional device permission step; r=jesup
authorJim Chen <nchen@mozilla.com>
Fri, 02 Jun 2017 16:11:53 -0400
changeset 412630 483b253cdd73802d4de006eedc5a543b4339a3e7
parent 412629 a3f93870b233106e15e8ff90dfa41f0c72241e0c
child 412631 54c93d5eb3089ed12163a7308edc003cdb41cf7a
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1369108
milestone55.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
Bug 1369108 - 1. Introduce optional device permission step; r=jesup Right before `MediaManager::EnumerateRawDevices` posts its enumeration task to the background thread, add the option to send out a "getUserMedia:ask-device-permission" notification, whose purpose is to give frontend UI a chance to ask for device permissions (e.g. permission to enumerate cameras on Android). The subject parameter of the notification is an `nsIRunnable` task, which is passed back to `MediaManager`, after receiving permission, through the `getUserMedia:got-device-permission` notification. The notification listener then posts the task and enumeration proceeds as before. MozReview-Commit-ID: IhRXTdIplvN
dom/media/MediaManager.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1694,19 +1694,19 @@ MediaManager::EnumerateRawDevices(uint64
     if (aVideoType == MediaSourceEnum::Camera) {
       videoLoopDev = Preferences::GetCString("media.video_loopback_dev");
     }
     if (aAudioType == MediaSourceEnum::Microphone) {
       audioLoopDev = Preferences::GetCString("media.audio_loopback_dev");
     }
   }
 
-  MediaManager::PostTask(NewTaskFrom([id, aWindowId, audioLoopDev,
-                                      videoLoopDev, aVideoType,
-                                      aAudioType, aFake]() mutable {
+  RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, audioLoopDev,
+                                       videoLoopDev, aVideoType,
+                                       aAudioType, aFake]() mutable {
     // Only enumerate what's asked for, and only fake cams and mics.
     bool hasVideo = aVideoType != MediaSourceEnum::Other;
     bool hasAudio = aAudioType != MediaSourceEnum::Other;
     bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera;
     bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone;
 
     RefPtr<MediaEngine> fakeBackend, realBackend;
     if (fakeCams || fakeMics) {
@@ -1743,17 +1743,38 @@ MediaManager::EnumerateRawDevices(uint64
         return NS_OK;
       }
       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
       if (p) {
         p->Resolve(result.release());
       }
       return NS_OK;
     }));
-  }));
+  });
+
+  if (!aFake &&
+      (aVideoType == MediaSourceEnum::Camera ||
+       aAudioType == MediaSourceEnum::Microphone) &&
+      Preferences::GetBool("media.navigator.permission.device", false)) {
+    // Need to ask permission to retrieve list of all devices;
+    // notify frontend observer and wait for callback notification to post task.
+    const char16_t* const type =
+      (aVideoType != MediaSourceEnum::Camera)     ? u"audio" :
+      (aAudioType != MediaSourceEnum::Microphone) ? u"video" :
+                                                    u"all";
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    obs->NotifyObservers(static_cast<nsIRunnable*>(task),
+                         "getUserMedia:ask-device-permission",
+                         type);
+  } else {
+    // Don't need to ask permission to retrieve list of all devices;
+    // post the retrieval task immediately.
+    MediaManager::PostTask(task.forget());
+  }
+
   return p.forget();
 }
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mBackend(nullptr) {
   mPrefs.mFreq         = 1000; // 1KHz test tone
   mPrefs.mWidth        = 0; // adaptive default
@@ -1836,16 +1857,17 @@ MediaManager::Get() {
       MOZ_CRASH();
     }
 
     LOG(("New Media thread for gum"));
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->AddObserver(sSingleton, "last-pb-context-exited", false);
+      obs->AddObserver(sSingleton, "getUserMedia:got-device-permission", false);
       obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
       obs->AddObserver(sSingleton, "phone-state-changed", false);
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -3014,16 +3036,23 @@ MediaManager::Observe(nsISupports* aSubj
       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
       LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__,
            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
     }
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
     // Clear memory of private-browsing-specific deviceIds. Fire and forget.
     media::SanitizeOriginKeys(0, true);
     return NS_OK;
+  } else if (!strcmp(aTopic, "getUserMedia:got-device-permission")) {
+    MOZ_ASSERT(aSubject);
+    nsCOMPtr<nsIRunnable> task = do_QueryInterface(aSubject);
+    MediaManager::PostTask(NewTaskFrom([task] {
+      task->Run();
+    }));
+    return NS_OK;
   } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
              !strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     RefPtr<GetUserMediaTask> task;
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (!task) {
       return NS_OK;
     }