dom/media/MediaPermissionGonk.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a6104d918442
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "MediaManager.h"
6 #include "MediaPermissionGonk.h"
7
8 #include "nsCOMPtr.h"
9 #include "nsCxPusher.h"
10 #include "nsIContentPermissionPrompt.h"
11 #include "nsIDocument.h"
12 #include "nsIDOMNavigatorUserMedia.h"
13 #include "nsIStringEnumerator.h"
14 #include "nsISupportsArray.h"
15 #include "nsJSUtils.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsTArray.h"
18 #include "GetUserMediaRequest.h"
19 #include "PCOMContentPermissionRequestChild.h"
20 #include "mozilla/dom/PBrowserChild.h"
21 #include "mozilla/dom/TabChild.h"
22 #include "mozilla/dom/MediaStreamTrackBinding.h"
23 #include "nsISupportsPrimitives.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsArrayUtils.h"
26 #include "nsContentPermissionHelper.h"
27 #include "mozilla/dom/PermissionMessageUtils.h"
28
29 #define AUDIO_PERMISSION_NAME "audio-capture"
30 #define VIDEO_PERMISSION_NAME "video-capture"
31
32 using namespace mozilla::dom;
33
34 namespace mozilla {
35
36 static MediaPermissionManager *gMediaPermMgr = nullptr;
37
38 static uint32_t
39 ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
40 nsTArray<PermissionRequest>& aDesArray)
41 {
42 uint32_t len = 0;
43 aSrcArray->GetLength(&len);
44 for (uint32_t i = 0; i < len; i++) {
45 nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
46 nsAutoCString type;
47 nsAutoCString access;
48 cpt->GetType(type);
49 cpt->GetAccess(access);
50
51 nsCOMPtr<nsIArray> optionArray;
52 cpt->GetOptions(getter_AddRefs(optionArray));
53 uint32_t optionsLength = 0;
54 optionArray->GetLength(&optionsLength);
55 nsTArray<nsString> options;
56 for (uint32_t j = 0; j < optionsLength; ++j) {
57 nsCOMPtr<nsISupportsString> isupportsString = do_QueryElementAt(optionArray, j);
58 if (isupportsString) {
59 nsString option;
60 isupportsString->GetData(option);
61 options.AppendElement(option);
62 }
63 }
64
65 aDesArray.AppendElement(PermissionRequest(type, access, options));
66 }
67 return len;
68 }
69
70 static void
71 CreateDeviceNameList(nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices,
72 nsTArray<nsString> &aDeviceNameList)
73 {
74 for (uint32_t i = 0; i < aDevices.Length(); ++i) {
75 nsString name;
76 nsresult rv = aDevices[i]->GetName(name);
77 NS_ENSURE_SUCCESS_VOID(rv);
78 aDeviceNameList.AppendElement(name);
79 }
80 }
81
82 static already_AddRefed<nsIMediaDevice>
83 FindDeviceByName(nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices,
84 const nsAString &aDeviceName)
85 {
86 for (uint32_t i = 0; i < aDevices.Length(); ++i) {
87 nsCOMPtr<nsIMediaDevice> device = aDevices[i];
88 nsString deviceName;
89 device->GetName(deviceName);
90 if (deviceName.Equals(aDeviceName)) {
91 return device.forget();
92 }
93 }
94
95 return nullptr;
96 }
97
98 // Helper function for notifying permission granted
99 static nsresult
100 NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
101 {
102 nsresult rv;
103 nsCOMPtr<nsISupportsArray> array;
104 rv = NS_NewISupportsArray(getter_AddRefs(array));
105 NS_ENSURE_SUCCESS(rv, rv);
106
107 for (uint32_t i = 0; i < aDevices.Length(); ++i) {
108 rv = array->AppendElement(aDevices.ElementAt(i));
109 NS_ENSURE_SUCCESS(rv, rv);
110 }
111
112 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
113 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
114
115 return obs->NotifyObservers(array, "getUserMedia:response:allow",
116 aCallID.BeginReading());
117 }
118
119 // Helper function for notifying permision denial or error
120 static nsresult
121 NotifyPermissionDeny(const nsAString &aCallID, const nsAString &aErrorMsg)
122 {
123 nsresult rv;
124 nsCOMPtr<nsISupportsString> supportsString =
125 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
126 NS_ENSURE_SUCCESS(rv, rv);
127
128 rv = supportsString->SetData(aErrorMsg);
129 NS_ENSURE_SUCCESS(rv, rv);
130
131 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
132 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
133
134 return obs->NotifyObservers(supportsString, "getUserMedia:response:deny",
135 aCallID.BeginReading());
136 }
137
138 namespace {
139
140 /**
141 * MediaPermissionRequest will send a prompt ipdl request to b2g process according
142 * to its owned type.
143 */
144 class MediaPermissionRequest : public nsIContentPermissionRequest
145 , public PCOMContentPermissionRequestChild
146 {
147 public:
148 NS_DECL_ISUPPORTS
149 NS_DECL_NSICONTENTPERMISSIONREQUEST
150
151 MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
152 nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices);
153 virtual ~MediaPermissionRequest() {}
154
155 // It will be called when prompt dismissed.
156 virtual bool Recv__delete__(const bool &allow,
157 const InfallibleTArray<PermissionChoice>& choices) MOZ_OVERRIDE;
158 virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
159
160 already_AddRefed<nsPIDOMWindow> GetOwner();
161
162 private:
163 nsresult DoAllow(const nsString &audioDevice, const nsString &videoDevice);
164
165 bool mAudio; // Request for audio permission
166 bool mVideo; // Request for video permission
167 nsRefPtr<dom::GetUserMediaRequest> mRequest;
168 nsTArray<nsCOMPtr<nsIMediaDevice> > mAudioDevices; // candidate audio devices
169 nsTArray<nsCOMPtr<nsIMediaDevice> > mVideoDevices; // candidate video devices
170 };
171
172 // MediaPermissionRequest
173 NS_IMPL_ISUPPORTS(MediaPermissionRequest, nsIContentPermissionRequest)
174
175 MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
176 nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
177 : mRequest(aRequest)
178 {
179 dom::MediaStreamConstraints constraints;
180 mRequest->GetConstraints(constraints);
181
182 mAudio = !constraints.mAudio.IsBoolean() || constraints.mAudio.GetAsBoolean();
183 mVideo = !constraints.mVideo.IsBoolean() || constraints.mVideo.GetAsBoolean();
184
185 for (uint32_t i = 0; i < aDevices.Length(); ++i) {
186 nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
187 nsAutoString deviceType;
188 device->GetType(deviceType);
189 if (mAudio && deviceType.EqualsLiteral("audio")) {
190 mAudioDevices.AppendElement(device);
191 }
192 if (mVideo && deviceType.EqualsLiteral("video")) {
193 mVideoDevices.AppendElement(device);
194 }
195 }
196 }
197
198 // nsIContentPermissionRequest methods
199 NS_IMETHODIMP
200 MediaPermissionRequest::GetTypes(nsIArray** aTypes)
201 {
202 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
203 //XXX append device list
204 if (mAudio) {
205 nsTArray<nsString> audioDeviceNames;
206 CreateDeviceNameList(mAudioDevices, audioDeviceNames);
207 nsCOMPtr<nsISupports> AudioType =
208 new ContentPermissionType(NS_LITERAL_CSTRING(AUDIO_PERMISSION_NAME),
209 NS_LITERAL_CSTRING("unused"),
210 audioDeviceNames);
211 types->AppendElement(AudioType, false);
212 }
213 if (mVideo) {
214 nsTArray<nsString> videoDeviceNames;
215 CreateDeviceNameList(mVideoDevices, videoDeviceNames);
216 nsCOMPtr<nsISupports> VideoType =
217 new ContentPermissionType(NS_LITERAL_CSTRING(VIDEO_PERMISSION_NAME),
218 NS_LITERAL_CSTRING("unused"),
219 videoDeviceNames);
220 types->AppendElement(VideoType, false);
221 }
222 NS_IF_ADDREF(*aTypes = types);
223
224 return NS_OK;
225 }
226
227 NS_IMETHODIMP
228 MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
229 {
230 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
231
232 nsCOMPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
233 (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID()));
234 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
235
236 nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
237 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
238
239 NS_ADDREF(*aRequestingPrincipal = doc->NodePrincipal());
240 return NS_OK;
241 }
242
243 NS_IMETHODIMP
244 MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
245 {
246 NS_ENSURE_ARG_POINTER(aRequestingWindow);
247 nsCOMPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
248 (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID()));
249 window.forget(aRequestingWindow);
250 return NS_OK;
251 }
252
253 NS_IMETHODIMP
254 MediaPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
255 {
256 NS_ENSURE_ARG_POINTER(aRequestingElement);
257 *aRequestingElement = nullptr;
258 return NS_OK;
259 }
260
261 NS_IMETHODIMP
262 MediaPermissionRequest::Cancel()
263 {
264 nsString callID;
265 mRequest->GetCallID(callID);
266 NotifyPermissionDeny(callID, NS_LITERAL_STRING("Permission Denied"));
267 return NS_OK;
268 }
269
270 NS_IMETHODIMP
271 MediaPermissionRequest::Allow(JS::HandleValue aChoices)
272 {
273 // check if JS object
274 if (!aChoices.isObject()) {
275 MOZ_ASSERT(false, "Not a correct format of PermissionChoice");
276 return NS_ERROR_INVALID_ARG;
277 }
278 // iterate through audio-capture and video-capture
279 AutoSafeJSContext cx;
280 JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
281 JSAutoCompartment ac(cx, obj);
282 JS::Rooted<JS::Value> v(cx);
283
284 // get selected audio device name
285 nsString audioDevice;
286 if (mAudio) {
287 if (!JS_GetProperty(cx, obj, AUDIO_PERMISSION_NAME, &v) || !v.isString()) {
288 return NS_ERROR_FAILURE;
289 }
290 nsDependentJSString deviceName;
291 if (!deviceName.init(cx, v)) {
292 MOZ_ASSERT(false, "Couldn't initialize string from aChoices");
293 return NS_ERROR_FAILURE;
294 }
295 audioDevice = deviceName;
296 }
297
298 // get selected video device name
299 nsString videoDevice;
300 if (mVideo) {
301 if (!JS_GetProperty(cx, obj, VIDEO_PERMISSION_NAME, &v) || !v.isString()) {
302 return NS_ERROR_FAILURE;
303 }
304 nsDependentJSString deviceName;
305 if (!deviceName.init(cx, v)) {
306 MOZ_ASSERT(false, "Couldn't initialize string from aChoices");
307 return NS_ERROR_FAILURE;
308 }
309 videoDevice = deviceName;
310 }
311
312 return DoAllow(audioDevice, videoDevice);
313 }
314
315 nsresult
316 MediaPermissionRequest::DoAllow(const nsString &audioDevice,
317 const nsString &videoDevice)
318 {
319 nsTArray<nsCOMPtr<nsIMediaDevice> > selectedDevices;
320 if (mAudio) {
321 nsCOMPtr<nsIMediaDevice> device =
322 FindDeviceByName(mAudioDevices, audioDevice);
323 if (device) {
324 selectedDevices.AppendElement(device);
325 }
326 }
327
328 if (mVideo) {
329 nsCOMPtr<nsIMediaDevice> device =
330 FindDeviceByName(mVideoDevices, videoDevice);
331 if (device) {
332 selectedDevices.AppendElement(device);
333 }
334 }
335
336 nsString callID;
337 mRequest->GetCallID(callID);
338 return NotifyPermissionAllow(callID, selectedDevices);
339 }
340
341 already_AddRefed<nsPIDOMWindow>
342 MediaPermissionRequest::GetOwner()
343 {
344 nsCOMPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
345 (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID()));
346 return window.forget();
347 }
348
349 //PCOMContentPermissionRequestChild
350 bool
351 MediaPermissionRequest::Recv__delete__(const bool& allow,
352 const InfallibleTArray<PermissionChoice>& choices)
353 {
354 if (allow) {
355 // get selected device name for audio and video
356 nsString audioDevice, videoDevice;
357 for (uint32_t i = 0; i < choices.Length(); ++i) {
358 const nsString &choice = choices[i].choice();
359 if (choices[i].type().EqualsLiteral(AUDIO_PERMISSION_NAME)) {
360 audioDevice = choice;
361 } else if (choices[i].type().EqualsLiteral(VIDEO_PERMISSION_NAME)) {
362 videoDevice = choice;
363 }
364 }
365 (void) DoAllow(audioDevice, videoDevice);
366 } else {
367 (void) Cancel();
368 }
369 return true;
370 }
371
372 // Success callback for MediaManager::GetUserMediaDevices().
373 class MediaDeviceSuccessCallback: public nsIGetUserMediaDevicesSuccessCallback
374 {
375 public:
376 NS_DECL_ISUPPORTS
377 NS_DECL_NSIGETUSERMEDIADEVICESSUCCESSCALLBACK
378
379 MediaDeviceSuccessCallback(nsRefPtr<dom::GetUserMediaRequest> &aRequest)
380 : mRequest(aRequest) {}
381 virtual ~MediaDeviceSuccessCallback() {}
382
383 private:
384 nsresult DoPrompt(nsRefPtr<MediaPermissionRequest> &req);
385 nsRefPtr<dom::GetUserMediaRequest> mRequest;
386 };
387
388 NS_IMPL_ISUPPORTS(MediaDeviceSuccessCallback, nsIGetUserMediaDevicesSuccessCallback)
389
390 // nsIGetUserMediaDevicesSuccessCallback method
391 NS_IMETHODIMP
392 MediaDeviceSuccessCallback::OnSuccess(nsIVariant* aDevices)
393 {
394 nsIID elementIID;
395 uint16_t elementType;
396 void* rawArray;
397 uint32_t arrayLen;
398
399 nsresult rv;
400 rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
401 NS_ENSURE_SUCCESS(rv, rv);
402
403 if (elementType != nsIDataType::VTYPE_INTERFACE) {
404 NS_Free(rawArray);
405 return NS_ERROR_FAILURE;
406 }
407
408 // Create array for nsIMediaDevice
409 nsTArray<nsCOMPtr<nsIMediaDevice> > devices;
410
411 nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
412 for (uint32_t i = 0; i < arrayLen; ++i) {
413 nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
414 devices.AppendElement(device);
415 NS_IF_RELEASE(supportsArray[i]); // explicitly decrease reference count for raw pointer
416 }
417 NS_Free(rawArray); // explicitly free for the memory from nsIVariant::GetAsArray
418
419 // Send MediaPermissionRequest
420 nsRefPtr<MediaPermissionRequest> req = new MediaPermissionRequest(mRequest, devices);
421 rv = DoPrompt(req);
422
423 NS_ENSURE_SUCCESS(rv, rv);
424 return NS_OK;
425 }
426
427 // Trigger permission prompt UI
428 nsresult
429 MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
430 {
431 // for content process
432 if (XRE_GetProcessType() == GeckoProcessType_Content) {
433 MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
434
435 nsresult rv;
436
437 nsCOMPtr<nsPIDOMWindow> window(req->GetOwner());
438 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
439
440 dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
441 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
442
443 nsCOMPtr<nsIArray> typeArray;
444 rv = req->GetTypes(getter_AddRefs(typeArray));
445 NS_ENSURE_SUCCESS(rv, rv);
446
447 nsTArray<PermissionRequest> permArray;
448 ConvertArrayToPermissionRequest(typeArray, permArray);
449
450 nsCOMPtr<nsIPrincipal> principal;
451 rv = req->GetPrincipal(getter_AddRefs(principal));
452 NS_ENSURE_SUCCESS(rv, rv);
453
454 req->AddRef();
455 child->SendPContentPermissionRequestConstructor(req,
456 permArray,
457 IPC::Principal(principal));
458
459 req->Sendprompt();
460 return NS_OK;
461 }
462
463 // for chrome process
464 nsCOMPtr<nsIContentPermissionPrompt> prompt =
465 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
466 if (prompt) {
467 prompt->Prompt(req);
468 }
469 return NS_OK;
470 }
471
472 // Error callback for MediaManager::GetUserMediaDevices()
473 class MediaDeviceErrorCallback: public nsIDOMGetUserMediaErrorCallback
474 {
475 public:
476 NS_DECL_ISUPPORTS
477 NS_DECL_NSIDOMGETUSERMEDIAERRORCALLBACK
478
479 MediaDeviceErrorCallback(const nsAString &aCallID)
480 : mCallID(aCallID) {}
481
482 virtual ~MediaDeviceErrorCallback() {}
483
484 private:
485 const nsString mCallID;
486 };
487
488 NS_IMPL_ISUPPORTS(MediaDeviceErrorCallback, nsIDOMGetUserMediaErrorCallback)
489
490 // nsIDOMGetUserMediaErrorCallback method
491 NS_IMETHODIMP
492 MediaDeviceErrorCallback::OnError(const nsAString &aError)
493 {
494 return NotifyPermissionDeny(mCallID, aError);
495 }
496
497 } // namespace anonymous
498
499 // MediaPermissionManager
500 NS_IMPL_ISUPPORTS(MediaPermissionManager, nsIObserver)
501
502 MediaPermissionManager*
503 MediaPermissionManager::GetInstance()
504 {
505 if (!gMediaPermMgr) {
506 gMediaPermMgr = new MediaPermissionManager();
507 }
508
509 return gMediaPermMgr;
510 }
511
512 MediaPermissionManager::MediaPermissionManager()
513 {
514 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
515 if (obs) {
516 obs->AddObserver(this, "getUserMedia:request", false);
517 obs->AddObserver(this, "xpcom-shutdown", false);
518 }
519 }
520
521 MediaPermissionManager::~MediaPermissionManager()
522 {
523 this->Deinit();
524 }
525
526 nsresult
527 MediaPermissionManager::Deinit()
528 {
529 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
530 if (obs) {
531 obs->RemoveObserver(this, "getUserMedia:request");
532 obs->RemoveObserver(this, "xpcom-shutdown");
533 }
534 return NS_OK;
535 }
536
537 // nsIObserver method
538 NS_IMETHODIMP
539 MediaPermissionManager::Observe(nsISupports* aSubject, const char* aTopic,
540 const char16_t* aData)
541 {
542 nsresult rv;
543 if (!strcmp(aTopic, "getUserMedia:request")) {
544 nsRefPtr<dom::GetUserMediaRequest> req =
545 static_cast<dom::GetUserMediaRequest*>(aSubject);
546 rv = HandleRequest(req);
547
548 if (NS_FAILED(rv)) {
549 nsString callID;
550 req->GetCallID(callID);
551 NotifyPermissionDeny(callID, NS_LITERAL_STRING("unable to enumerate media device"));
552 }
553 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
554 rv = this->Deinit();
555 } else {
556 // not reachable
557 rv = NS_ERROR_FAILURE;
558 }
559 return rv;
560 }
561
562 // Handle GetUserMediaRequest, query available media device first.
563 nsresult
564 MediaPermissionManager::HandleRequest(nsRefPtr<dom::GetUserMediaRequest> &req)
565 {
566 nsString callID;
567 req->GetCallID(callID);
568
569 nsCOMPtr<nsPIDOMWindow> innerWindow = static_cast<nsPIDOMWindow*>
570 (nsGlobalWindow::GetInnerWindowWithId(req->InnerWindowID()));
571 if (!innerWindow) {
572 MOZ_ASSERT(false, "No inner window");
573 return NS_ERROR_FAILURE;
574 }
575
576 nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess =
577 new MediaDeviceSuccessCallback(req);
578 nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError =
579 new MediaDeviceErrorCallback(callID);
580
581 dom::MediaStreamConstraints constraints;
582 req->GetConstraints(constraints);
583
584 nsRefPtr<MediaManager> MediaMgr = MediaManager::GetInstance();
585 nsresult rv = MediaMgr->GetUserMediaDevices(innerWindow, constraints, onSuccess, onError);
586 NS_ENSURE_SUCCESS(rv, rv);
587
588 return NS_OK;
589 }
590
591 } // namespace mozilla

mercurial