Bug 994912 - Add support for promises and event-based notifications to camera. r=mikeh r=bz

This commit is contained in:
Andrew Osmond 2014-09-24 12:51:00 +02:00
parent f60ff037fe
commit 942c9765a8
39 changed files with 2349 additions and 332 deletions

View File

@ -699,6 +699,7 @@ GK_ATOM(oncomplete, "oncomplete")
GK_ATOM(oncompositionend, "oncompositionend")
GK_ATOM(oncompositionstart, "oncompositionstart")
GK_ATOM(oncompositionupdate, "oncompositionupdate")
GK_ATOM(onconfigurationchange, "onconfigurationchange")
GK_ATOM(onconnect, "onconnect")
GK_ATOM(onconnected, "onconnected")
GK_ATOM(onconnecting, "onconnecting")
@ -748,6 +749,7 @@ GK_ATOM(onenterpincodereq, "onenterpincodereq")
GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
GK_ATOM(onerror, "onerror")
GK_ATOM(onevicted, "onevicted")
GK_ATOM(onfacesdetected, "onfacesdetected")
GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfetch, "onfetch")
GK_ATOM(onfocus, "onfocus")
@ -812,16 +814,19 @@ GK_ATOM(onpairingconfirmationreq, "onpairingconfirmationreq")
GK_ATOM(onpairingconsentreq, "onpairingconsentreq")
GK_ATOM(onpaste, "onpaste")
GK_ATOM(onpendingchange, "onpendingchange")
GK_ATOM(onpicture, "onpicture")
GK_ATOM(onpopuphidden, "onpopuphidden")
GK_ATOM(onpopuphiding, "onpopuphiding")
GK_ATOM(onpopupshowing, "onpopupshowing")
GK_ATOM(onpopupshown, "onpopupshown")
GK_ATOM(onpreviewstatechange, "onpreviewstatechange")
GK_ATOM(onradiostatechange, "onradiostatechange")
GK_ATOM(onreaderror, "onreaderror")
GK_ATOM(onreadsuccess, "onreadsuccess")
GK_ATOM(onready, "onready")
GK_ATOM(onreadystatechange, "onreadystatechange")
GK_ATOM(onreceived, "onreceived")
GK_ATOM(onrecorderstatechange, "onrecorderstatechange")
GK_ATOM(onremoteheld, "onremoteheld")
GK_ATOM(onremoteresumed, "onremoteresumed")
GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull")
@ -838,6 +843,7 @@ GK_ATOM(onsending, "onsending")
GK_ATOM(onsent, "onsent")
GK_ATOM(onset, "onset")
GK_ATOM(onshow, "onshow")
GK_ATOM(onshutter, "onshutter")
GK_ATOM(onstatechange, "onstatechange")
GK_ATOM(onstatuschanged, "onstatuschanged")
GK_ATOM(onstkcommand, "onstkcommand")

View File

@ -7,10 +7,9 @@
#include "DirectShowReader.h"
#include "MediaDecoderReader.h"
#include "mozilla/RefPtr.h"
#include "dshow.h"
#include "DirectShowUtils.h"
#include "AudioSinkFilter.h"
#include "SourceFilter.h"
#include "DirectShowUtils.h"
#include "SampleSink.h"
#include "MediaResource.h"
#include "VideoUtils.h"

View File

@ -3,11 +3,10 @@
* 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 "dshow.h"
#include "DirectShowUtils.h"
#include "dmodshow.h"
#include "wmcodecdsp.h"
#include "dmoreg.h"
#include "DirectShowUtils.h"
#include "nsAutoPtr.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/RefPtr.h"

View File

@ -8,6 +8,14 @@
#include <stdint.h>
#include "dshow.h"
// XXXbz windowsx.h defines GetFirstChild, GetNextSibling,
// GetPrevSibling are macros, apparently... Eeevil. We have functions
// called that on some classes, so undef them.
#undef GetFirstChild
#undef GetNextSibling
#undef GetPrevSibling
#include "DShowTools.h"
#include "prlog.h"

View File

@ -20,6 +20,7 @@
#include "nsIDOMDeviceStorage.h"
#include "nsIDOMEventListener.h"
#include "nsIScriptSecurityManager.h"
#include "nsDOMFile.h"
#include "Navigator.h"
#include "nsXULAppAPI.h"
#include "DOMCameraManager.h"
@ -27,9 +28,16 @@
#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/BlobEvent.h"
#include "DOMCameraDetectedFace.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsPrintfCString.h"
@ -40,7 +48,6 @@ using namespace mozilla::ipc;
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream)
@ -49,6 +56,12 @@ NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream)
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream,
mCapabilities,
mWindow,
mGetCameraPromise,
mAutoFocusPromise,
mTakePicturePromise,
mStartRecordingPromise,
mReleasePromise,
mSetConfigurationPromise,
mGetCameraOnSuccessCb,
mGetCameraOnErrorCb,
mAutoFocusOnSuccessCb,
@ -134,10 +147,12 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError,
Promise* aPromise,
nsPIDOMWindow* aWindow)
: DOMMediaStream()
, mCameraControl(nullptr)
, mAudioChannelAgent(nullptr)
, mGetCameraPromise(aPromise)
, mGetCameraOnSuccessCb(aOnSuccess)
, mGetCameraOnErrorCb(aOnError)
, mAutoFocusOnSuccessCb(nullptr)
@ -157,10 +172,12 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
, mOnAutoFocusMovingCb(nullptr)
, mOnFacesDetectedCb(nullptr)
, mWindow(aWindow)
, mPreviewState(CameraControlListener::kPreviewStopped)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mInput = new CameraPreviewMediaStream(this);
BindToOwner(aWindow);
SetIsDOMBinding();
nsRefPtr<DOMCameraConfiguration> initialConfig =
@ -670,28 +687,30 @@ protected:
};
// Methods.
void
already_AddRefed<Promise>
nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& aStorageArea,
const nsAString& aFilename,
CameraStartRecordingCallback& aOnSuccess,
const Optional<OwningNonNull<CameraStartRecordingCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
nsRefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
nsRefPtr<CameraStartRecordingCallback> cb = mStartRecordingOnSuccessCb;
if (cb) {
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
if (aOnError.WasPassed()) {
DOM_CAMERA_LOGT("%s:onError WasPassed\n", __func__);
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("StartRecordingInProgress")));
} else {
DOM_CAMERA_LOGT("%s:onError NS_ERROR_FAILURE\n", __func__);
// Only throw if no error callback was passed in.
aRv = NS_ERROR_FAILURE;
}
return;
return promise.forget();
}
NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
@ -714,11 +733,15 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(),
getter_AddRefs(request));
if (aRv.Failed()) {
return;
return nullptr;
}
mStartRecordingPromise = promise;
mOptions = aOptions;
mStartRecordingOnSuccessCb = &aOnSuccess;
mStartRecordingOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mStartRecordingOnSuccessCb = &aOnSuccess.Value();
}
mStartRecordingOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mStartRecordingOnErrorCb = &aOnError.Value();
@ -727,6 +750,7 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
request->AddEventListener(NS_LITERAL_STRING("success"), listener, false);
request->AddEventListener(NS_LITERAL_STRING("error"), listener, false);
return promise.forget();
}
void
@ -781,7 +805,7 @@ nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
aRv = mCameraControl->StartPreview();
}
void
already_AddRefed<Promise>
nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
const Optional<OwningNonNull<CameraSetConfigurationCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
@ -789,19 +813,21 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
{
MOZ_ASSERT(mCameraControl);
nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
if (cb) {
nsRefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
if (mTakePicturePromise) {
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
// We're busy taking a picture, can't change modes right now.
if (aOnError.WasPassed()) {
// There is already a call to TakePicture() in progress, abort this
// call and invoke the error callback (if one was passed in).
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("TakePictureInProgress")));
} else {
// Only throw if no error callback was passed in.
aRv = NS_ERROR_FAILURE;
}
return;
return promise.forget();
}
ICameraControl::Configuration config;
@ -813,6 +839,12 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
config.mMode = ICameraControl::kVideoMode;
}
aRv = mCameraControl->SetConfiguration(config);
if (aRv.Failed()) {
return nullptr;
}
mSetConfigurationPromise = promise;
mSetConfigurationOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mSetConfigurationOnSuccessCb = &aOnSuccess.Value();
@ -821,32 +853,51 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
if (aOnError.WasPassed()) {
mSetConfigurationOnErrorCb = &aOnError.Value();
}
aRv = mCameraControl->SetConfiguration(config);
return promise.forget();
}
void
nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess,
already_AddRefed<Promise>
nsDOMCameraControl::AutoFocus(const Optional<OwningNonNull<CameraAutoFocusCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
nsRefPtr<CameraErrorCallback> ecb = mAutoFocusOnErrorCb.forget();
if (ecb) {
nsRefPtr<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).
NS_DispatchToMainThread(new ImmediateErrorCallback(ecb,
NS_LITERAL_STRING("AutoFocusInterrupted")));
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
mAutoFocusOnSuccessCb = nullptr;
nsRefPtr<CameraErrorCallback> ecb = mAutoFocusOnErrorCb.forget();
if (ecb) {
NS_DispatchToMainThread(new ImmediateErrorCallback(ecb,
NS_LITERAL_STRING("AutoFocusInterrupted")));
}
}
mAutoFocusOnSuccessCb = &aOnSuccess;
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;
mAutoFocusOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mAutoFocusOnSuccessCb = &aOnSuccess.Value();
}
mAutoFocusOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mAutoFocusOnErrorCb = &aOnError.Value();
}
aRv = mCameraControl->AutoFocus();
return promise.forget();
}
void
@ -863,26 +914,28 @@ nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv)
aRv = mCameraControl->StopFaceDetection();
}
void
already_AddRefed<Promise>
nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
CameraTakePictureCallback& aOnSuccess,
const Optional<OwningNonNull<CameraTakePictureCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
if (cb) {
nsRefPtr<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);
if (aOnError.WasPassed()) {
// There is already a call to TakePicture() in progress, abort this new
// one and invoke the error callback (if one was passed in).
NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
NS_LITERAL_STRING("TakePictureAlreadyInProgress")));
} else {
// Only throw if no error callback was passed in.
aRv = NS_ERROR_FAILURE;
}
return;
return promise.forget();
}
{
@ -908,22 +961,41 @@ nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
mCameraControl->SetLocation(p);
}
mTakePictureOnSuccessCb = &aOnSuccess;
aRv = mCameraControl->TakePicture();
if (aRv.Failed()) {
return nullptr;
}
mTakePicturePromise = promise;
mTakePictureOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mTakePictureOnSuccessCb = &aOnSuccess.Value();
}
mTakePictureOnErrorCb = nullptr;
if (aOnError.WasPassed()) {
mTakePictureOnErrorCb = &aOnError.Value();
}
aRv = mCameraControl->TakePicture();
return promise.forget();
}
void
already_AddRefed<Promise>
nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCallback> >& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
nsRefPtr<Promise> promise = CreatePromise(aRv);
if (aRv.Failed()) {
return nullptr;
}
aRv = mCameraControl->Stop();
if (aRv.Failed()) {
return nullptr;
}
mReleasePromise = promise;
mReleaseOnSuccessCb = nullptr;
if (aOnSuccess.WasPassed()) {
mReleaseOnSuccessCb = &aOnSuccess.Value();
@ -932,8 +1004,7 @@ nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCa
if (aOnError.WasPassed()) {
mReleaseOnErrorCb = &aOnError.Value();
}
aRv = mCameraControl->Stop();
return promise.forget();
}
void
@ -952,6 +1023,12 @@ nsDOMCameraControl::Shutdown()
// 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);
mGetCameraOnSuccessCb = nullptr;
mGetCameraOnErrorCb = nullptr;
mAutoFocusOnSuccessCb = nullptr;
@ -987,6 +1064,64 @@ nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
true /* aIsVideo */);
}
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(nsRefPtr<Promise>& aPromise)
{
nsRefPtr<Promise> promise = aPromise.forget();
if (promise) {
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
promise = nullptr;
}
}
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;
nsRefPtr<CameraStateChangeEvent> event =
CameraStateChangeEvent::Constructor(this, aType, eventInit);
DispatchTrustedEvent(event);
}
// Camera Control event handlers--must only be called from the Main Thread!
void
nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState)
@ -998,26 +1133,46 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
switch (aState) {
case CameraControlListener::kHardwareOpen:
// The hardware is open, so we can return a camera to JS, even if
// the preview hasn't started yet.
if (mGetCameraOnSuccessCb) {
{
// The hardware is open, so we can return a camera to JS, even if
// the preview hasn't started yet.
nsRefPtr<Promise> promise = mGetCameraPromise.forget();
if (promise) {
CameraGetPromiseData data;
data.mCamera = this;
data.mConfiguration = *mCurrentConfiguration;
promise->MaybeResolve(data);
}
nsRefPtr<GetCameraCallback> cb = mGetCameraOnSuccessCb.forget();
ErrorResult ignored;
mGetCameraOnErrorCb = nullptr;
cb->Call(*this, *mCurrentConfiguration, ignored);
if (cb) {
ErrorResult ignored;
cb->Call(*this, *mCurrentConfiguration, ignored);
}
}
break;
case CameraControlListener::kHardwareClosed:
if (mReleaseOnSuccessCb) {
// If we have this event handler, this was a solicited hardware close.
nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
mReleaseOnErrorCb = nullptr;
cb->Call(ignored);
} else if(mOnClosedCb) {
// If not, something else closed the hardware.
nsRefPtr<CameraClosedCallback> cb = mOnClosedCb;
cb->Call(ignored);
{
nsRefPtr<Promise> promise = mReleasePromise.forget();
if (promise || mReleaseOnSuccessCb) {
// If we have this event handler, this was a solicited hardware close.
if (promise) {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
mReleaseOnErrorCb = nullptr;
if (cb) {
cb->Call(ignored);
}
} else {
// If not, something else closed the hardware.
nsRefPtr<CameraClosedCallback> cb = mOnClosedCb;
if (cb) {
cb->Call(ignored);
}
}
DispatchTrustedEvent(NS_LITERAL_STRING("close"));
}
break;
@ -1038,6 +1193,8 @@ nsDOMCameraControl::OnShutter()
ErrorResult ignored;
cb->Call(ignored);
}
DispatchTrustedEvent(NS_LITERAL_STRING("shutter"));
}
void
@ -1045,10 +1202,7 @@ nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aSt
{
MOZ_ASSERT(NS_IsMainThread());
if (!mOnPreviewStateChangeCb) {
return;
}
mPreviewState = aState;
nsString state;
switch (aState) {
case CameraControlListener::kPreviewStarted:
@ -1061,8 +1215,12 @@ nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aSt
}
nsRefPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
ErrorResult ignored;
cb->Call(state, ignored);
if (cb) {
ErrorResult ignored;
cb->Call(state, ignored);
}
DispatchPreviewStateEvent(aState);
}
void
@ -1077,12 +1235,19 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
switch (aState) {
case CameraControlListener::kRecorderStarted:
if (mStartRecordingOnSuccessCb) {
nsRefPtr<CameraStartRecordingCallback> cb = mStartRecordingOnSuccessCb.forget();
{
nsRefPtr<Promise> promise = mStartRecordingPromise.forget();
if (promise) {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
nsRefPtr<CameraStartRecordingCallback> scb = mStartRecordingOnSuccessCb.forget();
mStartRecordingOnErrorCb = nullptr;
cb->Call(ignored);
if (scb) {
scb->Call(ignored);
}
state = NS_LITERAL_STRING("Started");
}
state = NS_LITERAL_STRING("Started");
break;
case CameraControlListener::kRecorderStopped:
@ -1125,12 +1290,15 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
if (cb) {
cb->Call(state, ignored);
}
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
}
void
nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aConfiguration != nullptr);
// Update our record of the current camera configuration
mCurrentConfiguration = aConfiguration;
@ -1147,12 +1315,31 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
DOM_CAMERA_LOGI(" recorder profile : %s\n",
NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
nsRefPtr<Promise> promise = mSetConfigurationPromise.forget();
if (promise) {
promise->MaybeResolve(*aConfiguration);
}
nsRefPtr<CameraSetConfigurationCallback> cb = mSetConfigurationOnSuccessCb.forget();
mSetConfigurationOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(*mCurrentConfiguration, ignored);
}
CameraConfigurationEventInit eventInit;
eventInit.mMode = mCurrentConfiguration->mMode;
eventInit.mRecorderProfile = mCurrentConfiguration->mRecorderProfile;
eventInit.mPreviewSize = new DOMRect(this, 0, 0,
mCurrentConfiguration->mPreviewSize.mWidth,
mCurrentConfiguration->mPreviewSize.mHeight);
nsRefPtr<CameraConfigurationEvent> event =
CameraConfigurationEvent::Constructor(this,
NS_LITERAL_STRING("configurationchanged"),
eventInit);
DispatchTrustedEvent(event);
}
void
@ -1160,12 +1347,23 @@ nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<Promise> promise = mAutoFocusPromise.forget();
if (promise) {
promise->MaybeResolve(aAutoFocusSucceeded);
}
nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
mAutoFocusOnErrorCb = nullptr;
if (cb) {
ErrorResult ignored;
cb->Call(aAutoFocusSucceeded, ignored);
}
if (aAutoFocusSucceeded) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focused"));
} else {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("unfocused"));
}
}
void
@ -1178,6 +1376,10 @@ nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
ErrorResult ignored;
cb->Call(aIsMoving, ignored);
}
if (aIsMoving) {
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
}
}
void
@ -1186,11 +1388,6 @@ nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces
DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length());
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<CameraFaceDetectionCallback> cb = mOnFacesDetectedCb;
if (!cb) {
return;
}
Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;
uint32_t len = aFaces.Length();
@ -1202,27 +1399,50 @@ nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces
}
}
ErrorResult ignored;
cb->Call(faces, ignored);
nsRefPtr<CameraFaceDetectionCallback> cb = mOnFacesDetectedCb;
if (cb) {
ErrorResult ignored;
cb->Call(faces, ignored);
}
CameraFacesDetectedEventInit eventInit;
eventInit.mFaces.SetValue(faces);
nsRefPtr<CameraFacesDetectedEvent> event =
CameraFacesDetectedEvent::Constructor(this,
NS_LITERAL_STRING("facesdetected"),
eventInit);
DispatchTrustedEvent(event);
}
void
nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPicture != nullptr);
nsRefPtr<Promise> promise = mTakePicturePromise.forget();
if (promise) {
nsCOMPtr<nsIDOMBlob> picture = aPicture;
promise->MaybeResolve(picture);
}
nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
mTakePictureOnErrorCb = nullptr;
if (!cb) {
// Warn because it shouldn't be possible to get here without
// having passed a success callback into takePicture(), even
// though we guard against a nullptr dereference.
NS_WARNING("DOM Null success callback in OnTakePictureComplete()");
return;
if (cb) {
ErrorResult ignored;
cb->Call(aPicture, ignored);
}
ErrorResult ignored;
cb->Call(aPicture, ignored);
BlobEventInit eventInit;
eventInit.mData = aPicture;
nsRefPtr<BlobEvent> event = BlobEvent::Constructor(this,
NS_LITERAL_STRING("picture"),
eventInit);
DispatchTrustedEvent(event);
}
void
@ -1230,35 +1450,43 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<Promise> promise;
nsRefPtr<CameraErrorCallback> errorCb;
switch (aContext) {
case CameraControlListener::kInStartCamera:
promise = mGetCameraPromise.forget();
mGetCameraOnSuccessCb = nullptr;
errorCb = mGetCameraOnErrorCb.forget();
break;
case CameraControlListener::kInStopCamera:
promise = mReleasePromise.forget();
mReleaseOnSuccessCb = nullptr;
errorCb = mReleaseOnErrorCb.forget();
break;
case CameraControlListener::kInSetConfiguration:
promise = mSetConfigurationPromise.forget();
mSetConfigurationOnSuccessCb = nullptr;
errorCb = mSetConfigurationOnErrorCb.forget();
break;
case CameraControlListener::kInAutoFocus:
promise = mAutoFocusPromise.forget();
mAutoFocusOnSuccessCb = nullptr;
errorCb = mAutoFocusOnErrorCb.forget();
DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("unfocused"));
break;
case CameraControlListener::kInTakePicture:
promise = mTakePicturePromise.forget();
mTakePictureOnSuccessCb = nullptr;
errorCb = mTakePictureOnErrorCb.forget();
break;
case CameraControlListener::kInStartRecording:
promise = mStartRecordingPromise.forget();
mStartRecordingOnSuccessCb = nullptr;
errorCb = mStartRecordingOnErrorCb.forget();
break;
@ -1320,52 +1548,57 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
return;
}
if (!errorCb) {
if (!promise && !errorCb) {
DOM_CAMERA_LOGW("DOM No error handler for aError=0x%x in aContext=%u\n",
aError, aContext);
return;
}
nsString error;
switch (aError) {
case NS_ERROR_INVALID_ARG:
error = NS_LITERAL_STRING("InvalidArgument");
break;
case NS_ERROR_NOT_AVAILABLE:
error = NS_LITERAL_STRING("NotAvailable");
break;
case NS_ERROR_NOT_IMPLEMENTED:
error = NS_LITERAL_STRING("NotImplemented");
break;
case NS_ERROR_NOT_INITIALIZED:
error = NS_LITERAL_STRING("HardwareClosed");
break;
case NS_ERROR_ALREADY_INITIALIZED:
error = NS_LITERAL_STRING("HardwareAlreadyOpen");
break;
case NS_ERROR_OUT_OF_MEMORY:
error = NS_LITERAL_STRING("OutOfMemory");
break;
default:
{
nsPrintfCString msg("Reporting aError=0x%x as generic\n", aError);
NS_WARNING(msg.get());
}
// fallthrough
case NS_ERROR_FAILURE:
error = NS_LITERAL_STRING("GeneralFailure");
break;
DOM_CAMERA_LOGI("DOM OnUserError aContext=%u, aError=0x%x\n", aContext, aError);
if (promise) {
promise->MaybeReject(aError);
}
DOM_CAMERA_LOGI("DOM OnUserError aContext=%u, error='%s'\n", aContext,
NS_ConvertUTF16toUTF8(error).get());
ErrorResult ignored;
errorCb->Call(error, ignored);
if (errorCb) {
nsString error;
switch (aError) {
case NS_ERROR_INVALID_ARG:
error = NS_LITERAL_STRING("InvalidArgument");
break;
case NS_ERROR_NOT_AVAILABLE:
error = NS_LITERAL_STRING("NotAvailable");
break;
case NS_ERROR_NOT_IMPLEMENTED:
error = NS_LITERAL_STRING("NotImplemented");
break;
case NS_ERROR_NOT_INITIALIZED:
error = NS_LITERAL_STRING("HardwareClosed");
break;
case NS_ERROR_ALREADY_INITIALIZED:
error = NS_LITERAL_STRING("HardwareAlreadyOpen");
break;
case NS_ERROR_OUT_OF_MEMORY:
error = NS_LITERAL_STRING("OutOfMemory");
break;
default:
{
nsPrintfCString msg("Reporting aError=0x%x as generic\n", aError);
NS_WARNING(msg.get());
}
// fallthrough
case NS_ERROR_FAILURE:
error = NS_LITERAL_STRING("GeneralFailure");
break;
}
ErrorResult ignored;
errorCb->Call(error, ignored);
}
}

View File

@ -9,6 +9,7 @@
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/dom/CameraControlBinding.h"
#include "mozilla/dom/Promise.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
#include "DOMMediaStream.h"
@ -53,6 +54,7 @@ public:
const dom::CameraConfiguration& aInitialConfig,
dom::GetCameraCallback* aOnSuccess,
dom::CameraErrorCallback* aOnError,
dom::Promise* aPromise,
nsPIDOMWindow* aWindow);
void Shutdown();
@ -100,10 +102,10 @@ public:
void SetOnFacesDetected(dom::CameraFaceDetectionCallback* aCb);
// Methods.
void SetConfiguration(const dom::CameraConfiguration& aConfiguration,
const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> SetConfiguration(const dom::CameraConfiguration& aConfiguration,
const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void GetMeteringAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
void SetMeteringAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
void GetFocusAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
@ -112,30 +114,39 @@ public:
void SetPictureSize(const dom::CameraSize& aSize, ErrorResult& aRv);
void GetThumbnailSize(dom::CameraSize& aSize, ErrorResult& aRv);
void SetThumbnailSize(const dom::CameraSize& aSize, ErrorResult& aRv);
void AutoFocus(dom::CameraAutoFocusCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> AutoFocus(const dom::Optional<dom::OwningNonNull<dom::CameraAutoFocusCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void StartFaceDetection(ErrorResult& aRv);
void StopFaceDetection(ErrorResult& aRv);
void TakePicture(const dom::CameraPictureOptions& aOptions,
dom::CameraTakePictureCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void StartRecording(const dom::CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& storageArea,
const nsAString& filename,
dom::CameraStartRecordingCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> TakePicture(const dom::CameraPictureOptions& aOptions,
const dom::Optional<dom::OwningNonNull<dom::CameraTakePictureCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> StartRecording(const dom::CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& storageArea,
const nsAString& filename,
const dom::Optional<dom::OwningNonNull<dom::CameraStartRecordingCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void StopRecording(ErrorResult& aRv);
void ResumePreview(ErrorResult& aRv);
void ReleaseHardware(const dom::Optional<dom::OwningNonNull<dom::CameraReleaseCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<dom::Promise> ReleaseHardware(const dom::Optional<dom::OwningNonNull<dom::CameraReleaseCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void ResumeContinuousFocus(ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
IMPL_EVENT_HANDLER(facesdetected)
IMPL_EVENT_HANDLER(shutter)
IMPL_EVENT_HANDLER(close)
IMPL_EVENT_HANDLER(recorderstatechange)
IMPL_EVENT_HANDLER(previewstatechange)
IMPL_EVENT_HANDLER(focus)
IMPL_EVENT_HANDLER(picture)
IMPL_EVENT_HANDLER(configurationchange)
protected:
virtual ~nsDOMCameraControl();
@ -177,6 +188,12 @@ protected:
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
already_AddRefed<dom::Promise> CreatePromise(ErrorResult& aRv);
void AbortPromise(nsRefPtr<dom::Promise>& aPromise);
virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE;
void DispatchPreviewStateEvent(DOMCameraControlListener::PreviewState aState);
void DispatchStateEvent(const nsString& aType, const nsString& aState);
nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control
// An agent used to join audio channel service.
@ -188,6 +205,14 @@ protected:
nsRefPtr<DOMCameraConfiguration> mCurrentConfiguration;
nsRefPtr<dom::CameraCapabilities> mCapabilities;
// camera control pending promises
nsRefPtr<dom::Promise> mGetCameraPromise;
nsRefPtr<dom::Promise> mAutoFocusPromise;
nsRefPtr<dom::Promise> mTakePicturePromise;
nsRefPtr<dom::Promise> mStartRecordingPromise;
nsRefPtr<dom::Promise> mReleasePromise;
nsRefPtr<dom::Promise> mSetConfigurationPromise;
// solicited camera control event handlers
nsRefPtr<dom::GetCameraCallback> mGetCameraOnSuccessCb;
nsRefPtr<dom::CameraErrorCallback> mGetCameraOnErrorCb;
@ -223,6 +248,7 @@ protected:
dom::CameraStartRecordingOptions mOptions;
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
DOMCameraControlListener::PreviewState mPreviewState;
private:
nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;

View File

@ -146,7 +146,8 @@ public:
uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
nsRefPtr<GetCameraCallback> aOnSuccess,
nsRefPtr<CameraErrorCallback> aOnError)
nsRefPtr<CameraErrorCallback> aOnError,
nsRefPtr<Promise> aPromise)
: mPrincipal(aPrincipal)
, mWindow(aWindow)
, mCameraManager(aManager)
@ -154,6 +155,7 @@ public:
, mInitialConfig(aInitialConfig)
, mOnSuccess(aOnSuccess)
, mOnError(aOnError)
, mPromise(aPromise)
{
}
@ -172,9 +174,13 @@ protected:
CameraConfiguration mInitialConfig;
nsRefPtr<GetCameraCallback> mOnSuccess;
nsRefPtr<CameraErrorCallback> mOnError;
nsRefPtr<Promise> mPromise;
};
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mOnSuccess, mOnError)
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow,
mOnSuccess,
mOnError,
mPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
@ -240,13 +246,13 @@ CameraPermissionRequest::DispatchCallback(uint32_t aPermission)
void
CameraPermissionRequest::CallAllow()
{
mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError);
mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError, mPromise);
}
void
CameraPermissionRequest::CallCancel()
{
mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError);
mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError, mPromise);
}
NS_IMETHODIMP
@ -259,10 +265,10 @@ CameraPermissionRequest::GetTypes(nsIArray** aTypes)
aTypes);
}
void
already_AddRefed<Promise>
nsDOMCameraManager::GetCamera(const nsAString& aCamera,
const CameraConfiguration& aInitialConfig,
GetCameraCallback& aOnSuccess,
const OptionalNonNullGetCameraCallback& aOnSuccess,
const OptionalNonNullCameraErrorCallback& aOnError,
ErrorResult& aRv)
{
@ -273,43 +279,61 @@ nsDOMCameraManager::GetCamera(const nsAString& aCamera,
cameraId = 1;
}
nsRefPtr<CameraErrorCallback> errorCallback = nullptr;
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
nsRefPtr<GetCameraCallback> successCallback;
if (aOnSuccess.WasPassed()) {
successCallback = &aOnSuccess.Value();
}
nsRefPtr<CameraErrorCallback> errorCallback;
if (aOnError.WasPassed()) {
errorCallback = &aOnError.Value();
}
if (mPermission == nsIPermissionManager::ALLOW_ACTION) {
PermissionAllowed(cameraId, aInitialConfig, &aOnSuccess, errorCallback);
return;
PermissionAllowed(cameraId, aInitialConfig, successCallback, errorCallback, promise);
return promise.forget();
}
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
if (!sop) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
nsCOMPtr<nsIRunnable> permissionRequest =
new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig,
&aOnSuccess, errorCallback);
successCallback, errorCallback, promise);
NS_DispatchToMainThread(permissionRequest);
return promise.forget();
}
void
nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError)
CameraErrorCallback* aOnError,
Promise* aPromise)
{
mPermission = nsIPermissionManager::ALLOW_ACTION;
// Creating this object will trigger the aOnSuccess callback
// (or the aOnError one, if it fails).
nsRefPtr<nsDOMCameraControl> cameraControl =
new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, mWindow);
new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, aPromise, mWindow);
Register(cameraControl);
}
@ -318,10 +342,12 @@ void
nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
CameraErrorCallback* aOnError)
CameraErrorCallback* aOnError,
Promise* aPromise)
{
mPermission = nsIPermissionManager::DENY_ACTION;
aPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
if (aOnError) {
ErrorResult ignored;
aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored);

View File

@ -8,6 +8,7 @@
#define DOM_CAMERA_DOMCAMERAMANAGER_H
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Promise.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsIObserver.h"
@ -32,7 +33,9 @@ namespace mozilla {
typedef nsTArray<nsRefPtr<mozilla::nsDOMCameraControl> > CameraControls;
typedef nsClassHashtable<nsUint64HashKey, CameraControls> WindowTable;
typedef mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::CameraErrorCallback>>
typedef mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::GetCameraCallback> >
OptionalNonNullGetCameraCallback;
typedef mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::CameraErrorCallback> >
OptionalNonNullCameraErrorCallback;
class nsDOMCameraManager MOZ_FINAL
@ -64,19 +67,22 @@ public:
void PermissionAllowed(uint32_t aCameraId,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback* aOnSuccess,
mozilla::dom::CameraErrorCallback* aOnError);
mozilla::dom::CameraErrorCallback* aOnError,
mozilla::dom::Promise* aPromise);
void PermissionCancelled(uint32_t aCameraId,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback* aOnSuccess,
mozilla::dom::CameraErrorCallback* aOnError);
mozilla::dom::CameraErrorCallback* aOnError,
mozilla::dom::Promise* aPromise);
// WebIDL
void GetCamera(const nsAString& aCamera,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback& aOnSuccess,
const OptionalNonNullCameraErrorCallback& aOnError,
mozilla::ErrorResult& aRv);
already_AddRefed<mozilla::dom::Promise>
GetCamera(const nsAString& aCamera,
const mozilla::dom::CameraConfiguration& aOptions,
const OptionalNonNullGetCameraCallback& aOnSuccess,
const OptionalNonNullCameraErrorCallback& aOnError,
mozilla::ErrorResult& aRv);
void GetListOfCameras(nsTArray<nsString>& aList, mozilla::ErrorResult& aRv);
nsPIDOMWindow* GetParentObject() const { return mWindow; }

View File

@ -0,0 +1,97 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for bug 1022766</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var config = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var Camera = {
cameraObj: null,
_otherPictureSize: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
firstCallFailed: false,
secondCallSucceeded: false,
checkForDone: function test_checkForDone() {
if (Camera.firstCallFailed && Camera.secondCallSucceeded) {
Camera.cameraObj.release();
Camera.cameraObj = null;
CameraTest.end();
}
},
successOne: function test_successOne(focused) {
ok(false, "First call to autoFocus() succeeded unexpectedly");
},
failureOne: function test_failureOne(error) {
ok(error == "AutoFocusInterrupted", "First call to autoFocus() failed with: "
+ error);
Camera.firstCallFailed = true;
Camera.checkForDone();
},
successTwo: function test_successTwo(focused) {
ok(true, "Second call to autoFocus() succeeded");
Camera.secondCallSucceeded = true;
Camera.checkForDone();
},
failureTwo: function test_failureTwo(error) {
ok(false, "Second call to autoFocus() failed unexpectedly with: " + error);
},
start: function test_start() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
// It doesn't matter if the emulator supports focus or not;
// this is just testing the sequencing.
camera.autoFocus(Camera.successOne, Camera.failureOne);
camera.autoFocus(Camera.successTwo, Camera.failureTwo);
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
}
}
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
if (Camera.cameraObj) {
Camera.cameraObj.release();
Camera.cameraObj = null;
}
});
CameraTest.begin("hardware", function(test) {
test.set("auto-focus-process-failure", function() {
Camera.start();
})
});
</script>
</body>
</html>

View File

@ -0,0 +1,223 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for bug 975472</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var config = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var options = {
rotation: 0,
position: {
latitude: 43.645687,
longitude: -79.393661
},
dateTime: Date.now()
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
function next() {
Camera.nextTest();
}
// The array of tests
var tests = [
{
key: "release-after-release",
func: function testAutoFocus(camera) {
function onSuccess(success) {
ok(true, "release() succeeded");
next();
}
function onError(error) {
ok(false, "release() failed with: " + error);
}
camera.release(onSuccess, onError);
}
},
{
key: "set-picture-size-after-release",
func: function testSetPictureSize(camera) {
camera.setPictureSize({ width: 0, height: 0 });
next();
}
},
{
key: "set-thumbnail-size-after-release",
func: function testSetThumbnailSize(camera) {
camera.setThumbnailSize({ width: 0, height: 0 });
next();
}
},
{
key: "get-sensor-angle-after-release",
func: function testGetSensorAngle(camera) {
ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
next();
}
},
{
key: "resume-preview-after-release",
func: function testResumePreview(camera) {
camera.resumePreview();
next();
}
},
{
key: "auto-focus-after-release",
func: function testAutoFocus(camera) {
function onSuccess(success) {
ok(false, "autoFocus() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "autoFocus() failed with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "take-picture-after-release",
func: function testTakePicture(camera) {
function onSuccess(picture) {
ok(false, "takePicture() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "takePicture() failed with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
{
key: "start-recording-after-release",
func: function testStartRecording(camera) {
function onSuccess(picture) {
ok(false, "startRecording() process succeeded incorrectly");
}
function onError(error) {
ok(error === "GeneralFailure", "startRecording() failed with: " + error);
next();
}
var recordingOptions = {
profile: 'cif',
rotation: 0
};
camera.startRecording(recordingOptions,
navigator.getDeviceStorage('videos'),
'bug975472.mp4',
onSuccess, onError);
}
},
{
key: "stop-recording-after-release",
func: function testStopRecording(camera) {
camera.stopRecording();
next();
}
},
{
key: "set-configuration-after-release",
func: function testSetConfiguration(camera) {
function onSuccess(picture) {
ok(false, "setConfiguration() process succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "setConfiguration() failed with: " + error);
next();
}
camera.setConfiguration(config, onSuccess, onError);
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
var Camera = {
cameraObj: null,
_otherPictureSize: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
onCameraReady: function () {
Camera.nextTest = function() {
try {
var t = testGenerator.next();
info("test: " + t.key);
t.func(Camera.cameraObj);
} catch(e) {
if (e instanceof StopIteration) {
CameraTest.end();
} else {
throw e;
}
}
};
// Release the camera hardware, and call all of the asynchronous methods
// to make sure they properly handle being in this state.
Camera.cameraObj.release();
next();
},
release: function release() {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
ok(camera.capabilities.pictureSizes.length > 0,
"capabilities.pictureSizes.length = " +
camera.capabilities.pictureSizes.length);
Camera._otherPictureSize = camera.capabilities.pictureSizes.slice(-1)[0];
camera.setPictureSize(camera.capabilities.pictureSizes[0]);
options.pictureSize = Camera._otherPictureSize;
options.fileFormat = camera.capabilities.fileFormats[0];
info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
info("viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onCameraReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.start();
</script>
</body>
</html>

View File

@ -0,0 +1,242 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras.getCamera() with separate .setConfiguration() call</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles', 'zoomRatios', 'isoModes'];
var Camera = {
cameraObj: null,
_recording: false,
_currentTest: null,
_autoFocusSupported: 0,
_manuallyFocused: false,
_flashmodes: null,
_pictureSizes: null,
_previewSizes: null,
_whiteBalanceModes: null,
_zoomRatios: null,
_sceneModes: null,
_focusModes: null,
_zoomRatios: null,
_testsCompleted: 0,
_shutter: 0,
_config: {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
},
_tests: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
setFlashMode: function camera_setFlash(mode) {
this.cameraObj.flashMode = mode;
},
setFocus: function camera_setfocus(mode) {
this.cameraObj.focus = mode;
},
setZoom: function camera_setZoom(zoom) {
this.cameraObj.zoom = zoom;
},
getZoom: function camera_getZoom() {
return this.cameraObj.zoom;
},
getFileFormats: function camera_formats() {
this._fileFormats = this.cameraObj.capabilities.fileFormats;
},
getFlashModes: function camera_getFlash() {
this._flashmodes = this.cameraObj.capabilities.flashModes;
},
getFocusModes: function camera_getFocus() {
this._focusModes = this.cameraObj.capabilities.focusModes;
},
getSceneModes: function camera_getScene() {
this._sceneModes = this.cameraObj.capabilities.sceneModes;
},
getZoomRatios: function camera_getZoom() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
getWhiteBalance: function camera_white() {
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
},
getPictureSizes: function camera_sizes() {
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
},
getPreviewSizes: function camera_preview() {
this._previewSizes = this.cameraObj.capabilities.previewSizes;
},
getZoomRatios: function camera_preview() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
takePictureSuccess: function taken_foto(blob) {
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
SimpleTest.finish();
} else {
Camera.runTests();
}
}
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
img.src = window.URL.createObjectURL(blob);
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
ok(camcap.maxMeteringAreas >= 0, "maxMeteringAreas = " + camcap.maxMeteringAreas);
ok(camcap.maxFocusAreas >= 0, "maxFocusAreas = " + camcap.maxFocusAreas);
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
}
}
Camera.getPictureSizes();
Camera.getPreviewSizes();
Camera.getFileFormats();
Camera.getFocusModes();
Camera.getZoomRatios();
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
ok(camcap.isoModes.length == 0, "ISO modes length = " + camcap.isoModes.length);
// The emulator doesn't support zoom, so these parameters will be very constrained
// For more ambitious tests, see test_camera_fake_parameters.html
ok(Camera._zoomRatios.length == 1, "zoom ratios length = " + Camera._zoomRatios.length);
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
// Test snapping to supported values
Camera.cameraObj.zoom = 0.9;
ok(Camera.cameraObj.zoom == 1.0, "zoom (lower limit) = " + Camera.cameraObj.zoom);
Camera.cameraObj.zoom = 1.1;
ok(Camera.cameraObj.zoom == 1.0, "zoom (upper limit) = " + Camera.cameraObj.zoom);
// Check image quality handling
Camera.cameraObj.pictureQuality = 0.0;
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = -0.1;
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (minimum limit) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = -Math.pow(2, 80);
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (BIG negative) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = 1.0;
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = 1.1;
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (maximum limit) = " + Camera.cameraObj.pictureQuality);
Camera.cameraObj.pictureQuality = Math.pow(2, 80);
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (BIG positive) = " + Camera.cameraObj.pictureQuality);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {
for (var l in Camera._fileFormats) {
var config = {
pictureSize: Camera._pictureSizes[i],
fileFormat: Camera._fileFormats[l]
};
Camera._tests.push(config);
}
}
Camera.runTests();
},
runTests: function run_tests() {
var test = this._tests[this._testsCompleted];
this._currentTest = test;
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
onConfigChange: function onConfigChange(config) {
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
},
setUp: function setup_tests() {
function onSuccess(camera) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
ok(true, "Camera Control object has been successfully initialized");
Camera.cameraObj.setConfiguration(options, Camera.onConfigChange, onError);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, null, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.setUp();
</script>
</body>
</html>

View File

@ -0,0 +1,200 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras.getCamera() using an initial configuration</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles'];
var Camera = {
cameraObj: null,
_recording: false,
_currentTest: null,
_autoFocusSupported: 0,
_manuallyFocused: false,
_flashmodes: null,
_pictureSizes: null,
_previewSizes: null,
_whiteBalanceModes: null,
_zoomRatios: null,
_sceneModes: null,
_focusModes: null,
_testsCompleted: 0,
_shutter: 0,
_config: {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
},
_tests: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
setFlashMode: function camera_setFlash(mode) {
this.cameraObj.flashMode = mode;
},
setFocus: function camera_setfocus(mode) {
this.cameraObj.focus = mode;
},
getFileFormats: function camera_formats() {
this._fileFormats = this.cameraObj.capabilities.fileFormats;
},
getFlashModes: function camera_getFlash() {
this._flashmodes = this.cameraObj.capabilities.flashModes;
},
getFocusModes: function camera_getFocus() {
this._focusModes = this.cameraObj.capabilities.focusModes;
},
getSceneModes: function camera_getScene() {
this._sceneModes = this.cameraObj.capabilities.sceneModes;
},
getZoomRatios: function camera_getZoom() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
getWhiteBalance: function camera_white() {
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
},
getPictureSizes: function camera_sizes() {
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
},
getPreviewSizes: function camera_preview() {
this._previewSizes = this.cameraObj.capabilities.previewSizes;
},
takePictureSuccess: function taken_foto(blob) {
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
SimpleTest.finish();
} else {
Camera.runTests();
}
}
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
img.src = window.URL.createObjectURL(blob);
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
}
}
Camera.getPictureSizes();
Camera.getPreviewSizes();
Camera.getFileFormats();
Camera.getFocusModes();
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {
for (var l in Camera._fileFormats) {
var config = {
pictureSize: Camera._pictureSizes[i],
fileFormat: Camera._fileFormats[l]
};
Camera._tests.push(config);
}
}
Camera.runTests();
},
runTests: function run_tests() {
var test = this._tests[this._testsCompleted];
this._currentTest = test;
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
setUp: function setup_tests() {
function onSuccess(camera, config) {
ok(true, "Camera Control object has been successfully initialized");
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.setUp();
</script>
</body>
</html>

View File

@ -0,0 +1,76 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for multiple calls to mozCameras.getCamera()</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
var Camera = {
cameraObj: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
onReady: function take_two() {
function onSuccess(camera, config) {
ok(false, "Unexpectedly got second camera instance: " + config.toSource);
}
function onFailure(error) {
ok(true, "Correctly failed to get camera again");
SimpleTest.finish();
}
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onFailure);
},
release: function release() {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
Camera.cameraObj.release();
Camera.cameraObj = null;
});
Camera.start();
</script>
</body>
</html>

View File

@ -0,0 +1,131 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=965421
-->
<head>
<title>Bug 965421 - Test camera hardware API for Auto focus moving Callback</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965421">Mozilla Bug 965421</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
const PREF_AUTOFOCUSCALLBACK_ENABLED = "camera.control.autofocus_moving_callback.enabled";
var cameraObj;
var oldPref;
// Shorthand functions
function end() {
function reallyEnd() {
CameraTest.end();
}
if (oldPref) {
SpecialPowers.pushPrefEnv(
{'set': [[PREF_AUTOFOCUSCALLBACK_ENABLED, oldPref]]}, reallyEnd);
} else {
SpecialPowers.pushPrefEnv(
{'clear': [[PREF_AUTOFOCUSCALLBACK_ENABLED]]}, reallyEnd);
}
}
function next() {
CameraTest.next();
}
var tests = [
{
key: "autofocus-moving-true",
func: function testAutoFocusMovingIsTrue(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == true,"onAutoFocusMoving callback received true correctly");
camera.focusMode = 'auto';
next();
}
camera.focusMode = 'continuous-picture';
}
},
{
key: "autofocus-moving-false",
func: function testAutoFocusMovingIsFalse(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == false, "onAutoFocusMoving callback received false correctly");
camera.focusMode = 'auto';
end();
}
camera.focusMode = 'continuous-video';
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
cameraObj.release();
cameraObj = null;
});
// Must call CameraTest.begin() before any other async methods.
CameraTest.begin("hardware", function(test) {
// If the pref doesn't exist, this get will fail; catch it and continue.
try {
oldPref = SpecialPowers.getBoolPref(PREF_AUTOFOCUSCALLBACK_ENABLED);
} catch(e) { }
SpecialPowers.pushPrefEnv({'set': [[PREF_AUTOFOCUSCALLBACK_ENABLED, true]]}, function() {
var enabled;
try {
enabled = SpecialPowers.getBoolPref(PREF_AUTOFOCUSCALLBACK_ENABLED);
} catch(e) { }
ok(enabled, PREF_AUTOFOCUSCALLBACK_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
})
});
</script>
</body>
</html>

View File

@ -0,0 +1,320 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=965420
-->
<head>
<title>Bug 965420 - Test camera hardware API for face detection</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965420">Mozilla Bug 965420</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
const PREF_FACEDETECTION_ENABLED = "camera.control.face_detection.enabled";
var cameraObj;
var oldPref;
// Shorthand functions
function end() {
function reallyEnd() {
CameraTest.end();
}
if (oldPref) {
SpecialPowers.pushPrefEnv(
{'set': [[PREF_FACEDETECTION_ENABLED, oldPref]]}, reallyEnd);
} else {
SpecialPowers.pushPrefEnv(
{'clear': [[PREF_FACEDETECTION_ENABLED]]}, reallyEnd);
}
}
function next() {
CameraTest.next();
}
function compareFaces(aFaces, expected)
{
ok(aFaces, "have detected faces object");
ok(aFaces.length == expected.faces.length,
"expected=" + expected.faces.length + ", got=" + aFaces.length);
aFaces.forEach(function (face, index) {
let result = compareFace(face, expected.faces[index]);
ok(result === "ok", "face check: " + result);
if (result !== "ok") {
return false;
}
});
return true;
}
function compareFace(aFace, expected)
{
if (aFace.id != expected.id) {
return "expected face.id=" + expected.id + ", got=" + aFace.id;
}
if (aFace.score != expected.score) {
return "expected face.score=" + expected.score + ", got=" + aFace.score;
}
if (!aFace.bounds) {
return "face.bounds is missing";
}
if (aFace.bounds.left != expected.bounds.left ||
aFace.bounds.top != expected.bounds.top ||
aFace.bounds.right != expected.bounds.right ||
aFace.bounds.bottom != expected.bounds.bottom) {
return "expected face.bounds=" + expected.bounds.toSource() +
", got=({left:" + aFace.bounds.left + ", top:" + aFace.bounds.top + ", right:" + aFace.bounds.right + ", bottom:" + aFace.bounds.bottom + "})";
}
if (aFace.leftEye && !expected.leftEye) {
return "expected null face.leftEye, got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
}
if (!aFace.leftEye && expected.leftEye) {
return "expected face.leftEye=" + expected.leftEye.toSource() + ", got null leftEye";
}
if (aFace.leftEye && expected.leftEye &&
(aFace.leftEye.x != expected.leftEye.x || aFace.leftEye.y != expected.leftEye.y)) {
return "expected face.leftEye=" + expected.leftEye.toSource() +
", got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
}
if (aFace.rightEye && !expected.rightEye) {
return "expected null face.rightEye, got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
}
if (!aFace.rightEye && expected.rightEye) {
return "expected face.rightEye=" + expected.rightEye.toSource() + ", got null rightEye";
}
if (aFace.rightEye && expected.rightEye &&
(aFace.rightEye.x != expected.rightEye.x || aFace.rightEye.y != expected.rightEye.y)) {
return "expected face.rightEye=" + expected.rightEye.toSource() +
", got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
}
if (aFace.mouth && !expected.mouth) {
return "expected null face.mouth, got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
}
if (!aFace.mouth && expected.mouth) {
return "expected face.mouth=" + expected.mouth.toSource() + ", got null mouth";
}
if (aFace.mouth && expected.mouth &&
(aFace.mouth.x != expected.mouth.x || aFace.mouth.y != expected.mouth.y)) {
return "expected face.mouth=" + expected.mouth.toSource() +
", got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
}
return "ok";
}
var tests = [
{
key: "face-detection-detected-one-face",
func: function testFaceDetectionFoundOneFace(camera) {
var expected = {
faces: [ {
id: 1,
score: 2,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: {
x: 7,
y: 8
},
rightEye: {
x: 9,
y: 10
},
mouth: {
x: 11,
y: 12
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-detected-two-faces",
func: function testFaceDetectionFoundTwoFace(camera) {
var expected = {
faces: [ {
id: 1,
score: 2,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: {
x: 7,
y: 8
},
rightEye: {
x: 9,
y: 10
},
mouth: {
x: 11,
y: 12
}
},
{
id: 13,
score: 14,
bounds: {
left: 15,
top: 16,
right: 17,
bottom: 18
},
leftEye: {
x: 19,
y: 20
},
rightEye: {
x: 21,
y: 22
},
mouth: {
x: 23,
y: 24
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-detected-one-face-no-features",
func: function (camera) {
var expected = {
faces: [ {
id: 1,
score: 100,
bounds: {
left: 3,
top: 4,
right: 5,
bottom: 6
},
leftEye: null,
rightEye: null,
mouth: null
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
{
key: "face-detection-no-faces-detected",
func: function (camera) {
var expected = {
faces: []
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
camera.stopFaceDetection();
next();
}
camera.startFaceDetection();
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
if (cameraObj) {
cameraObj.release();
cameraObj = null;
}
});
// Must call CameraTest.begin() before any other async methods.
CameraTest.begin("hardware", function(test) {
// If the pref doesn't exist, this get will fail; catch it and continue.
try {
oldPref = SpecialPowers.getBoolPref(PREF_FACEDETECTION_ENABLED);
} catch(e) { }
SpecialPowers.pushPrefEnv({'set': [[PREF_FACEDETECTION_ENABLED, true]]}, function() {
var enabled;
try {
enabled = SpecialPowers.getBoolPref(PREF_FACEDETECTION_ENABLED);
} catch(e) { }
ok(enabled, PREF_FACEDETECTION_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
})
});
</script>
</body>
</html>

View File

@ -0,0 +1,145 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=940424
-->
<head>
<title>Bug 940424 - Test camera hardware API failure handling</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940424">Mozilla Bug 940424</a>
<video id="viewfinder" width = "200" height = "200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var cameraObj;
// Shorthand functions
function end() {
CameraTest.end();
}
function next() {
CameraTest.next();
}
// The array of tests
var tests = [
{
key: "auto-focus-failure",
func: function testAutoFocusApiFailure(camera) {
function onSuccess(success) {
ok(false, "autoFocus() succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "autoFocus() failed correctly with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "auto-focus-process-failure",
func: function testAutoFocusProcessFailure(camera) {
function onSuccess(success) {
if (success) {
ok(false, "autoFocus() process succeeded incorrectly");
end();
} else {
ok(true, "autoFocus() process failed correctly");
next();
}
}
function onError(error) {
ok(false, "autoFocus() process failed incorrectly with: " + error);
end();
}
camera.autoFocus(onSuccess, onError);
}
},
{
key: "take-picture-failure",
func: function testTakePictureApiFailure(camera) {
function onSuccess(picture) {
ok(false, "takePicture() succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "takePicture() failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
{
key: "take-picture-process-failure",
func: function testTakePictureProcessFailure(camera) {
function onSuccess(picture) {
ok(false, "takePicture() process succeeded incorrectly");
end();
}
function onError(error) {
ok(true, "takePicture() process failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
}
},
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
cameraObj.release();
cameraObj = null;
});
CameraTest.begin("hardware", function(test) {
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
} else {
throw e;
}
}
};
next();
}
function onError(error) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
});
</script>
</body>
</html>

View File

@ -0,0 +1,96 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=940424
-->
<head>
<title>Bug 940424 - Test camera hardware init failure handling</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="../camera_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940424">Mozilla Bug 940424</a>
<video id="viewfinder" width="200" height="200" autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var initialConfig = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var tests = [
{
name: "init-failure",
key: "init-failure",
func: function testInitFailure(test) {
function onSuccess(camera, config) {
ok(false, "onSuccess called incorrectly");
camera.release();
test.next();
}
function onError(error) {
ok(true, "onError called correctly on init failure");
test.next();
}
info("Running test: init-failure");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
}
},
/* This test case (init-success) *must* follow the preceeding test case
(init-failure) in order for the desired condition to be verified */
{
name: "init-success",
key: "",
func: function(test) {
function onSuccess(camera, config) {
ok(true, "onSuccess called correctly");
camera.release();
test.next();
}
function onError(error) {
ok(false, "onError called incorrectly: " + error);
test.next();
}
info("Running test: init-success");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError)
}
}
];
var testGenerator = function() {
for (var i = 0; i < tests.length; ++i ) {
yield tests[i];
}
}();
CameraTest.begin("hardware", function(test) {
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, CameraTest));
} catch(e) {
if (e instanceof StopIteration) {
CameraTest.end();
} else {
throw e;
}
}
};
CameraTest.next();
});
</script>
</body>
</html>

View File

@ -122,6 +122,7 @@ var CameraTest = (function() {
callback();
}
}
function cleanUpTestEnabled() {
var next = allCleanedUp;
if (oldTestEnabled) {

View File

@ -1,6 +1,15 @@
[DEFAULT]
support-files = camera_common.js
[callback/test_camera.html]
[callback/test_camera_2.html]
[callback/test_camera_3.html]
[callback/test_camera_hardware_init_failure.html]
[callback/test_camera_hardware_failures.html]
[callback/test_bug975472.html]
[callback/test_camera_hardware_face_detection.html]
[callback/test_camera_hardware_auto_focus_moving_cb.html]
[callback/test_bug1022766.html]
[test_camera.html]
[test_camera_2.html]
[test_camera_3.html]

View File

@ -23,7 +23,7 @@ var config = {
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
var Camera = {
@ -47,7 +47,7 @@ var Camera = {
ok(false, "First call to autoFocus() succeeded unexpectedly");
},
failureOne: function test_failureOne(error) {
ok(error == "AutoFocusInterrupted", "First call to autoFocus() failed with: "
ok(error.name == "NS_ERROR_IN_PROGRESS", "First call to autoFocus() failed with: "
+ error);
Camera.firstCallFailed = true;
Camera.checkForDone();
@ -62,18 +62,19 @@ var Camera = {
},
start: function test_start() {
function onSuccess(camera, config) {
function onSuccess(d) {
var camera = d.camera;
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
// It doesn't matter if the emulator supports focus or not;
// this is just testing the sequencing.
camera.autoFocus(Camera.successOne, Camera.failureOne);
camera.autoFocus(Camera.successTwo, Camera.failureTwo);
camera.autoFocus().then(Camera.successOne, Camera.failureOne);
camera.autoFocus().then(Camera.successTwo, Camera.failureTwo);
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
}
}

View File

@ -46,7 +46,9 @@ var Camera = {
SimpleTest.finish();
}
function getCamera_onSuccess(camera, cfg) {
function getCamera_onSuccess(d) {
var camera = d.camera;
var cfg = d.configuration;
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
@ -59,10 +61,10 @@ var Camera = {
"Initial recorder profile = '" + cfg.recorderProfile + "'");
// Apply our specific configuration
camera.setConfiguration(config, setConfig_onSuccess, onError);
camera.setConfiguration(config).then(setConfig_onSuccess, onError);
}
navigator.mozCameras.getCamera(whichCamera, {}, getCamera_onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
}
}

View File

@ -31,7 +31,7 @@ var options = {
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
function next() {
Camera.nextTest();
@ -49,7 +49,7 @@ var tests = [
function onError(error) {
ok(false, "release() failed with: " + error);
}
camera.release(onSuccess, onError);
camera.release().then(onSuccess, onError);
}
},
{
@ -87,10 +87,10 @@ var tests = [
ok(false, "autoFocus() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "autoFocus() failed with: " + error);
ok(error.name === "NS_ERROR_NOT_INITIALIZED", "autoFocus() failed with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
camera.autoFocus().then(onSuccess, onError);
}
},
{
@ -100,10 +100,10 @@ var tests = [
ok(false, "takePicture() succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "takePicture() failed with: " + error);
ok(error.name === "NS_ERROR_NOT_INITIALIZED", "takePicture() failed with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
camera.takePicture(null).then(onSuccess, onError);
}
},
{
@ -113,7 +113,7 @@ var tests = [
ok(false, "startRecording() process succeeded incorrectly");
}
function onError(error) {
ok(error === "GeneralFailure", "startRecording() failed with: " + error);
ok(error.name === "NS_ERROR_FAILURE", "startRecording() failed with: " + error);
next();
}
var recordingOptions = {
@ -122,8 +122,7 @@ var tests = [
};
camera.startRecording(recordingOptions,
navigator.getDeviceStorage('videos'),
'bug975472.mp4',
onSuccess, onError);
'bug975472.mp4').then(onSuccess, onError);
}
},
{
@ -140,10 +139,10 @@ var tests = [
ok(false, "setConfiguration() process succeeded incorrectly");
}
function onError(error) {
ok(error === "HardwareClosed", "setConfiguration() failed with: " + error);
ok(error.name === "NS_ERROR_NOT_INITIALIZED", "setConfiguration() failed with: " + error);
next();
}
camera.setConfiguration(config, onSuccess, onError);
camera.setConfiguration(config).then(onSuccess, onError);
}
},
];
@ -183,8 +182,17 @@ var Camera = {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
function onSuccess(d) {
var camera = d.camera;
Camera.cameraObj = camera;
var onPreviewStateChange = function(e) {
if (e.newState === 'started') {
info("viewfinder is ready and playing");
Camera.cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
Camera.onCameraReady();
}
};
camera.addEventListener('previewstatechange', onPreviewStateChange);
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
ok(camera.capabilities.pictureSizes.length > 0,
@ -195,15 +203,8 @@ var Camera = {
options.pictureSize = Camera._otherPictureSize;
options.fileFormat = camera.capabilities.fileFormats[0];
info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
info("viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onCameraReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
}
}

View File

@ -29,7 +29,7 @@ var config = {
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
@ -102,12 +102,17 @@ var Camera = {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
takePictureSuccess: function taken_foto(blob) {
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
},
takePictureEvent: function taken_foto_evt(e) {
var blob = e.data;
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
@ -196,7 +201,7 @@ var Camera = {
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
Camera.cameraObj.takePicture(config).then(this.takePictureSuccess.bind(this), onError);
},
onConfigChange: function onConfigChange(config) {
ok(config.mode === options.mode, "configuration mode = " + config.mode);
@ -205,24 +210,27 @@ var Camera = {
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
},
onPreviewStateChange: function onPreviewStateChange(e) {
if (e.newState === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.onReady();
}
},
setUp: function setup_tests() {
function onSuccess(camera) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
function onSuccess(d) {
Camera.cameraObj = d.camera;
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.cameraObj.addEventListener('configurationchanged', Camera.onConfigChange);
Camera.cameraObj.addEventListener('shutter', Camera.shutter);
Camera.cameraObj.addEventListener('picture', Camera.takePictureEvent.bind(Camera));
Camera.viewfinder.mozSrcObject = d.camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
ok(true, "Camera Control object has been successfully initialized");
Camera.cameraObj.setConfiguration(options, Camera.onConfigChange, onError);
Camera.cameraObj.onShutter = Camera.shutter;
Camera.cameraObj.setConfiguration(options).then(Camera.onConfigChange, onError);
};
navigator.mozCameras.getCamera(whichCamera, null, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, null).then(onSuccess, onError);
}
}

View File

@ -29,7 +29,7 @@ var config = {
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
@ -113,11 +113,19 @@ var Camera = {
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onPreviewStateChange: function onPreviewStateChange(e) {
ok(true, "viewfinder state change " + e);
if (e.newState === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.onReady();
}
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
@ -125,7 +133,7 @@ var Camera = {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
}
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
@ -157,30 +165,25 @@ var Camera = {
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
Camera.cameraObj.takePicture(config).then(this.takePictureSuccess.bind(this), onError);
},
setUp: function setup_tests() {
function onSuccess(camera, config) {
function onSuccess(d) {
var config = d.configuration;
ok(true, "Camera Control object has been successfully initialized");
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.cameraObj = d.camera;
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.cameraObj.addEventListener('shutter', Camera.shutter);
Camera.viewfinder.mozSrcObject = d.camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
}
}

View File

@ -22,7 +22,7 @@ var options = {
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
var Camera = {
@ -31,32 +31,33 @@ var Camera = {
return document.getElementById('viewfinder');
},
onReady: function take_two() {
function onSuccess(camera, config) {
ok(false, "Unexpectedly got second camera instance: " + config.toSource);
function onSuccess(d) {
ok(false, "Unexpectedly got second camera instance: " + d.config.toSource);
}
function onFailure(error) {
ok(true, "Correctly failed to get camera again");
SimpleTest.finish();
}
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onFailure);
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onFailure);
},
onPreviewStateChange: function onPreviewStateChange(e) {
if (e.newState === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.onReady();
}
},
release: function release() {
cameraObj = null;
},
start: function run_test() {
function onSuccess(camera, config) {
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
function onSuccess(d) {
Camera.cameraObj = d.camera;
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
Camera.viewfinder.mozSrcObject = d.camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
}
}

View File

@ -28,7 +28,7 @@ var cameraObj = null;
// Shorthand functions
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
ok(false, "Error " + e);
}
function end() {
@ -427,20 +427,19 @@ CameraTest.begin("hardware", function(test) {
try {
var t = testGenerator.next();
info("test: " + t.key);
function onSuccess(camera, config) {
cameraObj = camera;
document.getElementById('viewfinder').mozSrcObject = camera;
camera.onPreviewStateChange = function (state) {
if (state === "started") {
t.test(camera, camera.capabilities);
} else {
ok(false, "preview started (state = '" + state + "')");
function onSuccess(d) {
cameraObj = d.camera;
document.getElementById('viewfinder').mozSrcObject = d.camera;
var onPreviewStateChange = function (evt) {
if (evt.newState === "started") {
cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
t.test(cameraObj, cameraObj.capabilities);
}
camera.onPreviewStateChange = null;
};
cameraObj.addEventListener('previewstatechange', onPreviewStateChange);
}
CameraTest.run = function() {
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError);
};
t.prep(test);
} catch(e) {

View File

@ -53,25 +53,16 @@ var tests = [
{
key: "autofocus-moving-true",
func: function testAutoFocusMovingIsTrue(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == true,"onAutoFocusMoving callback received true correctly");
var handler = function(evt) {
camera.removeEventListener("focus", handler);
ok(evt.newState == "focusing", "autofocus event state focusing == " + evt.newState);
camera.focusMode = 'auto';
next();
}
camera.addEventListener("focus", handler);
camera.focusMode = 'continuous-picture';
}
},
{
key: "autofocus-moving-false",
func: function testAutoFocusMovingIsFalse(camera) {
camera.onAutoFocusMoving = function(aIsMoving) {
ok(aIsMoving == false, "onAutoFocusMoving callback received false correctly");
camera.focusMode = 'auto';
end();
}
camera.focusMode = 'continuous-video';
}
},
];
var testGenerator = function() {
@ -100,13 +91,13 @@ CameraTest.begin("hardware", function(test) {
} catch(e) { }
ok(enabled, PREF_AUTOFOCUSCALLBACK_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
function onSuccess(d) {
document.getElementById('viewfinder').mozSrcObject = d.camera;
cameraObj = d.camera;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
test.set(t.key, t.func.bind(undefined, d.camera));
} catch(e) {
if (e instanceof StopIteration) {
end();
@ -121,7 +112,7 @@ CameraTest.begin("hardware", function(test) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError);
})
});

View File

@ -150,12 +150,16 @@ var tests = [
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
var handler = function(evt) {
ok(compareFaces(evt.faces, expected),
"facedetected event received the detected faces correctly");
camera.stopFaceDetection();
camera.removeEventListener('facesdetected', handler);
next();
}
};
camera.addEventListener('facesdetected', handler);
camera.startFaceDetection();
}
},
@ -208,12 +212,16 @@ var tests = [
}
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
var handler = function(evt) {
ok(compareFaces(evt.faces, expected),
"facedetected event received the detected faces correctly");
camera.stopFaceDetection();
camera.removeEventListener('facesdetected', handler);
next();
}
};
camera.addEventListener('facesdetected', handler);
camera.startFaceDetection();
}
},
@ -235,12 +243,16 @@ var tests = [
mouth: null
} ]
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
var handler = function(evt) {
ok(compareFaces(evt.faces, expected),
"facedetected event received the detected faces correctly");
camera.stopFaceDetection();
camera.removeEventListener('facesdetected', handler);
next();
}
};
camera.addEventListener('facesdetected', handler);
camera.startFaceDetection();
}
},
@ -250,12 +262,16 @@ var tests = [
var expected = {
faces: []
};
camera.onFacesDetected = function(aFaces) {
ok(compareFaces(aFaces, expected),
"onFaceDetected received the detected faces correctly");
var handler = function(evt) {
ok(compareFaces(evt.faces, expected),
"facedetected event received the detected faces correctly");
camera.stopFaceDetection();
camera.removeEventListener('facesdetected', handler);
next();
}
};
camera.addEventListener('facesdetected', handler);
camera.startFaceDetection();
}
},
@ -289,13 +305,13 @@ CameraTest.begin("hardware", function(test) {
} catch(e) { }
ok(enabled, PREF_FACEDETECTION_ENABLED + " is " + enabled);
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
function onSuccess(d) {
cameraObj = d.camera;
document.getElementById('viewfinder').mozSrcObject = cameraObj;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
test.set(t.key, t.func.bind(undefined, cameraObj));
} catch(e) {
if (e instanceof StopIteration) {
end();
@ -310,7 +326,7 @@ CameraTest.begin("hardware", function(test) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError);
})
});

View File

@ -50,7 +50,7 @@ var tests = [
ok(true, "autoFocus() failed correctly with: " + error);
next();
}
camera.autoFocus(onSuccess, onError);
camera.autoFocus().then(onSuccess, onError);
}
},
{
@ -69,7 +69,7 @@ var tests = [
ok(false, "autoFocus() process failed incorrectly with: " + error);
end();
}
camera.autoFocus(onSuccess, onError);
camera.autoFocus().then(onSuccess, onError);
}
},
{
@ -83,7 +83,7 @@ var tests = [
ok(true, "takePicture() failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
camera.takePicture(null).then(onSuccess, onError);
}
},
{
@ -97,7 +97,7 @@ var tests = [
ok(true, "takePicture() process failed correctly with: " + error);
next();
}
camera.takePicture(null, onSuccess, onError);
camera.takePicture(null).then(onSuccess, onError);
}
},
];
@ -115,13 +115,13 @@ window.addEventListener('beforeunload', function() {
});
CameraTest.begin("hardware", function(test) {
function onSuccess(camera, config) {
document.getElementById('viewfinder').mozSrcObject = camera;
cameraObj = camera;
function onSuccess(d) {
cameraObj = d.camera;
document.getElementById('viewfinder').mozSrcObject = cameraObj;
CameraTest.next = function() {
try {
var t = testGenerator.next();
test.set(t.key, t.func.bind(undefined, camera));
test.set(t.key, t.func.bind(undefined, cameraObj));
} catch(e) {
if (e instanceof StopIteration) {
end();
@ -136,7 +136,7 @@ CameraTest.begin("hardware", function(test) {
ok(false, "getCamera() failed with: " + error);
end();
}
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError);
});
</script>

View File

@ -34,9 +34,9 @@ var tests = [
name: "init-failure",
key: "init-failure",
func: function testInitFailure(test) {
function onSuccess(camera, config) {
function onSuccess(d) {
ok(false, "onSuccess called incorrectly");
camera.release();
d.camera.release();
test.next();
}
function onError(error) {
@ -44,7 +44,7 @@ var tests = [
test.next();
}
info("Running test: init-failure");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError);
}
},
/* This test case (init-success) *must* follow the preceeding test case
@ -53,9 +53,9 @@ var tests = [
name: "init-success",
key: "",
func: function(test) {
function onSuccess(camera, config) {
function onSuccess(d) {
ok(true, "onSuccess called correctly");
camera.release();
d.camera.release();
test.next();
}
function onError(error) {
@ -63,7 +63,7 @@ var tests = [
test.next();
}
info("Running test: init-success");
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError)
navigator.mozCameras.getCamera(whichCamera, initialConfig).then(onSuccess, onError)
}
}
];

View File

@ -76,6 +76,18 @@ const kEventConstructors = {
return new CallGroupErrorEvent(aName, aProps);
},
},
CameraConfigurationEvent: { create: function (aName, aProps) {
return new CameraConfigurationEvent(aName, aProps);
},
},
CameraFacesDetectedEvent: { create: function (aName, aProps) {
return new CameraFacesDetectedEvent(aName, aProps);
},
},
CameraStateChangeEvent: { create: function (aName, aProps) {
return new CameraStateChangeEvent(aName, aProps);
},
},
CFStateChangeEvent: { create: function (aName, aProps) {
return new CFStateChangeEvent(aName, aProps);
},

View File

@ -200,12 +200,18 @@ var interfaceNamesInGlobalScope =
{name: "CallGroupErrorEvent", b2g: true, pref: "dom.telephony.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraCapabilities", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraConfigurationEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraControl", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraDetectedFace", b2g: true, pref: "camera.control.face_detection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraFacesDetectedEvent", b2g: true, pref: "camera.control.face_detection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraManager", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraStateChangeEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"CanvasGradient",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,20 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
[Func="Navigator::HasCameraSupport",
Constructor(DOMString type, optional CameraConfigurationEventInit eventInitDict)]
interface CameraConfigurationEvent : Event
{
readonly attribute CameraMode mode;
readonly attribute DOMString recorderProfile;
readonly attribute DOMRectReadOnly? previewSize;
};
dictionary CameraConfigurationEventInit : EventInit
{
CameraMode mode = "picture";
DOMString recorderProfile = "cif";
DOMRectReadOnly? previewSize = null;
};

View File

@ -235,20 +235,48 @@ interface CameraControl : MediaStream
a shutter sound and/or a visual shutter indicator. */
attribute CameraShutterCallback? onShutter;
/* the event dispatched on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator.
contains no event-specific data. */
attribute EventHandler onshutter;
/* the function to call when the camera hardware is closed
by the underlying framework, e.g. when another app makes a more
recent call to get the camera. */
attribute CameraClosedCallback? onClosed;
/* the event dispatched when the camera hardware is closed
by the underlying framework, e.g. when another app makes a more
recent call to get the camera.
contains no event-specific data. */
attribute EventHandler onclose;
/* the function to call when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached. */
attribute CameraRecorderStateChange? onRecorderStateChange;
/* the event dispatched when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached.
event type is CameraStateChangeEvent where:
'newState' is the new recorder state */
attribute EventHandler onrecorderstatechange;
/* the function to call when the viewfinder stops or starts,
useful for synchronizing other UI elements. */
attribute CameraPreviewStateChange? onPreviewStateChange;
/* the event dispatched when the viewfinder stops or starts,
useful for synchronizing other UI elements.
event type is CameraStateChangeEvent where:
'newState' is the new preview state */
attribute EventHandler onpreviewstatechange;
/* the size of the picture to be returned by a call to takePicture();
an object with 'height' and 'width' properties that corresponds to
one of the options returned by capabilities.pictureSizes. */
@ -287,7 +315,25 @@ interface CameraControl : MediaStream
/* tell the camera to attempt to focus the image */
[Throws]
void autoFocus(CameraAutoFocusCallback onSuccess, optional CameraErrorCallback onError);
Promise<boolean> autoFocus(optional CameraAutoFocusCallback onSuccess,
optional CameraErrorCallback onError);
/* the event dispatched whenever the focus state changes due to calling
autoFocus or due to continuous autofocus.
if continuous autofocus is supported and focusMode is set to enable it,
then this event is dispatched whenever the camera decides to start and
stop moving the focus position; it can be used to update a UI element to
indicate that the camera is still trying to focus, or has finished. Some
platforms do not support this event, in which case the callback is never
invoked.
event type is CameraStateChangeEvent where:
'newState' is one of the following states:
'focused' if the focus is now set
'focusing' if the focus is moving
'unfocused' if last attempt to focus failed */
attribute EventHandler onfocus;
/* if continuous autofocus is supported and focusMode is set to enable it,
then this function is called whenever the camera decides to start and
@ -305,18 +351,23 @@ interface CameraControl : MediaStream
invoking this function will stop the preview stream, which must be
manually restarted (e.g. by calling .play() on it). */
[Throws]
void takePicture(CameraPictureOptions aOptions,
CameraTakePictureCallback onSuccess,
optional CameraErrorCallback onError);
Promise<Blob> takePicture(optional CameraPictureOptions aOptions,
optional CameraTakePictureCallback onSuccess,
optional CameraErrorCallback onError);
/* start recording video; 'aOptions' is a
CameraStartRecordingOptions object. */
/* the event dispatched when a picture is successfully taken; it is of the
type BlobEvent, where the data attribute contains the picture. */
attribute EventHandler onpicture;
/* start recording video; 'aOptions' is a CameraStartRecordingOptions object.
If the success/error callbacks are not used, one may determine success by
waiting for the recorderstatechange event. */
[Throws]
void startRecording(CameraStartRecordingOptions aOptions,
DeviceStorage storageArea,
DOMString filename,
CameraStartRecordingCallback onSuccess,
optional CameraErrorCallback onError);
Promise<void> startRecording(CameraStartRecordingOptions aOptions,
DeviceStorage storageArea,
DOMString filename,
optional CameraStartRecordingCallback onSuccess,
optional CameraErrorCallback onError);
/* stop precording video. */
[Throws]
@ -337,8 +388,8 @@ interface CameraControl : MediaStream
once this is called, the camera control object is to be considered
defunct; a new instance will need to be created to access the camera. */
[Throws]
void release(optional CameraReleaseCallback onSuccess,
optional CameraErrorCallback onError);
Promise<void> release(optional CameraReleaseCallback onSuccess,
optional CameraErrorCallback onError);
/* changes the camera configuration on the fly;
'configuration' is of type CameraConfiguration.
@ -349,9 +400,18 @@ interface CameraControl : MediaStream
a required argument must be optional"
*/
[Throws]
void setConfiguration(optional CameraConfiguration configuration,
optional CameraSetConfigurationCallback onSuccess,
optional CameraErrorCallback onError);
Promise<CameraConfiguration> setConfiguration(optional CameraConfiguration configuration,
optional CameraSetConfigurationCallback onSuccess,
optional CameraErrorCallback onError);
/* the event dispatched when the camera is successfully configured.
event type is CameraConfigurationEvent where:
'mode' is the selected camera mode
'recorderProfile' is the selected profile
'width' contains the preview width
'height' contains the preview height */
attribute EventHandler onconfigurationchange;
/* if focusMode is set to either 'continuous-picture' or 'continuous-video',
then calling autoFocus() will trigger its onSuccess callback immediately
@ -453,4 +513,8 @@ partial interface CameraControl
detected, the callback is invoked with an empty sequence. */
[Pref="camera.control.face_detection.enabled"]
attribute CameraFaceDetectionCallback? onFacesDetected;
/* CameraFacesDetectedEvent */
[Pref="camera.control.face_detection.enabled"]
attribute EventHandler onfacesdetected;
};

View File

@ -0,0 +1,18 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
[Pref="camera.control.face_detection.enabled",
Func="Navigator::HasCameraSupport",
Constructor(DOMString type, optional CameraFacesDetectedEventInit eventInitDict)]
interface CameraFacesDetectedEvent : Event
{
[Pure, Cached]
readonly attribute sequence<CameraDetectedFace>? faces;
};
dictionary CameraFacesDetectedEventInit : EventInit
{
sequence<CameraDetectedFace>? faces = null;
};

View File

@ -36,10 +36,10 @@ interface CameraManager
identifiers returned by getListOfCameras() below.
*/
[Throws]
void getCamera(DOMString camera,
CameraConfiguration initialConfiguration,
GetCameraCallback callback,
optional CameraErrorCallback errorCallback);
Promise<CameraGetPromiseData> getCamera(DOMString camera,
optional CameraConfiguration initialConfiguration,
optional GetCameraCallback callback,
optional CameraErrorCallback errorCallback);
/* return an array of camera identifiers, e.g.
[ "front", "back" ]

View File

@ -0,0 +1,16 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
[Func="Navigator::HasCameraSupport",
Constructor(DOMString type, optional CameraStateChangeEventInit eventInitDict)]
interface CameraStateChangeEvent : Event
{
readonly attribute DOMString newState;
};
dictionary CameraStateChangeEventInit : EventInit
{
DOMString newState = "";
};

View File

@ -0,0 +1,12 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/.
*/
dictionary CameraGetPromiseData
{
CameraControl? camera = null;
CameraConfiguration configuration;
};

View File

@ -57,6 +57,7 @@ WEBIDL_FILES = [
'CameraCapabilities.webidl',
'CameraControl.webidl',
'CameraManager.webidl',
'CameraUtil.webidl',
'CanvasRenderingContext2D.webidl',
'CaretPosition.webidl',
'CDATASection.webidl',
@ -643,6 +644,9 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'BlobEvent.webidl',
'CallEvent.webidl',
'CallGroupErrorEvent.webidl',
'CameraConfigurationEvent.webidl',
'CameraFacesDetectedEvent.webidl',
'CameraStateChangeEvent.webidl',
'CFStateChangeEvent.webidl',
'CloseEvent.webidl',
'DataErrorEvent.webidl',