|
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 "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 "nsIObserverService.h" |
|
13 #include "nsIPermissionManager.h" |
|
14 #include "DOMCameraControl.h" |
|
15 #include "nsDOMClassInfo.h" |
|
16 #include "CameraCommon.h" |
|
17 #include "mozilla/dom/BindingUtils.h" |
|
18 #include "mozilla/dom/CameraManagerBinding.h" |
|
19 #include "mozilla/dom/PermissionMessageUtils.h" |
|
20 #include "mozilla/dom/TabChild.h" |
|
21 #include "PCOMContentPermissionRequestChild.h" |
|
22 |
|
23 using namespace mozilla; |
|
24 using namespace mozilla::dom; |
|
25 |
|
26 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow) |
|
27 |
|
28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager) |
|
29 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) |
|
30 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
|
31 NS_INTERFACE_MAP_ENTRY(nsIObserver) |
|
32 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
33 NS_INTERFACE_MAP_END |
|
34 |
|
35 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager) |
|
36 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager) |
|
37 |
|
38 /** |
|
39 * Global camera logging object |
|
40 * |
|
41 * Set the NSPR_LOG_MODULES environment variable to enable logging |
|
42 * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5 |
|
43 */ |
|
44 PRLogModuleInfo* |
|
45 GetCameraLog() |
|
46 { |
|
47 static PRLogModuleInfo *sLog; |
|
48 if (!sLog) { |
|
49 sLog = PR_NewLogModule("Camera"); |
|
50 } |
|
51 return sLog; |
|
52 } |
|
53 |
|
54 WindowTable* nsDOMCameraManager::sActiveWindows = nullptr; |
|
55 |
|
56 nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) |
|
57 : mWindowId(aWindow->WindowID()) |
|
58 , mPermission(nsIPermissionManager::DENY_ACTION) |
|
59 , mWindow(aWindow) |
|
60 { |
|
61 /* member initializers and constructor code */ |
|
62 DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId); |
|
63 MOZ_COUNT_CTOR(nsDOMCameraManager); |
|
64 SetIsDOMBinding(); |
|
65 } |
|
66 |
|
67 nsDOMCameraManager::~nsDOMCameraManager() |
|
68 { |
|
69 /* destructor code */ |
|
70 MOZ_COUNT_DTOR(nsDOMCameraManager); |
|
71 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); |
|
72 } |
|
73 |
|
74 /* static */ |
|
75 void |
|
76 nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv) |
|
77 { |
|
78 aRv = ICameraControl::GetListOfCameras(aList); |
|
79 } |
|
80 |
|
81 /* static */ |
|
82 bool |
|
83 nsDOMCameraManager::HasSupport(JSContext* aCx, JSObject* aGlobal) |
|
84 { |
|
85 return Navigator::HasCameraSupport(aCx, aGlobal); |
|
86 } |
|
87 |
|
88 /* static */ |
|
89 bool |
|
90 nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow) |
|
91 { |
|
92 nsCOMPtr<nsIPermissionManager> permMgr = |
|
93 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
|
94 NS_ENSURE_TRUE(permMgr, false); |
|
95 |
|
96 uint32_t permission = nsIPermissionManager::DENY_ACTION; |
|
97 permMgr->TestPermissionFromWindow(aWindow, "camera", &permission); |
|
98 if (permission != nsIPermissionManager::ALLOW_ACTION && |
|
99 permission != nsIPermissionManager::PROMPT_ACTION) { |
|
100 return false; |
|
101 } |
|
102 |
|
103 return true; |
|
104 } |
|
105 |
|
106 /* static */ |
|
107 already_AddRefed<nsDOMCameraManager> |
|
108 nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow) |
|
109 { |
|
110 // Initialize the shared active window tracker |
|
111 if (!sActiveWindows) { |
|
112 sActiveWindows = new WindowTable(); |
|
113 } |
|
114 |
|
115 nsRefPtr<nsDOMCameraManager> cameraManager = |
|
116 new nsDOMCameraManager(aWindow); |
|
117 |
|
118 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
119 obs->AddObserver(cameraManager, "xpcom-shutdown", true); |
|
120 |
|
121 return cameraManager.forget(); |
|
122 } |
|
123 |
|
124 class CameraPermissionRequest : public nsIContentPermissionRequest |
|
125 , public PCOMContentPermissionRequestChild |
|
126 , public nsIRunnable |
|
127 { |
|
128 public: |
|
129 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
130 NS_DECL_NSICONTENTPERMISSIONREQUEST |
|
131 NS_DECL_NSIRUNNABLE |
|
132 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest, |
|
133 nsIContentPermissionRequest) |
|
134 |
|
135 CameraPermissionRequest(nsIPrincipal* aPrincipal, |
|
136 nsPIDOMWindow* aWindow, |
|
137 nsRefPtr<nsDOMCameraManager> aManager, |
|
138 uint32_t aCameraId, |
|
139 const CameraConfiguration& aInitialConfig, |
|
140 nsRefPtr<GetCameraCallback> aOnSuccess, |
|
141 nsRefPtr<CameraErrorCallback> aOnError) |
|
142 : mPrincipal(aPrincipal) |
|
143 , mWindow(aWindow) |
|
144 , mCameraManager(aManager) |
|
145 , mCameraId(aCameraId) |
|
146 , mInitialConfig(aInitialConfig) |
|
147 , mOnSuccess(aOnSuccess) |
|
148 , mOnError(aOnError) |
|
149 { |
|
150 } |
|
151 |
|
152 virtual ~CameraPermissionRequest() |
|
153 { |
|
154 } |
|
155 |
|
156 bool Recv__delete__(const bool& aAllow, |
|
157 const InfallibleTArray<PermissionChoice>& choices); |
|
158 |
|
159 void IPDLRelease() |
|
160 { |
|
161 Release(); |
|
162 } |
|
163 |
|
164 protected: |
|
165 nsresult DispatchCallback(uint32_t aPermission); |
|
166 void CallAllow(); |
|
167 void CallCancel(); |
|
168 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
169 nsCOMPtr<nsPIDOMWindow> mWindow; |
|
170 nsRefPtr<nsDOMCameraManager> mCameraManager; |
|
171 uint32_t mCameraId; |
|
172 CameraConfiguration mInitialConfig; |
|
173 nsRefPtr<GetCameraCallback> mOnSuccess; |
|
174 nsRefPtr<CameraErrorCallback> mOnError; |
|
175 }; |
|
176 |
|
177 NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mOnSuccess, mOnError) |
|
178 |
|
179 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest) |
|
180 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) |
|
181 NS_INTERFACE_MAP_ENTRY(nsIRunnable) |
|
182 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) |
|
183 NS_INTERFACE_MAP_END |
|
184 |
|
185 NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest) |
|
186 NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest) |
|
187 |
|
188 NS_IMETHODIMP |
|
189 CameraPermissionRequest::Run() |
|
190 { |
|
191 if (XRE_GetProcessType() == GeckoProcessType_Content) { |
|
192 TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); |
|
193 if (!child) { |
|
194 return NS_ERROR_NOT_AVAILABLE; |
|
195 } |
|
196 |
|
197 // Retain a reference so the object isn't deleted without IPDL's knowledge. |
|
198 // Corresponding release occurs in DeallocPContentPermissionRequest. |
|
199 AddRef(); |
|
200 |
|
201 nsTArray<PermissionRequest> permArray; |
|
202 nsTArray<nsString> emptyOptions; |
|
203 permArray.AppendElement(PermissionRequest( |
|
204 NS_LITERAL_CSTRING("camera"), |
|
205 NS_LITERAL_CSTRING("unused"), |
|
206 emptyOptions)); |
|
207 child->SendPContentPermissionRequestConstructor(this, permArray, |
|
208 IPC::Principal(mPrincipal)); |
|
209 |
|
210 Sendprompt(); |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 nsCOMPtr<nsIContentPermissionPrompt> prompt = |
|
215 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); |
|
216 if (prompt) { |
|
217 prompt->Prompt(this); |
|
218 } |
|
219 |
|
220 return NS_OK; |
|
221 } |
|
222 |
|
223 bool |
|
224 CameraPermissionRequest::Recv__delete__(const bool& aAllow, |
|
225 const InfallibleTArray<PermissionChoice>& choices) |
|
226 { |
|
227 if (aAllow) { |
|
228 Allow(JS::UndefinedHandleValue); |
|
229 } else { |
|
230 Cancel(); |
|
231 } |
|
232 return true; |
|
233 } |
|
234 |
|
235 NS_IMETHODIMP |
|
236 CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal) |
|
237 { |
|
238 NS_ADDREF(*aRequestingPrincipal = mPrincipal); |
|
239 return NS_OK; |
|
240 } |
|
241 |
|
242 NS_IMETHODIMP |
|
243 CameraPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) |
|
244 { |
|
245 NS_ADDREF(*aRequestingWindow = mWindow); |
|
246 return NS_OK; |
|
247 } |
|
248 |
|
249 NS_IMETHODIMP |
|
250 CameraPermissionRequest::GetElement(nsIDOMElement** aElement) |
|
251 { |
|
252 *aElement = nullptr; |
|
253 return NS_OK; |
|
254 } |
|
255 |
|
256 NS_IMETHODIMP |
|
257 CameraPermissionRequest::Cancel() |
|
258 { |
|
259 return DispatchCallback(nsIPermissionManager::DENY_ACTION); |
|
260 } |
|
261 |
|
262 NS_IMETHODIMP |
|
263 CameraPermissionRequest::Allow(JS::HandleValue aChoices) |
|
264 { |
|
265 MOZ_ASSERT(aChoices.isUndefined()); |
|
266 return DispatchCallback(nsIPermissionManager::ALLOW_ACTION); |
|
267 } |
|
268 |
|
269 nsresult |
|
270 CameraPermissionRequest::DispatchCallback(uint32_t aPermission) |
|
271 { |
|
272 nsCOMPtr<nsIRunnable> callbackRunnable; |
|
273 if (aPermission == nsIPermissionManager::ALLOW_ACTION) { |
|
274 callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); |
|
275 } else { |
|
276 callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); |
|
277 } |
|
278 return NS_DispatchToMainThread(callbackRunnable); |
|
279 } |
|
280 |
|
281 void |
|
282 CameraPermissionRequest::CallAllow() |
|
283 { |
|
284 mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError); |
|
285 } |
|
286 |
|
287 void |
|
288 CameraPermissionRequest::CallCancel() |
|
289 { |
|
290 mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError); |
|
291 } |
|
292 |
|
293 NS_IMETHODIMP |
|
294 CameraPermissionRequest::GetTypes(nsIArray** aTypes) |
|
295 { |
|
296 nsTArray<nsString> emptyOptions; |
|
297 return CreatePermissionArray(NS_LITERAL_CSTRING("camera"), |
|
298 NS_LITERAL_CSTRING("unused"), |
|
299 emptyOptions, |
|
300 aTypes); |
|
301 } |
|
302 |
|
303 void |
|
304 nsDOMCameraManager::GetCamera(const nsAString& aCamera, |
|
305 const CameraConfiguration& aInitialConfig, |
|
306 GetCameraCallback& aOnSuccess, |
|
307 const OptionalNonNullCameraErrorCallback& aOnError, |
|
308 ErrorResult& aRv) |
|
309 { |
|
310 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); |
|
311 |
|
312 uint32_t cameraId = 0; // back (or forward-facing) camera by default |
|
313 if (aCamera.EqualsLiteral("front")) { |
|
314 cameraId = 1; |
|
315 } |
|
316 |
|
317 nsRefPtr<CameraErrorCallback> errorCallback = nullptr; |
|
318 if (aOnError.WasPassed()) { |
|
319 errorCallback = &aOnError.Value(); |
|
320 } |
|
321 |
|
322 if (mPermission == nsIPermissionManager::ALLOW_ACTION) { |
|
323 PermissionAllowed(cameraId, aInitialConfig, &aOnSuccess, errorCallback); |
|
324 return; |
|
325 } |
|
326 |
|
327 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow); |
|
328 if (!sop) { |
|
329 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
330 return; |
|
331 } |
|
332 |
|
333 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); |
|
334 |
|
335 nsCOMPtr<nsIRunnable> permissionRequest = |
|
336 new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig, |
|
337 &aOnSuccess, errorCallback); |
|
338 |
|
339 NS_DispatchToMainThread(permissionRequest); |
|
340 } |
|
341 |
|
342 void |
|
343 nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId, |
|
344 const CameraConfiguration& aInitialConfig, |
|
345 GetCameraCallback* aOnSuccess, |
|
346 CameraErrorCallback* aOnError) |
|
347 { |
|
348 mPermission = nsIPermissionManager::ALLOW_ACTION; |
|
349 |
|
350 // Creating this object will trigger the aOnSuccess callback |
|
351 // (or the aOnError one, if it fails). |
|
352 nsRefPtr<nsDOMCameraControl> cameraControl = |
|
353 new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, mWindow); |
|
354 |
|
355 Register(cameraControl); |
|
356 } |
|
357 |
|
358 void |
|
359 nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId, |
|
360 const CameraConfiguration& aInitialConfig, |
|
361 GetCameraCallback* aOnSuccess, |
|
362 CameraErrorCallback* aOnError) |
|
363 { |
|
364 mPermission = nsIPermissionManager::DENY_ACTION; |
|
365 |
|
366 if (aOnError) { |
|
367 ErrorResult ignored; |
|
368 aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored); |
|
369 } |
|
370 } |
|
371 |
|
372 void |
|
373 nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) |
|
374 { |
|
375 DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId); |
|
376 MOZ_ASSERT(NS_IsMainThread()); |
|
377 |
|
378 // Put the camera control into the hash table |
|
379 CameraControls* controls = sActiveWindows->Get(mWindowId); |
|
380 if (!controls) { |
|
381 controls = new CameraControls; |
|
382 sActiveWindows->Put(mWindowId, controls); |
|
383 } |
|
384 controls->AppendElement(aDOMCameraControl); |
|
385 } |
|
386 |
|
387 void |
|
388 nsDOMCameraManager::Shutdown(uint64_t aWindowId) |
|
389 { |
|
390 DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId); |
|
391 MOZ_ASSERT(NS_IsMainThread()); |
|
392 |
|
393 CameraControls* controls = sActiveWindows->Get(aWindowId); |
|
394 if (!controls) { |
|
395 return; |
|
396 } |
|
397 |
|
398 uint32_t length = controls->Length(); |
|
399 for (uint32_t i = 0; i < length; i++) { |
|
400 nsRefPtr<nsDOMCameraControl> cameraControl = controls->ElementAt(i); |
|
401 cameraControl->Shutdown(); |
|
402 } |
|
403 controls->Clear(); |
|
404 |
|
405 sActiveWindows->Remove(aWindowId); |
|
406 } |
|
407 |
|
408 void |
|
409 nsDOMCameraManager::XpComShutdown() |
|
410 { |
|
411 DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n"); |
|
412 MOZ_ASSERT(NS_IsMainThread()); |
|
413 |
|
414 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
415 obs->RemoveObserver(this, "xpcom-shutdown"); |
|
416 |
|
417 delete sActiveWindows; |
|
418 sActiveWindows = nullptr; |
|
419 } |
|
420 |
|
421 nsresult |
|
422 nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) |
|
423 { |
|
424 if (strcmp(aTopic, "xpcom-shutdown") == 0) { |
|
425 XpComShutdown(); |
|
426 } |
|
427 return NS_OK; |
|
428 } |
|
429 |
|
430 void |
|
431 nsDOMCameraManager::OnNavigation(uint64_t aWindowId) |
|
432 { |
|
433 DOM_CAMERA_LOGI(">>> OnNavigation event\n"); |
|
434 Shutdown(aWindowId); |
|
435 } |
|
436 |
|
437 bool |
|
438 nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId) |
|
439 { |
|
440 MOZ_ASSERT(NS_IsMainThread()); |
|
441 |
|
442 if (!sActiveWindows) { |
|
443 return false; |
|
444 } |
|
445 |
|
446 return !!sActiveWindows->Get(aWindowId); |
|
447 } |
|
448 |
|
449 JSObject* |
|
450 nsDOMCameraManager::WrapObject(JSContext* aCx) |
|
451 { |
|
452 return CameraManagerBinding::Wrap(aCx, this); |
|
453 } |