Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "DOMCameraControl.h"
6 #include "base/basictypes.h"
7 #include "nsCOMPtr.h"
8 #include "nsDOMClassInfo.h"
9 #include "nsHashPropertyBag.h"
10 #include "nsThread.h"
11 #include "DeviceStorage.h"
12 #include "DeviceStorageFileDescriptor.h"
13 #include "mozilla/dom/TabChild.h"
14 #include "mozilla/ipc/FileDescriptorUtils.h"
15 #include "mozilla/MediaManager.h"
16 #include "mozilla/Services.h"
17 #include "mozilla/unused.h"
18 #include "nsIAppsService.h"
19 #include "nsIObserverService.h"
20 #include "nsIDOMDeviceStorage.h"
21 #include "nsIDOMEventListener.h"
22 #include "nsIScriptSecurityManager.h"
23 #include "Navigator.h"
24 #include "nsXULAppAPI.h"
25 #include "DOMCameraManager.h"
26 #include "DOMCameraCapabilities.h"
27 #include "CameraCommon.h"
28 #include "nsGlobalWindow.h"
29 #include "CameraPreviewMediaStream.h"
30 #include "mozilla/dom/CameraControlBinding.h"
31 #include "mozilla/dom/CameraManagerBinding.h"
32 #include "mozilla/dom/CameraCapabilitiesBinding.h"
33 #include "DOMCameraDetectedFace.h"
34 #include "mozilla/dom/BindingUtils.h"
36 using namespace mozilla;
37 using namespace mozilla::dom;
38 using namespace mozilla::ipc;
40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
41 NS_INTERFACE_MAP_ENTRY(nsISupports)
42 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
43 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
45 NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream)
46 NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream)
48 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream,
49 mCapabilities,
50 mWindow,
51 mGetCameraOnSuccessCb,
52 mGetCameraOnErrorCb,
53 mAutoFocusOnSuccessCb,
54 mAutoFocusOnErrorCb,
55 mTakePictureOnSuccessCb,
56 mTakePictureOnErrorCb,
57 mStartRecordingOnSuccessCb,
58 mStartRecordingOnErrorCb,
59 mReleaseOnSuccessCb,
60 mReleaseOnErrorCb,
61 mSetConfigurationOnSuccessCb,
62 mSetConfigurationOnErrorCb,
63 mOnShutterCb,
64 mOnClosedCb,
65 mOnRecorderStateChangeCb,
66 mOnPreviewStateChangeCb,
67 mOnAutoFocusMovingCb,
68 mOnFacesDetectedCb)
70 /* static */
71 bool
72 nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* aGlobal)
73 {
74 return Navigator::HasCameraSupport(aCx, aGlobal);
75 }
77 class mozilla::StartRecordingHelper : public nsIDOMEventListener
78 {
79 public:
80 NS_DECL_ISUPPORTS
81 NS_DECL_NSIDOMEVENTLISTENER
83 StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
84 : mDOMCameraControl(aDOMCameraControl)
85 {
86 MOZ_COUNT_CTOR(StartRecordingHelper);
87 }
89 protected:
90 virtual ~StartRecordingHelper()
91 {
92 MOZ_COUNT_DTOR(StartRecordingHelper);
93 }
95 protected:
96 nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
97 };
99 NS_IMETHODIMP
100 StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
101 {
102 nsString eventType;
103 aEvent->GetType(eventType);
105 mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
106 return NS_OK;
107 }
109 NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
111 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
112 : CameraConfiguration()
113 , mMaxFocusAreas(0)
114 , mMaxMeteringAreas(0)
115 {
116 MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
117 }
119 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration)
120 : CameraConfiguration(aConfiguration)
121 , mMaxFocusAreas(0)
122 , mMaxMeteringAreas(0)
123 {
124 MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
125 }
127 nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration()
128 {
129 MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration);
130 }
132 nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
133 const CameraConfiguration& aInitialConfig,
134 GetCameraCallback* aOnSuccess,
135 CameraErrorCallback* aOnError,
136 nsPIDOMWindow* aWindow)
137 : DOMMediaStream()
138 , mCameraControl(nullptr)
139 , mAudioChannelAgent(nullptr)
140 , mGetCameraOnSuccessCb(aOnSuccess)
141 , mGetCameraOnErrorCb(aOnError)
142 , mAutoFocusOnSuccessCb(nullptr)
143 , mAutoFocusOnErrorCb(nullptr)
144 , mTakePictureOnSuccessCb(nullptr)
145 , mTakePictureOnErrorCb(nullptr)
146 , mStartRecordingOnSuccessCb(nullptr)
147 , mStartRecordingOnErrorCb(nullptr)
148 , mReleaseOnSuccessCb(nullptr)
149 , mReleaseOnErrorCb(nullptr)
150 , mSetConfigurationOnSuccessCb(nullptr)
151 , mSetConfigurationOnErrorCb(nullptr)
152 , mOnShutterCb(nullptr)
153 , mOnClosedCb(nullptr)
154 , mOnRecorderStateChangeCb(nullptr)
155 , mOnPreviewStateChangeCb(nullptr)
156 , mOnAutoFocusMovingCb(nullptr)
157 , mOnFacesDetectedCb(nullptr)
158 , mWindow(aWindow)
159 {
160 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
161 mInput = new CameraPreviewMediaStream(this);
163 SetIsDOMBinding();
165 nsRefPtr<DOMCameraConfiguration> initialConfig =
166 new DOMCameraConfiguration(aInitialConfig);
168 // Create and initialize the underlying camera.
169 ICameraControl::Configuration config;
171 switch (aInitialConfig.mMode) {
172 case CameraMode::Picture:
173 config.mMode = ICameraControl::kPictureMode;
174 break;
176 case CameraMode::Video:
177 config.mMode = ICameraControl::kVideoMode;
178 break;
180 default:
181 MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
182 }
184 config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth;
185 config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
186 config.mRecorderProfile = aInitialConfig.mRecorderProfile;
188 mCameraControl = ICameraControl::Create(aCameraId);
189 mCurrentConfiguration = initialConfig.forget();
191 // Attach our DOM-facing media stream to our viewfinder stream.
192 mStream = mInput;
193 MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!");
194 if (mWindow->GetExtantDoc()) {
195 CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal());
196 }
198 // Register a listener for camera events.
199 mListener = new DOMCameraControlListener(this, mInput);
200 mCameraControl->AddListener(mListener);
202 // Start the camera...
203 nsresult rv = mCameraControl->Start(&config);
204 if (NS_FAILED(rv)) {
205 mListener->OnError(DOMCameraControlListener::kInStartCamera,
206 DOMCameraControlListener::kErrorApiFailed);
207 }
208 }
210 nsDOMCameraControl::~nsDOMCameraControl()
211 {
212 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
213 }
215 JSObject*
216 nsDOMCameraControl::WrapObject(JSContext* aCx)
217 {
218 return CameraControlBinding::Wrap(aCx, this);
219 }
221 bool
222 nsDOMCameraControl::IsWindowStillActive()
223 {
224 return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
225 }
227 // JS-to-native helpers
228 // Setter for weighted regions: { top, bottom, left, right, weight }
229 nsresult
230 nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
231 {
232 if (aLimit == 0) {
233 DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
234 return NS_OK;
235 }
237 if (!aValue.isObject()) {
238 return NS_ERROR_INVALID_ARG;
239 }
241 uint32_t length = 0;
243 JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
244 if (!JS_GetArrayLength(aCx, regions, &length)) {
245 return NS_ERROR_FAILURE;
246 }
248 DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
249 if (length > aLimit) {
250 length = aLimit;
251 }
253 nsTArray<ICameraControl::Region> regionArray;
254 regionArray.SetCapacity(length);
256 for (uint32_t i = 0; i < length; ++i) {
257 JS::Rooted<JS::Value> v(aCx);
259 if (!JS_GetElement(aCx, regions, i, &v)) {
260 return NS_ERROR_FAILURE;
261 }
263 CameraRegion region;
264 if (!region.Init(aCx, v)) {
265 return NS_ERROR_FAILURE;
266 }
268 ICameraControl::Region* r = regionArray.AppendElement();
269 r->top = region.mTop;
270 r->left = region.mLeft;
271 r->bottom = region.mBottom;
272 r->right = region.mRight;
273 r->weight = region.mWeight;
275 DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
276 i,
277 r->top,
278 r->left,
279 r->bottom,
280 r->right,
281 r->weight
282 );
283 }
284 return mCameraControl->Set(aKey, regionArray);
285 }
287 // Getter for weighted regions: { top, bottom, left, right, weight }
288 nsresult
289 nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
290 {
291 nsTArray<ICameraControl::Region> regionArray;
293 nsresult rv = mCameraControl->Get(aKey, regionArray);
294 NS_ENSURE_SUCCESS(rv, rv);
296 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
297 if (!array) {
298 return NS_ERROR_OUT_OF_MEMORY;
299 }
301 uint32_t length = regionArray.Length();
302 DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
304 for (uint32_t i = 0; i < length; ++i) {
305 ICameraControl::Region* r = ®ionArray[i];
306 JS::Rooted<JS::Value> v(aCx);
308 JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
309 if (!o) {
310 return NS_ERROR_OUT_OF_MEMORY;
311 }
313 DOM_CAMERA_LOGI("top=%d\n", r->top);
314 v = INT_TO_JSVAL(r->top);
315 if (!JS_SetProperty(aCx, o, "top", v)) {
316 return NS_ERROR_FAILURE;
317 }
318 DOM_CAMERA_LOGI("left=%d\n", r->left);
319 v = INT_TO_JSVAL(r->left);
320 if (!JS_SetProperty(aCx, o, "left", v)) {
321 return NS_ERROR_FAILURE;
322 }
323 DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
324 v = INT_TO_JSVAL(r->bottom);
325 if (!JS_SetProperty(aCx, o, "bottom", v)) {
326 return NS_ERROR_FAILURE;
327 }
328 DOM_CAMERA_LOGI("right=%d\n", r->right);
329 v = INT_TO_JSVAL(r->right);
330 if (!JS_SetProperty(aCx, o, "right", v)) {
331 return NS_ERROR_FAILURE;
332 }
333 DOM_CAMERA_LOGI("weight=%d\n", r->weight);
334 v = INT_TO_JSVAL(r->weight);
335 if (!JS_SetProperty(aCx, o, "weight", v)) {
336 return NS_ERROR_FAILURE;
337 }
339 if (!JS_SetElement(aCx, array, i, o)) {
340 return NS_ERROR_FAILURE;
341 }
342 }
344 *aValue = JS::ObjectValue(*array);
345 return NS_OK;
346 }
348 void
349 nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv)
350 {
351 MOZ_ASSERT(mCameraControl);
352 aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
353 }
354 void
355 nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv)
356 {
357 MOZ_ASSERT(mCameraControl);
358 aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect);
359 }
361 void
362 nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv)
363 {
364 MOZ_ASSERT(mCameraControl);
365 aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
366 }
367 void
368 nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv)
369 {
370 MOZ_ASSERT(mCameraControl);
371 aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
372 }
374 void
375 nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv)
376 {
377 MOZ_ASSERT(mCameraControl);
378 aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode);
379 }
380 void
381 nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv)
382 {
383 MOZ_ASSERT(mCameraControl);
384 aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode);
385 }
387 void
388 nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv)
389 {
390 MOZ_ASSERT(mCameraControl);
391 aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode);
392 }
393 void
394 nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv)
395 {
396 MOZ_ASSERT(mCameraControl);
397 aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode);
398 }
400 void
401 nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv)
402 {
403 MOZ_ASSERT(mCameraControl);
404 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode);
405 }
406 void
407 nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
408 {
409 MOZ_ASSERT(mCameraControl);
410 aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
411 }
413 void
414 nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv)
415 {
416 MOZ_ASSERT(mCameraControl);
417 aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode);
418 }
419 void
420 nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv)
421 {
422 MOZ_ASSERT(mCameraControl);
423 aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode);
424 }
426 double
427 nsDOMCameraControl::GetZoom(ErrorResult& aRv)
428 {
429 MOZ_ASSERT(mCameraControl);
431 double zoom;
432 aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom);
433 return zoom;
434 }
436 void
437 nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv)
438 {
439 MOZ_ASSERT(mCameraControl);
440 aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
441 }
443 /* attribute jsval meteringAreas; */
444 void
445 nsDOMCameraControl::GetMeteringAreas(JSContext* cx,
446 JS::MutableHandle<JS::Value> aMeteringAreas,
447 ErrorResult& aRv)
448 {
449 aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas.address());
450 }
452 void
453 nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle<JS::Value> aMeteringAreas, ErrorResult& aRv)
454 {
455 aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
456 mCurrentConfiguration->mMaxMeteringAreas);
457 }
459 void
460 nsDOMCameraControl::GetFocusAreas(JSContext* cx,
461 JS::MutableHandle<JS::Value> aFocusAreas,
462 ErrorResult& aRv)
463 {
464 aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas.address());
465 }
466 void
467 nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle<JS::Value> aFocusAreas, ErrorResult& aRv)
468 {
469 aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
470 mCurrentConfiguration->mMaxFocusAreas);
471 }
473 static nsresult
474 GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize)
475 {
476 JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
477 if (!o) {
478 return NS_ERROR_OUT_OF_MEMORY;
479 }
481 JS::Rooted<JS::Value> v(aCx);
483 v = INT_TO_JSVAL(aSize.width);
484 if (!JS_SetProperty(aCx, o, "width", v)) {
485 return NS_ERROR_FAILURE;
486 }
487 v = INT_TO_JSVAL(aSize.height);
488 if (!JS_SetProperty(aCx, o, "height", v)) {
489 return NS_ERROR_FAILURE;
490 }
492 *aValue = JS::ObjectValue(*o);
493 return NS_OK;
494 }
496 /* attribute any pictureSize */
497 void
498 nsDOMCameraControl::GetPictureSize(JSContext* cx,
499 JS::MutableHandle<JS::Value> aSize,
500 ErrorResult& aRv)
501 {
502 ICameraControl::Size size;
503 aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size);
504 if (aRv.Failed()) {
505 return;
506 }
508 aRv = GetSize(cx, aSize.address(), size);
509 }
510 void
511 nsDOMCameraControl::SetPictureSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
512 {
513 CameraSize size;
514 if (!size.Init(aCx, aSize)) {
515 aRv = NS_ERROR_FAILURE;
516 return;
517 }
519 ICameraControl::Size s = { size.mWidth, size.mHeight };
520 aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
521 }
523 /* attribute any thumbnailSize */
524 void
525 nsDOMCameraControl::GetThumbnailSize(JSContext* aCx,
526 JS::MutableHandle<JS::Value> aSize,
527 ErrorResult& aRv)
528 {
529 ICameraControl::Size size;
530 aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
531 if (aRv.Failed()) {
532 return;
533 }
535 aRv = GetSize(aCx, aSize.address(), size);
536 }
537 void
538 nsDOMCameraControl::SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
539 {
540 CameraSize size;
541 if (!size.Init(aCx, aSize)) {
542 aRv = NS_ERROR_FAILURE;
543 return;
544 }
546 ICameraControl::Size s = { size.mWidth, size.mHeight };
547 aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
548 }
550 double
551 nsDOMCameraControl::GetFocalLength(ErrorResult& aRv)
552 {
553 MOZ_ASSERT(mCameraControl);
555 double focalLength;
556 aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength);
557 return focalLength;
558 }
560 double
561 nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv)
562 {
563 MOZ_ASSERT(mCameraControl);
565 double distance;
566 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance);
567 return distance;
568 }
570 double
571 nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv)
572 {
573 MOZ_ASSERT(mCameraControl);
575 double distance;
576 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance);
577 return distance;
578 }
580 double
581 nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv)
582 {
583 MOZ_ASSERT(mCameraControl);
585 double distance;
586 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance);
587 return distance;
588 }
590 void
591 nsDOMCameraControl::SetExposureCompensation(const Optional<double>& aCompensation, ErrorResult& aRv)
592 {
593 MOZ_ASSERT(mCameraControl);
595 if (!aCompensation.WasPassed()) {
596 // use NaN to switch the camera back into auto mode
597 aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
598 return;
599 }
601 aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value());
602 }
604 double
605 nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
606 {
607 MOZ_ASSERT(mCameraControl);
609 double compensation;
610 aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
611 return compensation;
612 }
614 int32_t
615 nsDOMCameraControl::SensorAngle()
616 {
617 MOZ_ASSERT(mCameraControl);
619 int32_t angle = 0;
620 mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle);
621 return angle;
622 }
624 // Callback attributes
626 CameraShutterCallback*
627 nsDOMCameraControl::GetOnShutter()
628 {
629 return mOnShutterCb;
630 }
631 void
632 nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb)
633 {
634 mOnShutterCb = aCb;
635 }
637 CameraClosedCallback*
638 nsDOMCameraControl::GetOnClosed()
639 {
640 return mOnClosedCb;
641 }
642 void
643 nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb)
644 {
645 mOnClosedCb = aCb;
646 }
648 CameraRecorderStateChange*
649 nsDOMCameraControl::GetOnRecorderStateChange()
650 {
651 return mOnRecorderStateChangeCb;
652 }
653 void
654 nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb)
655 {
656 mOnRecorderStateChangeCb = aCb;
657 }
659 CameraPreviewStateChange*
660 nsDOMCameraControl::GetOnPreviewStateChange()
661 {
662 return mOnPreviewStateChangeCb;
663 }
664 void
665 nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb)
666 {
667 mOnPreviewStateChangeCb = aCb;
668 }
670 CameraAutoFocusMovingCallback*
671 nsDOMCameraControl::GetOnAutoFocusMoving()
672 {
673 return mOnAutoFocusMovingCb;
674 }
675 void
676 nsDOMCameraControl::SetOnAutoFocusMoving(CameraAutoFocusMovingCallback* aCb)
677 {
678 mOnAutoFocusMovingCb = aCb;
679 }
681 CameraFaceDetectionCallback*
682 nsDOMCameraControl::GetOnFacesDetected()
683 {
684 return mOnFacesDetectedCb;
685 }
686 void
687 nsDOMCameraControl::SetOnFacesDetected(CameraFaceDetectionCallback* aCb)
688 {
689 mOnFacesDetectedCb = aCb;
690 }
692 already_AddRefed<dom::CameraCapabilities>
693 nsDOMCameraControl::Capabilities()
694 {
695 nsRefPtr<CameraCapabilities> caps = mCapabilities;
697 if (!caps) {
698 caps = new CameraCapabilities(mWindow);
699 nsresult rv = caps->Populate(mCameraControl);
700 if (NS_FAILED(rv)) {
701 DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv);
702 return nullptr;
703 }
704 mCapabilities = caps;
705 }
707 return caps.forget();
708 }
710 // Methods.
711 void
712 nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
713 nsDOMDeviceStorage& aStorageArea,
714 const nsAString& aFilename,
715 CameraStartRecordingCallback& aOnSuccess,
716 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
717 ErrorResult& aRv)
718 {
719 MOZ_ASSERT(mCameraControl);
721 NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
723 #ifdef MOZ_B2G
724 if (!mAudioChannelAgent) {
725 mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
726 if (mAudioChannelAgent) {
727 // Camera app will stop recording when it falls to the background, so no callback is necessary.
728 mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
729 // Video recording doesn't output any sound, so it's not necessary to check canPlay.
730 int32_t canPlay;
731 mAudioChannelAgent->StartPlaying(&canPlay);
732 }
733 }
734 #endif
736 nsCOMPtr<nsIDOMDOMRequest> request;
737 mDSFileDescriptor = new DeviceStorageFileDescriptor();
738 aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(),
739 getter_AddRefs(request));
740 if (aRv.Failed()) {
741 return;
742 }
744 mOptions = aOptions;
745 mStartRecordingOnSuccessCb = &aOnSuccess;
746 mStartRecordingOnErrorCb = nullptr;
747 if (aOnError.WasPassed()) {
748 mStartRecordingOnErrorCb = &aOnError.Value();
749 }
751 nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
752 request->AddEventListener(NS_LITERAL_STRING("success"), listener, false);
753 request->AddEventListener(NS_LITERAL_STRING("error"), listener, false);
754 }
756 void
757 nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
758 {
759 if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
760 ICameraControl::StartRecordingOptions o;
762 o.rotation = mOptions.mRotation;
763 o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
764 o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
765 o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
766 nsresult rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
767 if (NS_SUCCEEDED(rv)) {
768 return;
769 }
770 }
771 OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE"));
773 if (mDSFileDescriptor->mFileDescriptor.IsValid()) {
774 // An error occured. We need to manually close the file associated with the
775 // FileDescriptor, and we shouldn't do this on the main thread, so we
776 // use a little helper.
777 nsRefPtr<CloseFileRunnable> closer =
778 new CloseFileRunnable(mDSFileDescriptor->mFileDescriptor);
779 closer->Dispatch();
780 }
781 }
783 void
784 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
785 {
786 MOZ_ASSERT(mCameraControl);
788 #ifdef MOZ_B2G
789 if (mAudioChannelAgent) {
790 mAudioChannelAgent->StopPlaying();
791 mAudioChannelAgent = nullptr;
792 }
793 #endif
795 aRv = mCameraControl->StopRecording();
796 }
798 void
799 nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
800 {
801 MOZ_ASSERT(mCameraControl);
802 aRv = mCameraControl->StartPreview();
803 }
805 void
806 nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
807 const Optional<OwningNonNull<CameraSetConfigurationCallback> >& aOnSuccess,
808 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
809 ErrorResult& aRv)
810 {
811 MOZ_ASSERT(mCameraControl);
813 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
814 if (cb) {
815 // We're busy taking a picture, can't change modes right now.
816 if (aOnError.WasPassed()) {
817 ErrorResult ignored;
818 aOnError.Value().Call(NS_LITERAL_STRING("Busy"), ignored);
819 }
820 aRv = NS_ERROR_FAILURE;
821 return;
822 }
824 ICameraControl::Configuration config;
825 config.mRecorderProfile = aConfiguration.mRecorderProfile;
826 config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth;
827 config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight;
828 config.mMode = ICameraControl::kPictureMode;
829 if (aConfiguration.mMode == CameraMode::Video) {
830 config.mMode = ICameraControl::kVideoMode;
831 }
833 mSetConfigurationOnSuccessCb = nullptr;
834 if (aOnSuccess.WasPassed()) {
835 mSetConfigurationOnSuccessCb = &aOnSuccess.Value();
836 }
837 mSetConfigurationOnErrorCb = nullptr;
838 if (aOnError.WasPassed()) {
839 mSetConfigurationOnErrorCb = &aOnError.Value();
840 }
842 aRv = mCameraControl->SetConfiguration(config);
843 }
845 class ImmediateErrorCallback : public nsRunnable
846 {
847 public:
848 ImmediateErrorCallback(CameraErrorCallback* aCallback, const nsAString& aMessage)
849 : mCallback(aCallback)
850 , mMessage(aMessage)
851 { }
853 NS_IMETHODIMP
854 Run()
855 {
856 MOZ_ASSERT(NS_IsMainThread());
857 ErrorResult ignored;
858 mCallback->Call(mMessage, ignored);
859 return NS_OK;
860 }
862 protected:
863 nsRefPtr<CameraErrorCallback> mCallback;
864 nsString mMessage;
865 };
868 void
869 nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess,
870 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
871 ErrorResult& aRv)
872 {
873 MOZ_ASSERT(mCameraControl);
875 nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb;
876 if (cb) {
877 if (aOnError.WasPassed()) {
878 // There is already a call to AutoFocus() in progress, abort this new one
879 // and invoke the error callback (if one was passed in).
880 NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
881 NS_LITERAL_STRING("AutoFocusAlreadyInProgress")));
882 }
883 aRv = NS_ERROR_FAILURE;
884 return;
885 }
887 mAutoFocusOnSuccessCb = &aOnSuccess;
888 mAutoFocusOnErrorCb = nullptr;
889 if (aOnError.WasPassed()) {
890 mAutoFocusOnErrorCb = &aOnError.Value();
891 }
893 aRv = mCameraControl->AutoFocus();
894 }
896 void
897 nsDOMCameraControl::StartFaceDetection(ErrorResult& aRv)
898 {
899 MOZ_ASSERT(mCameraControl);
900 aRv = mCameraControl->StartFaceDetection();
901 }
903 void
904 nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv)
905 {
906 MOZ_ASSERT(mCameraControl);
907 aRv = mCameraControl->StopFaceDetection();
908 }
910 void
911 nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
912 CameraTakePictureCallback& aOnSuccess,
913 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
914 ErrorResult& aRv)
915 {
916 MOZ_ASSERT(mCameraControl);
918 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
919 if (cb) {
920 if (aOnError.WasPassed()) {
921 // There is already a call to TakePicture() in progress, abort this new
922 // one and invoke the error callback (if one was passed in).
923 NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
924 NS_LITERAL_STRING("TakePictureAlreadyInProgress")));
925 }
926 aRv = NS_ERROR_FAILURE;
927 return;
928 }
930 {
931 ICameraControlParameterSetAutoEnter batch(mCameraControl);
933 // XXXmikeh - remove this: see bug 931155
934 ICameraControl::Size s;
935 s.width = aOptions.mPictureSize.mWidth;
936 s.height = aOptions.mPictureSize.mHeight;
938 ICameraControl::Position p;
939 p.latitude = aOptions.mPosition.mLatitude;
940 p.longitude = aOptions.mPosition.mLongitude;
941 p.altitude = aOptions.mPosition.mAltitude;
942 p.timestamp = aOptions.mPosition.mTimestamp;
944 if (s.width && s.height) {
945 mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
946 }
947 mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation);
948 mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat);
949 mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime);
950 mCameraControl->SetLocation(p);
951 }
953 mTakePictureOnSuccessCb = &aOnSuccess;
954 mTakePictureOnErrorCb = nullptr;
955 if (aOnError.WasPassed()) {
956 mTakePictureOnErrorCb = &aOnError.Value();
957 }
959 aRv = mCameraControl->TakePicture();
960 }
962 void
963 nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCallback> >& aOnSuccess,
964 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
965 ErrorResult& aRv)
966 {
967 MOZ_ASSERT(mCameraControl);
969 mReleaseOnSuccessCb = nullptr;
970 if (aOnSuccess.WasPassed()) {
971 mReleaseOnSuccessCb = &aOnSuccess.Value();
972 }
973 mReleaseOnErrorCb = nullptr;
974 if (aOnError.WasPassed()) {
975 mReleaseOnErrorCb = &aOnError.Value();
976 }
978 aRv = mCameraControl->Stop();
979 }
981 void
982 nsDOMCameraControl::ResumeContinuousFocus(ErrorResult& aRv)
983 {
984 MOZ_ASSERT(mCameraControl);
985 aRv = mCameraControl->ResumeContinuousFocus();
986 }
988 void
989 nsDOMCameraControl::Shutdown()
990 {
991 DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
992 MOZ_ASSERT(mCameraControl);
994 // Remove any pending solicited event handlers; these
995 // reference our window object, which in turn references
996 // us. If we don't remove them, we can leak DOM objects.
997 mGetCameraOnSuccessCb = nullptr;
998 mGetCameraOnErrorCb = nullptr;
999 mAutoFocusOnSuccessCb = nullptr;
1000 mAutoFocusOnErrorCb = nullptr;
1001 mTakePictureOnSuccessCb = nullptr;
1002 mTakePictureOnErrorCb = nullptr;
1003 mStartRecordingOnSuccessCb = nullptr;
1004 mStartRecordingOnErrorCb = nullptr;
1005 mReleaseOnSuccessCb = nullptr;
1006 mReleaseOnErrorCb = nullptr;
1007 mSetConfigurationOnSuccessCb = nullptr;
1008 mSetConfigurationOnErrorCb = nullptr;
1010 // Remove all of the unsolicited event handlers too.
1011 mOnShutterCb = nullptr;
1012 mOnClosedCb = nullptr;
1013 mOnRecorderStateChangeCb = nullptr;
1014 mOnPreviewStateChangeCb = nullptr;
1015 mOnAutoFocusMovingCb = nullptr;
1016 mOnFacesDetectedCb = nullptr;
1018 mCameraControl->Shutdown();
1019 }
1021 nsresult
1022 nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
1023 {
1024 NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
1026 return MediaManager::NotifyRecordingStatusChange(mWindow,
1027 aMsg,
1028 true /* aIsAudio */,
1029 true /* aIsVideo */);
1030 }
1032 // Camera Control event handlers--must only be called from the Main Thread!
1033 void
1034 nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState)
1035 {
1036 MOZ_ASSERT(NS_IsMainThread());
1037 ErrorResult ignored;
1039 DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState);
1041 switch (aState) {
1042 case CameraControlListener::kHardwareOpen:
1043 // The hardware is open, so we can return a camera to JS, even if
1044 // the preview hasn't started yet.
1045 if (mGetCameraOnSuccessCb) {
1046 nsRefPtr<GetCameraCallback> cb = mGetCameraOnSuccessCb.forget();
1047 ErrorResult ignored;
1048 mGetCameraOnErrorCb = nullptr;
1049 cb->Call(*this, *mCurrentConfiguration, ignored);
1050 }
1051 break;
1053 case CameraControlListener::kHardwareClosed:
1054 if (mReleaseOnSuccessCb) {
1055 // If we have this event handler, this was a solicited hardware close.
1056 nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
1057 mReleaseOnErrorCb = nullptr;
1058 cb->Call(ignored);
1059 } else if(mOnClosedCb) {
1060 // If not, something else closed the hardware.
1061 nsRefPtr<CameraClosedCallback> cb = mOnClosedCb;
1062 cb->Call(ignored);
1063 }
1064 break;
1066 default:
1067 MOZ_ASSUME_UNREACHABLE("Unanticipated camera hardware state");
1068 }
1069 }
1071 void
1072 nsDOMCameraControl::OnShutter()
1073 {
1074 MOZ_ASSERT(NS_IsMainThread());
1076 DOM_CAMERA_LOGI("DOM ** SNAP **\n");
1078 nsRefPtr<CameraShutterCallback> cb = mOnShutterCb;
1079 if (cb) {
1080 ErrorResult ignored;
1081 cb->Call(ignored);
1082 }
1083 }
1085 void
1086 nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState)
1087 {
1088 MOZ_ASSERT(NS_IsMainThread());
1090 if (!mOnPreviewStateChangeCb) {
1091 return;
1092 }
1094 nsString state;
1095 switch (aState) {
1096 case CameraControlListener::kPreviewStarted:
1097 state = NS_LITERAL_STRING("started");
1098 break;
1100 default:
1101 state = NS_LITERAL_STRING("stopped");
1102 break;
1103 }
1105 nsRefPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
1106 ErrorResult ignored;
1107 cb->Call(state, ignored);
1108 }
1110 void
1111 nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
1112 int32_t aArg, int32_t aTrackNum)
1113 {
1114 // For now, we do nothing with 'aStatus' and 'aTrackNum'.
1115 MOZ_ASSERT(NS_IsMainThread());
1117 ErrorResult ignored;
1118 nsString state;
1120 switch (aState) {
1121 case CameraControlListener::kRecorderStarted:
1122 if (mStartRecordingOnSuccessCb) {
1123 nsRefPtr<CameraStartRecordingCallback> cb = mStartRecordingOnSuccessCb.forget();
1124 mStartRecordingOnErrorCb = nullptr;
1125 cb->Call(ignored);
1126 }
1127 state = NS_LITERAL_STRING("Started");
1128 break;
1130 case CameraControlListener::kRecorderStopped:
1131 NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
1132 state = NS_LITERAL_STRING("Stopped");
1133 break;
1135 #ifdef MOZ_B2G_CAMERA
1136 case CameraControlListener::kFileSizeLimitReached:
1137 state = NS_LITERAL_STRING("FileSizeLimitReached");
1138 break;
1140 case CameraControlListener::kVideoLengthLimitReached:
1141 state = NS_LITERAL_STRING("VideoLengthLimitReached");
1142 break;
1144 case CameraControlListener::kTrackCompleted:
1145 state = NS_LITERAL_STRING("TrackCompleted");
1146 break;
1148 case CameraControlListener::kTrackFailed:
1149 state = NS_LITERAL_STRING("TrackFailed");
1150 break;
1152 case CameraControlListener::kMediaRecorderFailed:
1153 state = NS_LITERAL_STRING("MediaRecorderFailed");
1154 break;
1156 case CameraControlListener::kMediaServerFailed:
1157 state = NS_LITERAL_STRING("MediaServerFailed");
1158 break;
1159 #endif
1161 default:
1162 MOZ_ASSUME_UNREACHABLE("Unanticipated video recorder error");
1163 return;
1164 }
1166 nsRefPtr<CameraRecorderStateChange> cb = mOnRecorderStateChangeCb;
1167 if (cb) {
1168 cb->Call(state, ignored);
1169 }
1170 }
1172 void
1173 nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
1174 {
1175 MOZ_ASSERT(NS_IsMainThread());
1177 // Update our record of the current camera configuration
1178 mCurrentConfiguration = aConfiguration;
1180 DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this);
1181 DOM_CAMERA_LOGI(" mode : %s\n",
1182 mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture");
1183 DOM_CAMERA_LOGI(" maximum focus areas : %d\n",
1184 mCurrentConfiguration->mMaxFocusAreas);
1185 DOM_CAMERA_LOGI(" maximum metering areas : %d\n",
1186 mCurrentConfiguration->mMaxMeteringAreas);
1187 DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n",
1188 mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
1189 DOM_CAMERA_LOGI(" recorder profile : %s\n",
1190 NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
1192 nsRefPtr<CameraSetConfigurationCallback> cb = mSetConfigurationOnSuccessCb.forget();
1193 mSetConfigurationOnErrorCb = nullptr;
1194 if (cb) {
1195 ErrorResult ignored;
1196 cb->Call(*mCurrentConfiguration, ignored);
1197 }
1198 }
1200 void
1201 nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
1202 {
1203 MOZ_ASSERT(NS_IsMainThread());
1205 nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
1206 mAutoFocusOnErrorCb = nullptr;
1207 if (cb) {
1208 ErrorResult ignored;
1209 cb->Call(aAutoFocusSucceeded, ignored);
1210 }
1211 }
1213 void
1214 nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
1215 {
1216 MOZ_ASSERT(NS_IsMainThread());
1218 nsRefPtr<CameraAutoFocusMovingCallback> cb = mOnAutoFocusMovingCb;
1219 if (cb) {
1220 ErrorResult ignored;
1221 cb->Call(aIsMoving, ignored);
1222 }
1223 }
1225 void
1226 nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
1227 {
1228 DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length());
1229 MOZ_ASSERT(NS_IsMainThread());
1231 nsRefPtr<CameraFaceDetectionCallback> cb = mOnFacesDetectedCb;
1232 if (!cb) {
1233 return;
1234 }
1236 Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;
1237 uint32_t len = aFaces.Length();
1239 if (faces.SetCapacity(len)) {
1240 nsRefPtr<DOMCameraDetectedFace> f;
1241 for (uint32_t i = 0; i < len; ++i) {
1242 f = new DOMCameraDetectedFace(this, aFaces[i]);
1243 *faces.AppendElement() = f.forget().take();
1244 }
1245 }
1247 ErrorResult ignored;
1248 cb->Call(faces, ignored);
1249 }
1251 void
1252 nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
1253 {
1254 MOZ_ASSERT(NS_IsMainThread());
1256 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
1257 mTakePictureOnErrorCb = nullptr;
1258 if (!cb) {
1259 // Warn because it shouldn't be possible to get here without
1260 // having passed a success callback into takePicture(), even
1261 // though we guard against a nullptr dereference.
1262 NS_WARNING("DOM Null success callback in OnTakePictureComplete()");
1263 return;
1264 }
1266 ErrorResult ignored;
1267 cb->Call(aPicture, ignored);
1268 }
1270 void
1271 nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError)
1272 {
1273 DOM_CAMERA_LOGI("DOM OnError context=%d, error='%s'\n", aContext,
1274 NS_LossyConvertUTF16toASCII(aError).get());
1275 MOZ_ASSERT(NS_IsMainThread());
1277 nsRefPtr<CameraErrorCallback> errorCb;
1279 switch (aContext) {
1280 case CameraControlListener::kInStartCamera:
1281 mGetCameraOnSuccessCb = nullptr;
1282 errorCb = mGetCameraOnErrorCb.forget();
1283 break;
1285 case CameraControlListener::kInStopCamera:
1286 mReleaseOnSuccessCb = nullptr;
1287 errorCb = mReleaseOnErrorCb.forget();
1288 break;
1290 case CameraControlListener::kInSetConfiguration:
1291 mSetConfigurationOnSuccessCb = nullptr;
1292 errorCb = mSetConfigurationOnErrorCb.forget();
1293 break;
1295 case CameraControlListener::kInAutoFocus:
1296 mAutoFocusOnSuccessCb = nullptr;
1297 errorCb = mAutoFocusOnErrorCb.forget();
1298 break;
1300 case CameraControlListener::kInTakePicture:
1301 mTakePictureOnSuccessCb = nullptr;
1302 errorCb = mTakePictureOnErrorCb.forget();
1303 break;
1305 case CameraControlListener::kInStartRecording:
1306 mStartRecordingOnSuccessCb = nullptr;
1307 errorCb = mStartRecordingOnErrorCb.forget();
1308 break;
1310 case CameraControlListener::kInStopRecording:
1311 // This method doesn't have any callbacks, so all we can do is log the
1312 // failure. This only happens after the hardware has been released.
1313 NS_WARNING("Failed to stop recording");
1314 return;
1316 case CameraControlListener::kInStartPreview:
1317 // This method doesn't have any callbacks, so all we can do is log the
1318 // failure. This only happens after the hardware has been released.
1319 NS_WARNING("Failed to (re)start preview");
1320 return;
1322 case CameraControlListener::kInUnspecified:
1323 if (aError.EqualsASCII("ErrorServiceFailed")) {
1324 // If the camera service fails, we will get preview-stopped and
1325 // hardware-closed events, so nothing to do here.
1326 NS_WARNING("Camera service failed");
1327 return;
1328 }
1329 if (aError.EqualsASCII("ErrorSetPictureSizeFailed") ||
1330 aError.EqualsASCII("ErrorSetThumbnailSizeFailed")) {
1331 // We currently don't handle attribute setter failure. Practically,
1332 // this only ever happens if a setter is called after the hardware
1333 // has gone away before an asynchronous set gets to happen, so we
1334 // swallow these.
1335 NS_WARNING("Failed to set either picture or thumbnail size");
1336 return;
1337 }
1338 // fallthrough
1340 default:
1341 MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state");
1342 return;
1343 }
1345 if (!errorCb) {
1346 DOM_CAMERA_LOGW("DOM No error handler for error '%s' in context=%d\n",
1347 NS_LossyConvertUTF16toASCII(aError).get(), aContext);
1348 return;
1349 }
1351 ErrorResult ignored;
1352 errorCb->Call(aError, ignored);
1353 }