Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / dom / camera / DOMCameraManager.cpp
blob6b0dafe04f2bdb05601c15a8baf8a437da0521f8
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/. */
5 #include "DOMCameraManager.h"
6 #include "nsDebug.h"
7 #include "jsapi.h"
8 #include "Navigator.h"
9 #include "nsPIDOMWindow.h"
10 #include "mozilla/Services.h"
11 #include "nsContentPermissionHelper.h"
12 #include "nsIContentPermissionPrompt.h"
13 #include "nsIObserverService.h"
14 #include "nsIPermissionManager.h"
15 #include "nsIScriptObjectPrincipal.h"
16 #include "DOMCameraControl.h"
17 #include "nsDOMClassInfo.h"
18 #include "CameraCommon.h"
19 #include "mozilla/dom/BindingUtils.h"
20 #include "mozilla/dom/CameraManagerBinding.h"
21 #include "mozilla/dom/PermissionMessageUtils.h"
23 using namespace mozilla;
24 using namespace mozilla::dom;
26 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCameraManager, mWindow)
28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
29 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
30 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
31 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
32 NS_INTERFACE_MAP_ENTRY(nsIObserver)
33 NS_INTERFACE_MAP_END
35 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager)
36 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager)
38 /**
39 * Global camera logging object
41 * Set the NSPR_LOG_MODULES environment variable to enable logging
42 * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
44 PRLogModuleInfo*
45 GetCameraLog()
47 static PRLogModuleInfo *sLog;
48 if (!sLog) {
49 sLog = PR_NewLogModule("Camera");
51 return sLog;
54 ::WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
56 nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
57 : mWindowId(aWindow->WindowID())
58 , mPermission(nsIPermissionManager::DENY_ACTION)
59 , mWindow(aWindow)
61 /* member initializers and constructor code */
62 DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\n", __func__, __LINE__, this, mWindowId);
63 MOZ_COUNT_CTOR(nsDOMCameraManager);
66 nsDOMCameraManager::~nsDOMCameraManager()
68 /* destructor code */
69 MOZ_COUNT_DTOR(nsDOMCameraManager);
70 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
73 /* static */
74 void
75 nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
77 aRv = ICameraControl::GetListOfCameras(aList);
80 /* static */
81 bool
82 nsDOMCameraManager::HasSupport(JSContext* aCx, JSObject* aGlobal)
84 return Navigator::HasCameraSupport(aCx, aGlobal);
87 /* static */
88 bool
89 nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow)
91 nsCOMPtr<nsIPermissionManager> permMgr =
92 services::GetPermissionManager();
93 NS_ENSURE_TRUE(permMgr, false);
95 uint32_t permission = nsIPermissionManager::DENY_ACTION;
96 permMgr->TestPermissionFromWindow(aWindow, "camera", &permission);
97 if (permission != nsIPermissionManager::ALLOW_ACTION &&
98 permission != nsIPermissionManager::PROMPT_ACTION) {
99 return false;
102 return true;
105 /* static */
106 already_AddRefed<nsDOMCameraManager>
107 nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow)
109 // Initialize the shared active window tracker
110 if (!sActiveWindows) {
111 sActiveWindows = new ::WindowTable();
114 nsRefPtr<nsDOMCameraManager> cameraManager =
115 new nsDOMCameraManager(aWindow);
117 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
118 if (!obs) {
119 DOM_CAMERA_LOGE("Camera manager failed to get observer service\n");
120 return nullptr;
123 nsresult rv = obs->AddObserver(cameraManager, "xpcom-shutdown", true);
124 if (NS_FAILED(rv)) {
125 DOM_CAMERA_LOGE("Camera manager failed to add 'xpcom-shutdown' observer (0x%x)\n", rv);
126 return nullptr;
129 return cameraManager.forget();
132 class CameraPermissionRequest : public nsIContentPermissionRequest
133 , public nsIRunnable
135 public:
136 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
137 NS_DECL_NSICONTENTPERMISSIONREQUEST
138 NS_DECL_NSIRUNNABLE
139 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest,
140 nsIContentPermissionRequest)
142 CameraPermissionRequest(nsIPrincipal* aPrincipal,
143 nsPIDOMWindow* aWindow,
144 nsRefPtr<nsDOMCameraManager> aManager,
145 uint32_t aCameraId,
146 const CameraConfiguration& aInitialConfig,
147 nsRefPtr<Promise> aPromise)
148 : mPrincipal(aPrincipal)
149 , mWindow(aWindow)
150 , mCameraManager(aManager)
151 , mCameraId(aCameraId)
152 , mInitialConfig(aInitialConfig)
153 , mPromise(aPromise)
156 protected:
157 virtual ~CameraPermissionRequest() { }
159 nsresult DispatchCallback(uint32_t aPermission);
160 void CallAllow();
161 void CallCancel();
162 nsCOMPtr<nsIPrincipal> mPrincipal;
163 nsCOMPtr<nsPIDOMWindow> mWindow;
164 nsRefPtr<nsDOMCameraManager> mCameraManager;
165 uint32_t mCameraId;
166 CameraConfiguration mInitialConfig;
167 nsRefPtr<Promise> mPromise;
170 NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mPromise)
172 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
173 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
174 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
175 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
176 NS_INTERFACE_MAP_END
178 NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest)
179 NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest)
181 NS_IMETHODIMP
182 CameraPermissionRequest::Run()
184 return nsContentPermissionUtils::AskPermission(this, mWindow);
187 NS_IMETHODIMP
188 CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
190 NS_ADDREF(*aRequestingPrincipal = mPrincipal);
191 return NS_OK;
194 NS_IMETHODIMP
195 CameraPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
197 NS_ADDREF(*aRequestingWindow = mWindow);
198 return NS_OK;
201 NS_IMETHODIMP
202 CameraPermissionRequest::GetElement(nsIDOMElement** aElement)
204 *aElement = nullptr;
205 return NS_OK;
208 NS_IMETHODIMP
209 CameraPermissionRequest::Cancel()
211 return DispatchCallback(nsIPermissionManager::DENY_ACTION);
214 NS_IMETHODIMP
215 CameraPermissionRequest::Allow(JS::HandleValue aChoices)
217 MOZ_ASSERT(aChoices.isUndefined());
218 return DispatchCallback(nsIPermissionManager::ALLOW_ACTION);
221 nsresult
222 CameraPermissionRequest::DispatchCallback(uint32_t aPermission)
224 nsCOMPtr<nsIRunnable> callbackRunnable;
225 if (aPermission == nsIPermissionManager::ALLOW_ACTION) {
226 callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow);
227 } else {
228 callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel);
230 return NS_DispatchToMainThread(callbackRunnable);
233 void
234 CameraPermissionRequest::CallAllow()
236 mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mPromise);
239 void
240 CameraPermissionRequest::CallCancel()
242 mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mPromise);
245 NS_IMETHODIMP
246 CameraPermissionRequest::GetTypes(nsIArray** aTypes)
248 nsTArray<nsString> emptyOptions;
249 return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("camera"),
250 NS_LITERAL_CSTRING("unused"),
251 emptyOptions,
252 aTypes);
255 #ifdef MOZ_WIDGET_GONK
256 /* static */ void
257 nsDOMCameraManager::PreinitCameraHardware()
259 nsDOMCameraControl::PreinitCameraHardware();
261 #endif
263 already_AddRefed<Promise>
264 nsDOMCameraManager::GetCamera(const nsAString& aCamera,
265 const CameraConfiguration& aInitialConfig,
266 ErrorResult& aRv)
268 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
270 uint32_t cameraId = 0; // back (or forward-facing) camera by default
271 if (aCamera.EqualsLiteral("front")) {
272 cameraId = 1;
275 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
276 if (!global) {
277 aRv.Throw(NS_ERROR_FAILURE);
278 return nullptr;
281 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
282 if (aRv.Failed()) {
283 return nullptr;
286 if (mPermission == nsIPermissionManager::ALLOW_ACTION) {
287 PermissionAllowed(cameraId, aInitialConfig, promise);
288 return promise.forget();
291 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
292 if (!sop) {
293 aRv.Throw(NS_ERROR_UNEXPECTED);
294 return nullptr;
297 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
298 // If we are a CERTIFIED app, we can short-circuit the permission check,
299 // which gets us a performance win.
300 uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
301 principal->GetAppStatus(&status);
302 if (status == nsIPrincipal::APP_STATUS_CERTIFIED && CheckPermission(mWindow)) {
303 PermissionAllowed(cameraId, aInitialConfig, promise);
304 return promise.forget();
307 nsCOMPtr<nsIRunnable> permissionRequest =
308 new CameraPermissionRequest(principal, mWindow, this, cameraId,
309 aInitialConfig, promise);
311 NS_DispatchToMainThread(permissionRequest);
312 return promise.forget();
315 void
316 nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
317 const CameraConfiguration& aInitialConfig,
318 Promise* aPromise)
320 mPermission = nsIPermissionManager::ALLOW_ACTION;
322 // Creating this object will trigger the aOnSuccess callback
323 // (or the aOnError one, if it fails).
324 nsRefPtr<nsDOMCameraControl> cameraControl =
325 new nsDOMCameraControl(aCameraId, aInitialConfig, aPromise, mWindow);
327 Register(cameraControl);
330 void
331 nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId,
332 const CameraConfiguration& aInitialConfig,
333 Promise* aPromise)
335 mPermission = nsIPermissionManager::DENY_ACTION;
336 aPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
339 void
340 nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
342 DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\n", aDOMCameraControl, mWindowId);
343 MOZ_ASSERT(NS_IsMainThread());
345 CameraControls* controls = sActiveWindows->Get(mWindowId);
346 if (!controls) {
347 controls = new CameraControls();
348 sActiveWindows->Put(mWindowId, controls);
351 // Remove any stale CameraControl objects to limit our memory usage
352 uint32_t i = controls->Length();
353 while (i > 0) {
354 --i;
355 nsRefPtr<nsDOMCameraControl> cameraControl =
356 do_QueryObject(controls->ElementAt(i));
357 if (!cameraControl) {
358 controls->RemoveElementAt(i);
362 // Put the camera control into the hash table
363 nsWeakPtr cameraControl =
364 do_GetWeakReference(static_cast<DOMMediaStream*>(aDOMCameraControl));
365 controls->AppendElement(cameraControl);
368 void
369 nsDOMCameraManager::Shutdown(uint64_t aWindowId)
371 DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\n", aWindowId);
372 MOZ_ASSERT(NS_IsMainThread());
374 CameraControls* controls = sActiveWindows->Get(aWindowId);
375 if (!controls) {
376 return;
379 uint32_t i = controls->Length();
380 while (i > 0) {
381 --i;
382 nsRefPtr<nsDOMCameraControl> cameraControl =
383 do_QueryObject(controls->ElementAt(i));
384 if (cameraControl) {
385 cameraControl->Shutdown();
388 controls->Clear();
390 sActiveWindows->Remove(aWindowId);
393 void
394 nsDOMCameraManager::XpComShutdown()
396 DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n");
397 MOZ_ASSERT(NS_IsMainThread());
399 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
400 obs->RemoveObserver(this, "xpcom-shutdown");
402 delete sActiveWindows;
403 sActiveWindows = nullptr;
406 nsresult
407 nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
409 if (strcmp(aTopic, "xpcom-shutdown") == 0) {
410 XpComShutdown();
412 return NS_OK;
415 void
416 nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
418 DOM_CAMERA_LOGI(">>> OnNavigation event\n");
419 Shutdown(aWindowId);
422 bool
423 nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId)
425 MOZ_ASSERT(NS_IsMainThread());
427 if (!sActiveWindows) {
428 return false;
431 return !!sActiveWindows->Get(aWindowId);
434 JSObject*
435 nsDOMCameraManager::WrapObject(JSContext* aCx)
437 return CameraManagerBinding::Wrap(aCx, this);