Files
palemoon27/dom/camera/DOMCameraControl.cpp
T
roytam1 fffeb135bb import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1040668 part 2 - Parse and compute text emphasis properties. r=dbaron (0fb79d4709)
- Bug 1040668 part 3 - Add helper function gfxTextRun::GetAdvanceForGlyph. r=jfkthame (f1cf02f5ff)
- Bug 1216427 - Tests for backspacing over a character with variation selector, and over Regional Indicator flag symbols. r=emk (18957bfe77)
- Bug 1216427 - part 1 - Ensure a character+VS sequence or a ligated Regional-Indicator flag symbol is deleted as a single unit when backspacing. r=emk (2776ff8f4a)
- Bug 1216427 - part 2 - Ensure mouse selection does not split up a Regional Indicator flag symbol. r=emk (58eb82e6a1)
- Bug 1040668 part 4 - Add helper function for ensuring a glyph is a complex glyph. r=jfkthame (ba17f7d0c4)
- Bug 1040668 part 5 - Avoid unnecessary allocation inside EnsureComplexGlyph helper function. r=jfkthame (4968a7c68e)
- Bug 1040668 part 6 - Add some specifier on gfxTextRun and gfxShapedWord so that compilers are able to reason out certain optimizations. r=jfkthame (69ca3eb959)
- Bug 1040668 part 7 - Add NO_EMPHASIS_MARK flag in CompressedGlyph. r=jfkthame (34e9d8a6a6)
- Bug 1227001 part 1 - Remove SetupBreakSinksFlags from BuildTextRunsScanner. r=jfkthame (520b1ba111)
- Bug 1227001 part 2 - Remove no longer used mExistingTextRun from BreaSink. r=jfkthame (201782a78c)
- Bug 1227001 part 3 - Remove no longer used mChangedBreaks from BreakSink. r=jfkthame (e754e2b13f)
- Bug 1040668 part 8 - Setup text emphasis for text run. r=jfkthame (376377180e)
- Bug 1040668 part 9 - Compute overflow from text-shadow after text decorations. r=dbaron (05c9bd08c2)
- Bug 1040668 part 10 - Implement emphasis mark rendering. r=jfkthame (4d270afca8)
- Bug 1040668 part 11 - Move line leadings adjusting code into a separate function in nsLineLayout. r=dholbert (fd4dd20ca5)
- Bug 1040668 part 12 - Add line leadings for emphasis marks if necessary. r=dholbert (138add5ff7)
- Bug 1040668 part 13 - Move first part of nsStyleFont::GetLanguage to nsPresContext::GetContentLanguage. r=dbaron (1e872d2a58)
- Bug 1040668 part 14 - Add helper function nsStyleUtil::MatchesLanguagePrefix for doing simple language matching. r=dbaron (9322a02369)
- Bug 1040668 part 15 - Make the default value of text-emphasis-position aware of the language. r=dbaron (6587c628da)
- Bug 1040668 part 16 - Add reftests for text-emphasis. r=dbaron (9940d65182)
- Bug 1040668 followup - Use monospace for text-emphasis reftests. rs=dbaron on a CLOSED TREE (fb7598c3ea)
- Bug 1040668 followup 2 - Disable failing reftests of text-emphasis on Windows XP. (7c3f24ac22)
- Bug 1040668 followup 3 - Wrap lang attribute mapping code in NS_STYLE_INHERIT_BIT test. r=dbaron (640e3b7b8f)
- Bug 1219145 - nsRefreshDriver::IsJankCritical(). r=hiro To refine its alerts, Performance Stats API needs to be able to know whether a long-running operation is actually causing user-visible jank in the current process. This patch introduces a trivial API that lets clients ask the refresh driver whether any kind of animation is ongoing. (7c0868d7c4)
- more missing XP theme stuff (637af0c6a0)
- Bug 1210261. Tick root refresh driver last. r=mattwoodrow (dae1a325fa)
- Bug 1221674 Part 2: Correct for negative content delay values. r=avih (9b9811c41c)
- Bug 1211334 - Check if presshell is still available after dispatching transition events; r=mats (8a044a462c)
- Bug 1211599 - Only allow whitelisted histograms to have > 100 buckets. r=nfroyd (d467e84130)
- Bug 1219733 - Allow a 'bug_numbers' field in Histograms.json entries. r=vladan (8e63a713ec)
- Bug 1222044 - Only allow lists of alert_emails r=vladan (1d5fcb009d)
- Bug 1168263 - Add a flags parameter to GetResultingTransformMatrix instead of using bools. r=roc (ec5224f9af)
- Bug 1168263 - Remove TransformRectOut since it's unused. r=roc (d20a79ae0e)
- Bug 1215406 - Part 1: Remove NS_STYLE_ANIMATION_DIRECTION_XXX and NS_STYLE_ANIMATION_FILL_MODE_XXX. r=heycam (4bd7f8116b)
- Bug 1215406 - Part 2: Change the types of direction and fillmode in StyleAnimation. r=heycam (e06323c81b)
- Bug 1215406 - Part 3: Change the types of direction and fillmode in AnimationTiming. r=heycam (e5454d3ea5)
- Bug 1215406 - Part 4: Add KeyframeEffectOptions. r=smaug Add KeyframeEffectOptions in KeyframeEffect.webidl (0ce3372fd1)
- Bug 1215406 - Part 5: Implement KeyframeEffectOptions in KeyframeEffectReadOnly constructor. r=birtles (a472e9ac5f)
- tch 2 - Use an enum class for NS_STYLE_BOX_SIZING_*. r=heycam (4897b7b8ff)
- Bug 1218195, mark MutationObserver as observing in all the nested DOM mutations, r=bz (48a4aa8a91)
- Bug 1172870 - Part 3 - Fix openWindow mochitest to work on e10s (574cc6fa4d)
- Bug 1223265 - Fix -Wunreachable-code and -Wimplicit-fallthrough warnings in dom/bindings and dom/ipc. r=khuey (9eab632140)
- Bug 1189195 - Fix PContentPermissionRequest shutdown () r=fabrice (6bc1a681f5)
- Bug 1210508 - Handle null OriginAttributes from JS-implemented nsILoadContext. r=me (2f71edb9d7)
- Bug 1224596 part 1. Add a version of WorkerMainThreadRunnable::Dispatch that takes an ErrorResult to report failure to dispatch on. r=khuey (e60a0fb115)
- Bug 1224596 part 2. Switch Navigator to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (d9298a4763)
- Bug 1224596 part 3. Make some WebSocket(Impl) methods whose return value is totally ignored void. r=khuey (1d04b52d44)
- Bug 1224596 part 4. Switch WebSocket to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (aa06e6417b)
- Bug 1224596 part 5. Switch nsPerformance to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (0303b7de09)
- Bug 1224596 part 6. Switch BroadcastChannel to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (feacc21f63)
- Bug 1224636. Fix some code in ImageBitmap that rejects a promise with an ErrorResult, then keeps trying to use that ErrorResult. r=kaku (89b769fe07)
- Bug 1224596 part 7. Switch ImageBitmap to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (e21b0a4a0a)
- Bug 1183954, Don't leak nsStructuredCloneContainer (use of 'auto' is error prone), r=leak (1a9b30f2ca)
- Bug 1114554 - Patch 9 - Fixed crash in b2g-desktop tests. r=nsm (6b12c6d121)
- Bug 1189090 - Rework the nsISupports implementation in the ScopeCheckingGetCallback and its subclasses; r=nsm (6bea544597)
- Bug 1187018 - Ensure feature is nulled out if it does not get added. r=khuey (9d7439498a)
- Bug 1224596 part 8. Switch Notification to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (03f0f6877f)
- Bug 1196079 - Always try to release Notification via normal WorkerRunnable first. r=wchen (c093253d6f)
- Bug 1203324 - disable notifications on serviceworkers. r=ehsan,wchen (aa39310dc1)
- Bug 1199901 - GetOrigin() fails cleanly instead of asserting principal. r=wchen (148c634a4f)
- Bug 1199901 - Clear mObserver when WorkerNotificationObserver is destroyed. r=wchen (6d5cd99183)
- Bug 1199901 - Bustage fix due to rebase. a=bustage (344bd62af5)
- Bug 1225470 Report a message to the console when a service worker waitUntil() is rejected. r=baku (610da2eec6)
- Bug 1217909 P1 Report service worker exceptions to controlled documents. r=catalinb (f8bd4677d5)
- Bug 1216566 - Fix a bug in nsIServiceWorkerManager.getAllRegistrations;r=catalinb (ed3e14ab43)
- Bug 1219205 - ServiceWorkerInfo should be an XPCOM object;r=catalinb (677a6f1ffd)
- Bug 1217909 P2 Track registering documents as weak reference so SWM can report errors to them. r=catalinb (4233dc3edc)
- Bug 1217909 P3 Refactor service worker register()/update() to reject only with SecurityErr or TypeErr. r=catalinb (c6891a7fae)
- Bug 1220740 - nsIServiceWorkerRegistrationInfo should emit an event when its scriptSpec property changes;r=amarchesini (5772bb5914)
- Bug 1207727 - Add WPT tests for service worker update algorithm. r=bkelly (a4812571a1)
- Bug 1217367 - Service workers update algorithm optimization. r=bkelly (e377debad1)
- Bug 1226479. Change ErrorResult::ThrowTypeError/ThrowRangeError to take string references, not pointers. r=mccr8 (0804899666)
- Bug 1224659 - Worker DataStore code should not use ErrorResult cross threads, r=bz (aef03b0fdd)
- Bug 1224596 part 9. Switch DataStore to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (1eb1c427f2)
- Bug 1224596 part 10. Switch DataStoreCursor to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (114af8021b)
- Bug 1224596 part 11. Switch WorkerNavigator to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (b2347a0c49)
- Bug 1224596 part 12. Switch ServiceWorkerRegistration to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (d1ff076836)
- Bug 1224596 part 13. Switch gfxUtils to using the new WorkerMainThreadRunnable::Dispatch signature. r=khuey (a523997588)
- Bug 1224007 part 1. Rename ThrowMethodFailed to MaybeSetPendingException and make it an ErrorResult instance method. r=peterv (bdf0891f54)
- Bug 1224007 part 2. Make the various ErrorResult::Report* methods private, so consumers all go through MaybeSetPendingException and rename them to more clearly indicate what they're actually doing. r=peterv (48f483c153)
- Bug 1203151 - Allow disabling of screen wakelocks for video elements. r=baku (6bd9ff6916)
- Bug 1224007 part 4. Fix some cases in which ErrorResult instances are destroyed without doing anything useful with exceptions on them. r=peterv (e328785e5d)
- Bug 1224007 part 5. Get rid of ErrorResult::StealJSException. r=peterv (db19cfb31e)
- Bug 1224007 part 3. Push down WouldReportJSException into MaybeSetPendingException, since anyone calling the latter will propagate the JS exception as needed. r=peterv (1d3b7b415d)
- Bug 1224007 part 6. Change MaybeSetPendingException to set the ErrorResult state to "not failed", just like SuppressException and StealNSResult already do, and assert in the destructor that the ErrorResult is not Failed(). (a028838e8d)
- Bug 1213815 - Update URLSearchParams and URLUtils in webidl files, r=bz (28fb8f7de5)
- Bug 1213815 - dom/webidl/HTMLHyperlinkElementUtils and URL don't need to throw exceptions as we did before, r=bz (b13dc3bcb9)
- Bug 1224596 part 14. Switch URL to using the new WorkerMainThreadRunnable::Dispatch signature. r=baku (232677e50e)
- fix updating the backport (9e958da5ca)
- Bug 1224596 part 16. Switch Fetch to using the new WorkerMainThreadRunnable::Dispatch signature. r=bkelly (939e338f22)
- Bug 1224596 part 17. Remove the old WorkerMainThreadRunnable::Dispatch signature. r=khuey (5a70429ec8)
- Bug 1143575. Don't report negative frame delays. r=cpearce (7d8bc0f753)
- Bug 1187371 - Get rid of dom.broadcastChannel.enabled pref, r=bz (9335b7ae90)
- Bug 1196514 - remove dom.messagechannel.enabled pref, r=smaug (31e06119b4)
- Bug 1166356 - Properly detect double-caching in nsXULPrototypeCache; r=ehsan (29df9ffb2d)
- Bug 1168916 - Get rid of redundant pref callback in nsXULPrototypeCache; r=janv (9f37fff405)
- Bug 1139099: Dispatch DOMDocElementInserted to match the document-element-inserted observer notification. r=mrbkap (6565e4b924)
- Bug 1187068 - Tell the cycle collector about nsContentSink::mCSSLoader. r=heycam (4ae23eb26c)
- Bug 1172189 - Fix overflow in nsXULContentSink.cpp. r=ehsan (cc6330f5de)
- Bug 1126010 - XULContentSinkImpl::mParser should be an nsRefPtr. r=smaug (d6bb567692)
- Bug 1147946, part 7 - Remove trailing whitespace from nsXULContentSink.cpp. r=baku (cdcadbfeeb)
- Bug 1147946, part 1 - Tuck elses in nsXULContentSink.cpp. r=baku (26fd806676)
- Bug 1147946, part 2 - Move body of check inside prior if in XULContentSinkImpl::OpenScript(). r=baku (b509455bdb)
- Bug 1147946, part 3 - Eliminate unused case for non-JS scripting languages in XULContentSinkImpl::OpenScript(). r=baku (4136933cc2)
- Bug 1147946, part 4 - Use an early return in XULContentSinkImpl::OpenScript(). r=baku (c3e293474b)
- Bug 1147946, part 5 - Don't use the generic nsIProgrammingLanguage enum in XULContentSinkImpl::OpenScript(). r=baku (89a124e23f)
- Bug 1147946, part 6 - Remove some useless null checks on infallible new in XULContentSinkImpl. r=baku (1ac57e8c3a)
- Bug 1221351 P1 ServiceWorkerContainer and ServiceWorkerRegistration should not crash for null window owner. r=catalinb (1a72748632)
- Bug 1212867 - Node.isEqualNode() should ignore internal subsets; r=bzBug (99b166ffee)
- Bug 492933 - getElementsByTagName should match on localName not tagName, r=smaug (d0c6ceabf1)
- Bug 912470 part 1 - Implement Encoding Standard-compliant big5 decoder. r=emk. (c680b0ae9b)
- Bug 1170932: Test handling of unmapped characters in unicode-to-codepage encoders (ca36bcbd35)
- fix style (95a90bfe3a)
- Bug 1170794 - patch 2 - Improve the length check of the input in nsUnicode*::GetMaxLength, r=dveditz (aa864d656f)
- Bug 1170794 followup: Add 'override' annotations to Convert() & Reset() methods in intl/uconv. rs=ehsan (bb3e6e492e)
- Bug 1176462 - Remove nsTableDecoderSupport. r=smontagu (f4a86c44b3)
- Bug 1169248 - Fix GBK/GB18030 encoders. r=smontagu (ed946e1ee1)
- Bug 1155539 - Remove obsolete encoding decoder telemetry probes. r=emk. (44e15bfb40)
- Bug 912470 part 2 - Implement Encoding Standard-compliant big5 encoder. r=emk. (5cca2dc4a0)
- Bug 912470 addendum - Pass override static analysis. r=emk. (c163bffeb4)
- Bug 1170932: Improve error handling for the gbk encoder, r=emk (30e95b34a5)
- Bug 1202366 - Implement the encoder error mode "HTML" for nsFormSubmission without nsISaveAsCharset. r=NOT_emk. (ebc8b542dd)
- update manifest (6cc19172cc)
- Bug 1197309 - remove PR_snprintf calls in intl/; r=froydnj (79fcdfa845)
- Bug 1214619 - Remove nsISaveAsCharset as much as possible without breaking extensions in popular use. r=emk. (89b71b3d87)
- Bug 1214857. Store the document-is-HTML state directly in nsContentList instead of refetching from the node being matched. r=smaug (4c4fbf469e)
- Bug 1221351 P2 Add a web-platform-test to check for crash when calling .register() on closed window. r=catalinb (eeb30c1bba)
- Bug 1221351 P3 Fix test name in register-closed-window.https.html. a=testonly (5bfa840044)
- Bug 1224436: Remove enumerator usage in ServiceWorkerManager.cpp. r=njn (b8cb094d3c)
- Bug 1223716. Make HTMLCollection check for the element being HTML before checking for its name inside its named getter. r=bkelly (977e0bff5a)
- Bug 1180737 - Add update-test.py and update test to latest version. r=bkelly. (59faa36d5c)
- Bug 1217909 P4 Extend wpt tests to verify update() promise values for different script failures. r=catalinb (a2f7352a3a)
- Bug 1217909 P5 Add wpt test case for fetch event handlers that throw. r=catalinb (56a77f611c)
- Bug 1217909 P6 Fix wpt registration.https.html to expect TypeError for script evaluation errors. r=catalinb (3de8a45688)
- Bug 1217909 P7 Fix mochitest to expect TypeError when serviceWorker.register() rejects. r=catalinb (6e8841c41e)
- Bug 1217909 P8 Track navigation interceptions per scope in ServiceWorkerManager. r=catalinb (6705ba8337)
- Bug 1217909 P9 Report exceptions to windows performing an intercepted navigation. r=catalinb (52f9fece14)
- Bug 1217909 P10 Remove stale nsTArray when the last registering document for a scope is removed. r=catalinb (b739bcc3b2)
- Bug 1217909 P11 Only report errors to documents that are active and not in the bfcache. r=catalinb (5ffd633af2)
- Bug 1223378 Tighten service worker register() principal checks. r=baku (478785f2cc)
- Bug 1189685 - Part 1: Ensure that the state of all ServiceWorker instances is up to date when dispatching statechange events; r=bkelly (2bd9b78c58)
- Bug 1189685 - Part 2: Make synced-state.https.html pass; r=bkelly (029f942d8c)
- Bug 1220740 - nsIServiceWorkerRegistrationInfo should emit an event when its worker properties change;r=amarchesini (8243a3debc)
- Bug 1186856 ServiceWorker .register() should always stop current registration from uninstalling. r=jdm (ec7d6e0e7c)
- Bug 1224941 Don't crash during ServiceWorker life cycle event dispatch if window is gone. r=baku (a3f45af3e4)
- Bug 1180754 - Get serviceworkerobject-scripturl test passing. r=bkelly (c7979bef47)
- Bug 1201498 - Service worker update should compare scriptURL to worker URL without fragment, r=bkelly (914f630528)
- Bug 594505 - Remove obsolete comment since this bug has now been fixed. r=me DONTBUILD (44f3a15b91)
- Bug 1221840. Support repeating images in 1 axis. r=seth (449ea3e97e)
- const-var (5433688051)
- Bug 1574573 - Disambiguate a use of Handle in XPCShellEnvironment.cpp r=Ehsan (15b44177d1)
- clean up warnings (6e64313d0c)
2023-02-14 09:56:52 +08:00

1757 lines
52 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMCameraControl.h"
#include "base/basictypes.h"
#include "nsCOMPtr.h"
#include "nsDOMClassInfo.h"
#include "nsHashPropertyBag.h"
#include "nsThread.h"
#include "DeviceStorage.h"
#include "DeviceStorageFileDescriptor.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/MediaManager.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "nsIAppsService.h"
#include "nsIObserverService.h"
#include "nsIDOMEventListener.h"
#include "nsIScriptSecurityManager.h"
#include "Navigator.h"
#include "nsXULAppAPI.h"
#include "DOMCameraManager.h"
#include "DOMCameraCapabilities.h"
#include "CameraCommon.h"
#include "nsGlobalWindow.h"
#include "CameraPreviewMediaStream.h"
#include "mozilla/dom/CameraUtilBinding.h"
#include "mozilla/dom/CameraControlBinding.h"
#include "mozilla/dom/CameraManagerBinding.h"
#include "mozilla/dom/CameraCapabilitiesBinding.h"
#include "mozilla/dom/CameraConfigurationEvent.h"
#include "mozilla/dom/CameraConfigurationEventBinding.h"
#include "mozilla/dom/CameraFacesDetectedEvent.h"
#include "mozilla/dom/CameraFacesDetectedEventBinding.h"
#include "mozilla/dom/CameraStateChangeEvent.h"
#include "mozilla/dom/CameraClosedEvent.h"
#include "mozilla/dom/VideoStreamTrack.h"
#include "mozilla/dom/BlobEvent.h"
#include "DOMCameraDetectedFace.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsPrintfCString.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
class mozilla::TrackCreatedListener : public MediaStreamListener
{
public:
explicit TrackCreatedListener(nsDOMCameraControl* aCameraControl)
: mCameraControl(aCameraControl) {}
void Forget() { mCameraControl = nullptr; }
void DoNotifyTrackCreated(TrackID aTrackID)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mCameraControl) {
return;
}
mCameraControl->TrackCreated(aTrackID);
}
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset, uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
if (aTrackEvents & TRACK_EVENT_CREATED) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArgs<TrackID>(
this, &TrackCreatedListener::DoNotifyTrackCreated, aID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
protected:
~TrackCreatedListener() {}
nsDOMCameraControl* mCameraControl;
};
#ifdef MOZ_WIDGET_GONK
StaticRefPtr<ICameraControl> nsDOMCameraControl::sCachedCameraControl;
/* static */ nsresult nsDOMCameraControl::sCachedCameraControlStartResult = NS_OK;
/* static */ nsCOMPtr<nsITimer> nsDOMCameraControl::sDiscardCachedCameraControlTimer;
#endif
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
// nsISupports is an ambiguous base of nsDOMCameraControl
// so we have to work around that.
if ( aIID.Equals(NS_GET_IID(nsDOMCameraControl)) )
foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
else
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream)
NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream)
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream,
mAudioChannelAgent,
mCapabilities,
mWindow,
mGetCameraPromise,
mAutoFocusPromise,
mTakePicturePromise,
mStartRecordingPromise,
mReleasePromise,
mSetConfigurationPromise)
/* static */
bool
nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* aGlobal)
{
return Navigator::HasCameraSupport(aCx, aGlobal);
}
static nsresult
RegisterStorageRequestEvents(DOMRequest* aRequest, nsIDOMEventListener* aListener)
{
EventListenerManager* elm = aRequest->GetOrCreateListenerManager();
if (NS_WARN_IF(!elm)) {
return NS_ERROR_UNEXPECTED;
}
elm->AddEventListener(NS_LITERAL_STRING("success"), aListener, false, false);
elm->AddEventListener(NS_LITERAL_STRING("error"), aListener, false, false);
return NS_OK;
}
class mozilla::StartRecordingHelper : public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
explicit StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
: mDOMCameraControl(aDOMCameraControl)
, mState(false)
{
MOZ_COUNT_CTOR(StartRecordingHelper);
}
protected:
virtual ~StartRecordingHelper()
{
MOZ_COUNT_DTOR(StartRecordingHelper);
mDOMCameraControl->OnCreatedFileDescriptor(mState);
}
protected:
RefPtr<nsDOMCameraControl> mDOMCameraControl;
bool mState;
};
NS_IMETHODIMP
StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
{
nsString eventType;
aEvent->GetType(eventType);
mState = eventType.EqualsLiteral("success");
return NS_OK;
}
NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
: CameraConfiguration()
, mMaxFocusAreas(0)
, mMaxMeteringAreas(0)
{
MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
}
nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration)
: CameraConfiguration(aConfiguration)
, mMaxFocusAreas(0)
, mMaxMeteringAreas(0)
{
MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
}
nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration()
{
MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration);
}
#ifdef MOZ_WIDGET_GONK
// This should be long enough for even our slowest platforms.
static const unsigned long kCachedCameraTimeoutMs = 3500;
// Open the battery-door-facing camera by default.
static const uint32_t kDefaultCameraId = 0;
/* static */ void
nsDOMCameraControl::PreinitCameraHardware()
{
// Assume a default, minimal configuration. This should initialize the
// hardware, but won't (can't) start the preview.
RefPtr<ICameraControl> cameraControl = ICameraControl::Create(kDefaultCameraId);
if (NS_WARN_IF(!cameraControl)) {
return;
}
sCachedCameraControlStartResult = cameraControl->Start();
if (NS_WARN_IF(NS_FAILED(sCachedCameraControlStartResult))) {
return;
}
sCachedCameraControl = cameraControl;
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (NS_WARN_IF(!timer)) {
return;
}
nsresult rv = timer->InitWithFuncCallback(DiscardCachedCameraInstance,
nullptr,
kCachedCameraTimeoutMs,
nsITimer::TYPE_ONE_SHOT);
if (NS_WARN_IF(NS_FAILED(rv))) {
// If we can't start the timer, it's possible for an app to never grab the
// camera, leaving the hardware tied up indefinitely. Better to take the
// performance hit.
sCachedCameraControl = nullptr;
return;
}
sDiscardCachedCameraControlTimer = timer;
}
/* static */ void
nsDOMCameraControl::DiscardCachedCameraInstance(nsITimer* aTimer, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
sDiscardCachedCameraControlTimer = nullptr;
sCachedCameraControl = nullptr;
}
#endif
nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
Promise* aPromise,
nsPIDOMWindow* aWindow)
: DOMMediaStream()
, mCameraControl(nullptr)
, mAudioChannelAgent(nullptr)
, mGetCameraPromise(aPromise)
, mWindow(aWindow)
, mPreviewState(CameraControlListener::kPreviewStopped)
, mRecording(false)
, mRecordingStoppedDeferred(false)
, mSetInitialConfig(false)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mInput = new CameraPreviewMediaStream(this);
BindToOwner(aWindow);
RefPtr<DOMCameraConfiguration> initialConfig =
new DOMCameraConfiguration(aInitialConfig);
// Create and initialize the underlying camera.
ICameraControl::Configuration config;
bool haveInitialConfig = false;
nsresult rv;
switch (aInitialConfig.mMode) {
case CameraMode::Picture:
config.mMode = ICameraControl::kPictureMode;
haveInitialConfig = true;
break;
case CameraMode::Video:
config.mMode = ICameraControl::kVideoMode;
haveInitialConfig = true;
break;
case CameraMode::Unspecified:
break;
default:
MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode!");
break;
}
if (haveInitialConfig) {
rv = SelectPreviewSize(aInitialConfig.mPreviewSize, config.mPreviewSize);
if (NS_FAILED(rv)) {
mListener->OnUserError(DOMCameraControlListener::kInStartCamera, rv);
return;
}
config.mPictureSize.width = aInitialConfig.mPictureSize.mWidth;
config.mPictureSize.height = aInitialConfig.mPictureSize.mHeight;
config.mRecorderProfile = aInitialConfig.mRecorderProfile;
}
#ifdef MOZ_WIDGET_GONK
bool gotCached = false;
if (sCachedCameraControl && aCameraId == kDefaultCameraId) {
mCameraControl = sCachedCameraControl;
sCachedCameraControl = nullptr;
gotCached = true;
} else {
sCachedCameraControl = nullptr;
#endif
mCameraControl = ICameraControl::Create(aCameraId);
#ifdef MOZ_WIDGET_GONK
}
#endif
mCurrentConfiguration = initialConfig.forget();
// Register a TrackCreatedListener directly on CameraPreviewMediaStream
// so we can know the TrackID of the video track.
mTrackCreatedListener = new TrackCreatedListener(this);
mInput->AddListener(mTrackCreatedListener);
// Register the playback listener directly on the camera input stream.
// We want as low latency as possible for the camera, thus avoiding
// MediaStreamGraph altogether. Don't do the regular InitStreamCommon()
// to avoid initializing the Owned and Playback streams. This is OK since
// we are not user/DOM facing anyway.
CreateAndAddPlaybackStreamListener(mInput);
MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!");
if (mWindow->GetExtantDoc()) {
CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal());
}
// Register a listener for camera events.
mListener = new DOMCameraControlListener(this, mInput);
mCameraControl->AddListener(mListener);
#ifdef MOZ_WIDGET_GONK
if (!gotCached || NS_FAILED(sCachedCameraControlStartResult)) {
#endif
// Start the camera...
if (haveInitialConfig) {
rv = mCameraControl->Start(&config);
if (NS_SUCCEEDED(rv)) {
mSetInitialConfig = true;
}
} else {
rv = mCameraControl->Start();
}
#ifdef MOZ_WIDGET_GONK
} else {
if (haveInitialConfig) {
rv = mCameraControl->SetConfiguration(config);
if (NS_SUCCEEDED(rv)) {
mSetInitialConfig = true;
}
} else {
rv = NS_OK;
}
}
#endif
if (NS_FAILED(rv)) {
mListener->OnUserError(DOMCameraControlListener::kInStartCamera, rv);
}
}
nsDOMCameraControl::~nsDOMCameraControl()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
/*invoke DOMMediaStream destroy*/
Destroy();
if (mInput) {
mInput->Destroy();
mInput = nullptr;
}
if (mTrackCreatedListener) {
mTrackCreatedListener->Forget();
mTrackCreatedListener = nullptr;
}
}
JSObject*
nsDOMCameraControl::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return CameraControlBinding::Wrap(aCx, this, aGivenProto);
}
bool
nsDOMCameraControl::IsWindowStillActive()
{
return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
}
nsresult
nsDOMCameraControl::SelectPreviewSize(const CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize)
{
if (aRequestedPreviewSize.mWidth && aRequestedPreviewSize.mHeight) {
aSelectedPreviewSize.width = aRequestedPreviewSize.mWidth;
aSelectedPreviewSize.height = aRequestedPreviewSize.mHeight;
} else {
/* Use the window width and height if no preview size is provided.
Note that the width and height are actually reversed from the
camera perspective. */
int32_t width = 0;
int32_t height = 0;
float ratio = 0.0;
nsresult rv;
rv = mWindow->GetDevicePixelRatio(&ratio);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mWindow->GetInnerWidth(&height);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mWindow->GetInnerHeight(&width);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(width > 0);
MOZ_ASSERT(height > 0);
MOZ_ASSERT(ratio > 0.0);
aSelectedPreviewSize.width = std::ceil(width * ratio);
aSelectedPreviewSize.height = std::ceil(height * ratio);
}
return NS_OK;
}
// Setter for weighted regions: { top, bottom, left, right, weight }
nsresult
nsDOMCameraControl::Set(uint32_t aKey, const Optional<Sequence<CameraRegion> >& aValue, uint32_t aLimit)
{
if (aLimit == 0) {
DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
return NS_OK;
}
nsTArray<ICameraControl::Region> regionArray;
if (aValue.WasPassed()) {
const Sequence<CameraRegion>& regions = aValue.Value();
uint32_t length = regions.Length();
DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
if (length > aLimit) {
length = aLimit;
}
// aLimit supplied by camera library provides sane ceiling (i.e. <10)
regionArray.SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
ICameraControl::Region* r = regionArray.AppendElement();
const CameraRegion &region = regions[i];
r->top = region.mTop;
r->left = region.mLeft;
r->bottom = region.mBottom;
r->right = region.mRight;
r->weight = region.mWeight;
DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
i,
r->top,
r->left,
r->bottom,
r->right,
r->weight
);
}
} else {
DOM_CAMERA_LOGI("%s:%d : clear regions\n", __func__, __LINE__);
}
return mCameraControl->Set(aKey, regionArray);
}
// Getter for weighted regions: { top, bottom, left, right, weight }
nsresult
nsDOMCameraControl::Get(uint32_t aKey, nsTArray<CameraRegion>& aValue)
{
nsTArray<ICameraControl::Region> regionArray;
nsresult rv = mCameraControl->Get(aKey, regionArray);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t length = regionArray.Length();
DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
aValue.SetLength(length);
for (uint32_t i = 0; i < length; ++i) {
ICameraControl::Region& r = regionArray[i];
CameraRegion& v = aValue[i];
v.mTop = r.top;
v.mLeft = r.left;
v.mBottom = r.bottom;
v.mRight = r.right;
v.mWeight = r.weight;
DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
i,
v.mTop,
v.mLeft,
v.mBottom,
v.mRight,
v.mWeight
);
}
return NS_OK;
}
MediaStream*
nsDOMCameraControl::GetCameraStream() const
{
return mInput;
}
void
nsDOMCameraControl::TrackCreated(TrackID aTrackID) {
// This track is not connected through a port.
MediaInputPort* inputPort = nullptr;
dom::VideoStreamTrack* track =
new dom::VideoStreamTrack(this, aTrackID);
RefPtr<TrackPort> port =
new TrackPort(inputPort, track,
TrackPort::InputPortOwnership::OWNED);
mTracks.AppendElement(port.forget());
NotifyTrackAdded(track);
}
#define THROW_IF_NO_CAMERACONTROL(...) \
do { \
if (!mCameraControl) { \
DOM_CAMERA_LOGW("mCameraControl is null at %s:%d\n", __func__, __LINE__); \
aRv = NS_ERROR_NOT_AVAILABLE; \
return __VA_ARGS__; \
} \
} while (0)
void
nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
}
void
nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect);
}
void
nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
}
void
nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
}
void
nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode);
}
void
nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode);
}
void
nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode);
}
void
nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode);
}
void
nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode);
}
void
nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
}
void
nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode);
}
void
nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode);
}
double
nsDOMCameraControl::GetPictureQuality(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(1.0);
double quality;
aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_QUALITY, quality);
return quality;
}
void
nsDOMCameraControl::SetPictureQuality(double aQuality, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_QUALITY, aQuality);
}
void
nsDOMCameraControl::GetMeteringMode(nsString& aMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Get(CAMERA_PARAM_METERINGMODE, aMode);
}
void
nsDOMCameraControl::SetMeteringMode(const nsAString& aMode, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_METERINGMODE, aMode);
}
double
nsDOMCameraControl::GetZoom(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(1.0);
double zoom = 1.0;
aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom);
return zoom;
}
void
nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
}
void
nsDOMCameraControl::GetMeteringAreas(nsTArray<CameraRegion>& aAreas, ErrorResult& aRv)
{
aRv = Get(CAMERA_PARAM_METERINGAREAS, aAreas);
}
void
nsDOMCameraControl::SetMeteringAreas(const Optional<Sequence<CameraRegion> >& aMeteringAreas, ErrorResult& aRv)
{
aRv = Set(CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
mCurrentConfiguration->mMaxMeteringAreas);
}
void
nsDOMCameraControl::GetFocusAreas(nsTArray<CameraRegion>& aAreas, ErrorResult& aRv)
{
aRv = Get(CAMERA_PARAM_FOCUSAREAS, aAreas);
}
void
nsDOMCameraControl::SetFocusAreas(const Optional<Sequence<CameraRegion> >& aFocusAreas, ErrorResult& aRv)
{
aRv = Set(CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
mCurrentConfiguration->mMaxFocusAreas);
}
void
nsDOMCameraControl::GetPictureSize(CameraSize& aSize, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
ICameraControl::Size size;
aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size);
if (aRv.Failed()) {
return;
}
aSize.mWidth = size.width;
aSize.mHeight = size.height;
}
void
nsDOMCameraControl::SetPictureSize(const CameraSize& aSize, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
ICameraControl::Size s = { aSize.mWidth, aSize.mHeight };
aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
}
void
nsDOMCameraControl::GetThumbnailSize(CameraSize& aSize, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
ICameraControl::Size size;
aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
if (aRv.Failed()) {
return;
}
aSize.mWidth = size.width;
aSize.mHeight = size.height;
}
void
nsDOMCameraControl::SetThumbnailSize(const CameraSize& aSize, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
ICameraControl::Size s = { aSize.mWidth, aSize.mHeight };
aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
}
double
nsDOMCameraControl::GetFocalLength(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(0.0);
double focalLength;
aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength);
return focalLength;
}
double
nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(0.0);
double distance;
aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance);
return distance;
}
double
nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(0.0);
double distance;
aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance);
return distance;
}
double
nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(0.0);
double distance;
aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance);
return distance;
}
void
nsDOMCameraControl::SetExposureCompensation(double aCompensation, ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation);
}
double
nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
{
THROW_IF_NO_CAMERACONTROL(0.0);
double compensation;
aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
return compensation;
}
int32_t
nsDOMCameraControl::SensorAngle()
{
int32_t angle = 0;
if (mCameraControl) {
mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle);
}
return angle;
}
already_AddRefed<dom::CameraCapabilities>
nsDOMCameraControl::Capabilities()
{
if (!mCameraControl) {
DOM_CAMERA_LOGW("mCameraControl is null at %s:%d\n", __func__, __LINE__);
return nullptr;
}
RefPtr<CameraCapabilities> caps = mCapabilities;
if (!caps) {
caps = new CameraCapabilities(mWindow, mCameraControl);
mCapabilities = caps;
}
return caps.forget();
}
// Methods.
already_AddRefed<Promise>
nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& aStorageArea,
const nsAString& aFilename,
ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
RefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
// If we are trying to start recording, already recording or are still
// waiting for a poster to be created/fail, we need to wait
if (mStartRecordingPromise || mRecording ||
mRecordingStoppedDeferred ||
mOptions.mCreatePoster) {
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
return promise.forget();
}
aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
if (aRv.Failed()) {
return nullptr;
}
mDSFileDescriptor = new DeviceStorageFileDescriptor();
RefPtr<DOMRequest> request = aStorageArea.CreateFileDescriptor(aFilename,
mDSFileDescriptor.get(),
aRv);
if (aRv.Failed()) {
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
return nullptr;
}
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
aRv = RegisterStorageRequestEvents(request, listener);
if (aRv.Failed()) {
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
return nullptr;
}
mStartRecordingPromise = promise;
mOptions = aOptions;
mRecording = true;
return promise.forget();
}
void
nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
{
nsresult rv = NS_ERROR_FAILURE;
if (!mCameraControl) {
rv = NS_ERROR_NOT_AVAILABLE;
} else if (!mRecording) {
// Race condition where StopRecording comes in before we issue
// the start recording request to Gonk
rv = NS_ERROR_ABORT;
mOptions.mCreatePoster = false;
} else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
ICameraControl::StartRecordingOptions o;
o.rotation = mOptions.mRotation;
o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
o.createPoster = mOptions.mCreatePoster;
rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
if (NS_SUCCEEDED(rv)) {
return;
}
}
OnUserError(CameraControlListener::kInStartRecording, rv);
if (mDSFileDescriptor->mFileDescriptor.IsValid()) {
// An error occured. We need to manually close the file associated with the
// FileDescriptor, and we shouldn't do this on the main thread, so we
// use a little helper.
RefPtr<CloseFileRunnable> closer =
new CloseFileRunnable(mDSFileDescriptor->mFileDescriptor);
closer->Dispatch();
}
}
void
nsDOMCameraControl::StopRecording(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
ReleaseAudioChannelAgent();
mRecording = false;
aRv = mCameraControl->StopRecording();
}
void
nsDOMCameraControl::PauseRecording(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->PauseRecording();
}
void
nsDOMCameraControl::ResumeRecording(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->ResumeRecording();
}
void
nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->StartPreview();
}
already_AddRefed<Promise>
nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL(nullptr);
RefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
if (mTakePicturePromise) {
// We're busy taking a picture, can't change modes right now.
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
return promise.forget();
}
ICameraControl::Configuration config;
aRv = SelectPreviewSize(aConfiguration.mPreviewSize, config.mPreviewSize);
if (aRv.Failed()) {
return nullptr;
}
config.mRecorderProfile = aConfiguration.mRecorderProfile;
config.mPictureSize.width = aConfiguration.mPictureSize.mWidth;
config.mPictureSize.height = aConfiguration.mPictureSize.mHeight;
config.mMode = ICameraControl::kPictureMode;
if (aConfiguration.mMode == CameraMode::Video) {
config.mMode = ICameraControl::kVideoMode;
}
aRv = mCameraControl->SetConfiguration(config);
if (aRv.Failed()) {
return nullptr;
}
mSetConfigurationPromise = promise;
return promise.forget();
}
already_AddRefed<Promise>
nsDOMCameraControl::AutoFocus(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL(nullptr);
RefPtr<Promise> promise = mAutoFocusPromise.forget();
if (promise) {
// There is already a call to AutoFocus() in progress, cancel it and
// invoke the error callback (if one was passed in).
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
}
promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
aRv = mCameraControl->AutoFocus();
if (aRv.Failed()) {
return nullptr;
}
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
mAutoFocusPromise = promise;
return promise.forget();
}
void
nsDOMCameraControl::StartFaceDetection(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->StartFaceDetection();
}
void
nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->StopFaceDetection();
}
already_AddRefed<Promise>
nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL(nullptr);
RefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
if (mTakePicturePromise) {
// There is already a call to TakePicture() in progress, abort this new
// one and invoke the error callback (if one was passed in).
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
return promise.forget();
}
{
ICameraControlParameterSetAutoEnter batch(mCameraControl);
// XXXmikeh - remove this: see bug 931155
ICameraControl::Size s;
s.width = aOptions.mPictureSize.mWidth;
s.height = aOptions.mPictureSize.mHeight;
ICameraControl::Position p;
p.latitude = aOptions.mPosition.mLatitude;
p.longitude = aOptions.mPosition.mLongitude;
p.altitude = aOptions.mPosition.mAltitude;
p.timestamp = aOptions.mPosition.mTimestamp;
if (s.width && s.height) {
mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
}
if (!aOptions.mFileFormat.IsEmpty()) {
mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat);
}
mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation);
mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime);
mCameraControl->SetLocation(p);
}
aRv = mCameraControl->TakePicture();
if (aRv.Failed()) {
return nullptr;
}
mTakePicturePromise = promise;
return promise.forget();
}
already_AddRefed<Promise>
nsDOMCameraControl::ReleaseHardware(ErrorResult& aRv)
{
DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
RefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!mCameraControl) {
// Always succeed if the camera instance is already closed.
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
aRv = mCameraControl->Stop();
if (aRv.Failed()) {
return nullptr;
}
// Once we stop the camera, there's nothing we can do with it,
// so we can throw away this reference. (This won't prevent us
// from receiving the last underlying events.)
mCameraControl = nullptr;
mReleasePromise = promise;
return promise.forget();
}
void
nsDOMCameraControl::ResumeContinuousFocus(ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
THROW_IF_NO_CAMERACONTROL();
aRv = mCameraControl->ResumeContinuousFocus();
}
void
nsDOMCameraControl::Shutdown()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
// Remove any pending solicited event handlers; these
// reference our window object, which in turn references
// us. If we don't remove them, we can leak DOM objects.
AbortPromise(mGetCameraPromise);
AbortPromise(mAutoFocusPromise);
AbortPromise(mTakePicturePromise);
AbortPromise(mStartRecordingPromise);
AbortPromise(mReleasePromise);
AbortPromise(mSetConfigurationPromise);
if (mCameraControl) {
mCameraControl->Stop();
mCameraControl = nullptr;
}
}
void
nsDOMCameraControl::ReleaseAudioChannelAgent()
{
#ifdef MOZ_B2G
if (mAudioChannelAgent) {
mAudioChannelAgent->NotifyStoppedPlaying();
mAudioChannelAgent = nullptr;
}
#endif
}
nsresult
nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
{
NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
if (aMsg.EqualsLiteral("shutdown")) {
ReleaseAudioChannelAgent();
}
nsresult rv = MediaManager::NotifyRecordingStatusChange(mWindow,
aMsg,
true /* aIsAudio */,
true /* aIsVideo */);
if (NS_FAILED(rv)) {
return rv;
}
#ifdef MOZ_B2G
if (aMsg.EqualsLiteral("starting") && !mAudioChannelAgent) {
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
if (!mAudioChannelAgent) {
return NS_ERROR_UNEXPECTED;
}
// Camera app will stop recording when it falls to the background, so no callback is necessary.
mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
// Video recording doesn't output any sound, so it's not necessary to check canPlay.
float volume = 0.0;
bool muted = true;
rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY,
&volume, &muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
#endif
return rv;
}
already_AddRefed<Promise>
nsDOMCameraControl::CreatePromise(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return Promise::Create(global, aRv);
}
void
nsDOMCameraControl::AbortPromise(RefPtr<Promise>& aPromise)
{
RefPtr<Promise> promise = aPromise.forget();
if (promise) {
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
}
}
void
nsDOMCameraControl::EventListenerAdded(nsIAtom* aType)
{
if (aType == nsGkAtoms::onpreviewstatechange) {
DispatchPreviewStateEvent(mPreviewState);
}
}
void
nsDOMCameraControl::DispatchPreviewStateEvent(CameraControlListener::PreviewState aState)
{
nsString state;
switch (aState) {
case CameraControlListener::kPreviewStarted:
state = NS_LITERAL_STRING("started");
break;
default:
state = NS_LITERAL_STRING("stopped");
break;
}
DispatchStateEvent(NS_LITERAL_STRING("previewstatechange"), state);
}
void
nsDOMCameraControl::DispatchStateEvent(const nsString& aType, const nsString& aState)
{
CameraStateChangeEventInit eventInit;
eventInit.mNewState = aState;
RefPtr<CameraStateChangeEvent> event =
CameraStateChangeEvent::Constructor(this, aType, eventInit);
DispatchTrustedEvent(event);
}
void
nsDOMCameraControl::OnGetCameraComplete()
{
// The hardware is open, so we can return a camera to JS, even if
// the preview hasn't started yet.
RefPtr<Promise> promise = mGetCameraPromise.forget();
if (promise) {
CameraGetPromiseData data;
data.mCamera = this;
data.mConfiguration = *mCurrentConfiguration;
promise->MaybeResolve(data);
}
}
// Camera Control event handlers--must only be called from the Main Thread!
void
nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState,
nsresult aReason)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
switch (aState) {
case CameraControlListener::kHardwareOpen:
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n");
MOZ_ASSERT(aReason == NS_OK);
if (!mSetInitialConfig) {
// The hardware is open, so we can return a camera to JS, even if
// the preview hasn't started yet.
OnGetCameraComplete();
}
break;
case CameraControlListener::kHardwareClosed:
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
if (!mSetInitialConfig) {
RefPtr<Promise> promise = mReleasePromise.forget();
if (promise) {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
CameraClosedEventInit eventInit;
switch (aReason) {
case NS_OK:
eventInit.mReason = NS_LITERAL_STRING("HardwareReleased");
break;
case NS_ERROR_FAILURE:
eventInit.mReason = NS_LITERAL_STRING("SystemFailure");
break;
case NS_ERROR_NOT_AVAILABLE:
eventInit.mReason = NS_LITERAL_STRING("NotAvailable");
break;
default:
DOM_CAMERA_LOGE("Unhandled hardware close reason, 0x%x\n", aReason);
MOZ_ASSERT_UNREACHABLE("Unanticipated reason for hardware close");
eventInit.mReason = NS_LITERAL_STRING("SystemFailure");
break;
}
RefPtr<CameraClosedEvent> event =
CameraClosedEvent::Constructor(this,
NS_LITERAL_STRING("close"),
eventInit);
DispatchTrustedEvent(event);
} else {
// The configuration failed and we forced the camera to shutdown.
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
}
break;
case CameraControlListener::kHardwareOpenFailed:
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open failed\n");
MOZ_ASSERT(aReason == NS_ERROR_NOT_AVAILABLE);
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
break;
case CameraControlListener::kHardwareUninitialized:
break;
default:
DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState);
MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state");
}
}
void
nsDOMCameraControl::OnShutter()
{
DOM_CAMERA_LOGI("DOM ** SNAP **\n");
MOZ_ASSERT(NS_IsMainThread());
DispatchTrustedEvent(NS_LITERAL_STRING("shutter"));
}
void
nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
mPreviewState = aState;
nsString state;
switch (aState) {
case CameraControlListener::kPreviewStarted:
state = NS_LITERAL_STRING("started");
break;
default:
state = NS_LITERAL_STRING("stopped");
break;
}
DispatchPreviewStateEvent(aState);
}
void
nsDOMCameraControl::OnPoster(BlobImpl* aPoster)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mOptions.mCreatePoster);
RefPtr<Blob> blob = Blob::Create(GetParentObject(), aPoster);
if (NS_WARN_IF(!blob)) {
OnRecorderStateChange(CameraControlListener::kPosterFailed, 0, 0);
return;
}
BlobEventInit eventInit;
eventInit.mData = blob;
RefPtr<BlobEvent> event = BlobEvent::Constructor(this,
NS_LITERAL_STRING("poster"),
eventInit);
DispatchTrustedEvent(event);
OnRecorderStateChange(CameraControlListener::kPosterCreated, 0, 0);
}
void
nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
int32_t aArg, int32_t aTrackNum)
{
// For now, we do nothing with 'aStatus' and 'aTrackNum'.
DOM_CAMERA_LOGT("%s:%d : this=%p, state=%u\n", __func__, __LINE__, this, aState);
MOZ_ASSERT(NS_IsMainThread());
nsString state;
switch (aState) {
case CameraControlListener::kRecorderStarted:
{
RefPtr<Promise> promise = mStartRecordingPromise.forget();
if (promise) {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
state = NS_LITERAL_STRING("Started");
}
break;
case CameraControlListener::kRecorderStopped:
if (mOptions.mCreatePoster) {
mRecordingStoppedDeferred = true;
return;
}
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
state = NS_LITERAL_STRING("Stopped");
break;
case CameraControlListener::kPosterCreated:
state = NS_LITERAL_STRING("PosterCreated");
mOptions.mCreatePoster = false;
break;
case CameraControlListener::kPosterFailed:
state = NS_LITERAL_STRING("PosterFailed");
mOptions.mCreatePoster = false;
break;
case CameraControlListener::kRecorderPaused:
state = NS_LITERAL_STRING("Paused");
break;
case CameraControlListener::kRecorderResumed:
state = NS_LITERAL_STRING("Resumed");
break;
#ifdef MOZ_B2G_CAMERA
case CameraControlListener::kFileSizeLimitReached:
state = NS_LITERAL_STRING("FileSizeLimitReached");
break;
case CameraControlListener::kVideoLengthLimitReached:
state = NS_LITERAL_STRING("VideoLengthLimitReached");
break;
case CameraControlListener::kTrackCompleted:
state = NS_LITERAL_STRING("TrackCompleted");
break;
case CameraControlListener::kTrackFailed:
state = NS_LITERAL_STRING("TrackFailed");
break;
case CameraControlListener::kMediaRecorderFailed:
state = NS_LITERAL_STRING("MediaRecorderFailed");
break;
case CameraControlListener::kMediaServerFailed:
state = NS_LITERAL_STRING("MediaServerFailed");
break;
#endif
default:
MOZ_ASSERT_UNREACHABLE("Unanticipated video recorder error");
return;
}
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
if (mRecordingStoppedDeferred && !mOptions.mCreatePoster) {
mRecordingStoppedDeferred = false;
OnRecorderStateChange(CameraControlListener::kRecorderStopped, 0, 0);
}
}
void
nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aConfiguration != nullptr);
// Update our record of the current camera configuration
mCurrentConfiguration = aConfiguration;
DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this);
DOM_CAMERA_LOGI(" mode : %s\n",
mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture");
DOM_CAMERA_LOGI(" maximum focus areas : %d\n",
mCurrentConfiguration->mMaxFocusAreas);
DOM_CAMERA_LOGI(" maximum metering areas : %d\n",
mCurrentConfiguration->mMaxMeteringAreas);
DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n",
mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
DOM_CAMERA_LOGI(" picture size (w x h) : %d x %d\n",
mCurrentConfiguration->mPictureSize.mWidth, mCurrentConfiguration->mPictureSize.mHeight);
DOM_CAMERA_LOGI(" recorder profile : %s\n",
NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
if (mSetInitialConfig) {
OnGetCameraComplete();
mSetInitialConfig = false;
return;
}
RefPtr<Promise> promise = mSetConfigurationPromise.forget();
if (promise) {
promise->MaybeResolve(*aConfiguration);
}
CameraConfigurationEventInit eventInit;
eventInit.mMode = mCurrentConfiguration->mMode;
eventInit.mRecorderProfile = mCurrentConfiguration->mRecorderProfile;
eventInit.mPreviewSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
mCurrentConfiguration->mPreviewSize.mWidth,
mCurrentConfiguration->mPreviewSize.mHeight);
eventInit.mPictureSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
mCurrentConfiguration->mPictureSize.mWidth,
mCurrentConfiguration->mPictureSize.mHeight);
RefPtr<CameraConfigurationEvent> event =
CameraConfigurationEvent::Constructor(this,
NS_LITERAL_STRING("configurationchanged"),
eventInit);
DispatchTrustedEvent(event);
}
void
nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Promise> promise = mAutoFocusPromise.forget();
if (promise) {
promise->MaybeResolve(aAutoFocusSucceeded);
}
if (aAutoFocusSucceeded) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focused"));
} else {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("unfocused"));
}
}
void
nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
if (aIsMoving) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
}
}
void
nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
{
DOM_CAMERA_LOGI("DOM OnFacesDetected %zu face(s)\n", aFaces.Length());
MOZ_ASSERT(NS_IsMainThread());
Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;
uint32_t len = aFaces.Length();
if (faces.SetCapacity(len, fallible)) {
for (uint32_t i = 0; i < len; ++i) {
*faces.AppendElement(fallible) =
new DOMCameraDetectedFace(static_cast<DOMMediaStream*>(this), aFaces[i]);
}
}
CameraFacesDetectedEventInit eventInit;
eventInit.mFaces.SetValue(faces);
RefPtr<CameraFacesDetectedEvent> event =
CameraFacesDetectedEvent::Constructor(this,
NS_LITERAL_STRING("facesdetected"),
eventInit);
DispatchTrustedEvent(event);
}
void
nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPicture);
RefPtr<Promise> promise = mTakePicturePromise.forget();
if (promise) {
nsCOMPtr<nsIDOMBlob> picture = aPicture;
promise->MaybeResolve(picture);
}
RefPtr<Blob> blob = static_cast<Blob*>(aPicture);
BlobEventInit eventInit;
eventInit.mData = blob;
RefPtr<BlobEvent> event = BlobEvent::Constructor(this,
NS_LITERAL_STRING("picture"),
eventInit);
DispatchTrustedEvent(event);
}
void
nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsresult aError)
{
DOM_CAMERA_LOGI("DOM OnUserError : this=%p, aContext=%u, aError=0x%x\n",
this, aContext, aError);
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Promise> promise;
switch (aContext) {
case CameraControlListener::kInStartCamera:
promise = mGetCameraPromise.forget();
// If we failed to open the camera, we never actually provided a reference
// for the application to release explicitly. Thus we must clear our handle
// here to ensure everything is freed.
mCameraControl = nullptr;
break;
case CameraControlListener::kInStopCamera:
promise = mReleasePromise.forget();
if (aError == NS_ERROR_NOT_INITIALIZED) {
// This value indicates that the hardware is already closed; which for
// kInStopCamera, is not actually an error.
if (promise) {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
return;
}
break;
case CameraControlListener::kInSetConfiguration:
if (mSetInitialConfig) {
// If the SetConfiguration() call in the constructor fails, there
// is nothing we can do except release the camera hardware. This
// will trigger a hardware state change, and when the flag that
// got us here is set in that handler, we replace the normal reason
// code with one that indicates the hardware isn't available.
DOM_CAMERA_LOGI("Failed to configure cached camera, stopping\n");
mCameraControl->Stop();
return;
}
promise = mSetConfigurationPromise.forget();
break;
case CameraControlListener::kInAutoFocus:
promise = mAutoFocusPromise.forget();
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("unfocused"));
break;
case CameraControlListener::kInTakePicture:
promise = mTakePicturePromise.forget();
break;
case CameraControlListener::kInStartRecording:
promise = mStartRecordingPromise.forget();
mRecording = false;
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
break;
case CameraControlListener::kInStartFaceDetection:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to start face detection");
return;
case CameraControlListener::kInStopFaceDetection:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to stop face detection");
return;
case CameraControlListener::kInResumeContinuousFocus:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to resume continuous focus");
return;
case CameraControlListener::kInStopRecording:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to stop recording");
return;
case CameraControlListener::kInPauseRecording:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to pause recording");
return;
case CameraControlListener::kInResumeRecording:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to resume recording");
return;
case CameraControlListener::kInStartPreview:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to (re)start preview");
return;
case CameraControlListener::kInStopPreview:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to stop preview");
return;
case CameraControlListener::kInSetPictureSize:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to set picture size");
return;
case CameraControlListener::kInSetThumbnailSize:
// This method doesn't have any callbacks, so all we can do is log the
// failure. This only happens after the hardware has been released.
NS_WARNING("Failed to set thumbnail size");
return;
default:
{
nsPrintfCString msg("Unhandled aContext=%u, aError=0x%x\n", aContext, aError);
NS_WARNING(msg.get());
}
MOZ_ASSERT_UNREACHABLE("Unhandled user error");
return;
}
if (!promise) {
DOM_CAMERA_LOGW("DOM No error handler for aError=0x%x in aContext=%u\n",
aError, aContext);
return;
}
promise->MaybeReject(aError);
}