diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index e0a2c866b4e8..ee9994f26bf8 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 08a521270b18..df15da588e41 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index e0a2c866b4e8..ee9994f26bf8 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 84d84441b554..d305018e6a37 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "b9f86acc02f79d37ad0e10e9ee3b9cc75f870126", + "revision": "973662e5df416f77550a9cdbc7ec597c623489c3", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 6aa42f13b079..9a9ce833d390 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 650f52dfdd31..da75692a708b 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 22d372ecb91e..f740c1f9bc88 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index b5d049a37684..940a2629df33 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index cb3bfe05d7ea..a9fcbeaaecbe 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index a5c9f83e4f70..a5b860a44330 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/content/media/webrtc/MediaEngineWebRTC.h b/content/media/webrtc/MediaEngineWebRTC.h index cb06a60cfecf..05b88ed8f429 100644 --- a/content/media/webrtc/MediaEngineWebRTC.h +++ b/content/media/webrtc/MediaEngineWebRTC.h @@ -154,12 +154,16 @@ public: return false; } +#ifndef MOZ_B2G_CAMERA NS_DECL_THREADSAFE_ISUPPORTS -#ifdef MOZ_B2G_CAMERA +#else + // We are subclassed from CameraControlListener, which implements a + // threadsafe reference-count for us. + NS_DECL_ISUPPORTS_INHERITED + void OnHardwareStateChange(HardwareState aState); - void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration); bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); - void OnError(CameraErrorContext aContext, const nsACString& aError); + void OnError(CameraErrorContext aContext, CameraError aError); void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType); void AllocImpl(); diff --git a/content/media/webrtc/MediaEngineWebRTCVideo.cpp b/content/media/webrtc/MediaEngineWebRTCVideo.cpp index 7d772801ce66..49d2cbd3c8d2 100644 --- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp +++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp @@ -25,7 +25,13 @@ extern PRLogModuleInfo* GetMediaManagerLog(); /** * Webrtc video source. */ +#ifndef MOZ_B2G_CAMERA NS_IMPL_ISUPPORTS1(MediaEngineWebRTCVideoSource, nsIRunnable) +#else +NS_IMPL_QUERY_INTERFACE1(MediaEngineWebRTCVideoSource, nsIRunnable) +NS_IMPL_ADDREF_INHERITED(MediaEngineWebRTCVideoSource, CameraControlListener) +NS_IMPL_RELEASE_INHERITED(MediaEngineWebRTCVideoSource, CameraControlListener) +#endif // ViEExternalRenderer Callback. #ifndef MOZ_B2G_CAMERA @@ -494,15 +500,21 @@ void MediaEngineWebRTCVideoSource::AllocImpl() { MOZ_ASSERT(NS_IsMainThread()); - mCameraControl = ICameraControl::Create(mCaptureIndex, nullptr); - mCameraControl->AddListener(this); + mCameraControl = ICameraControl::Create(mCaptureIndex); + if (mCameraControl) { + mState = kAllocated; + // Add this as a listener for CameraControl events. We don't need + // to explicitly remove this--destroying the CameraControl object + // in DeallocImpl() will do that for us. + mCameraControl->AddListener(this); + } + mCallbackMonitor.Notify(); } void MediaEngineWebRTCVideoSource::DeallocImpl() { MOZ_ASSERT(NS_IsMainThread()); - mCameraControl->ReleaseHardware(); mCameraControl = nullptr; } @@ -514,7 +526,7 @@ MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) { config.mMode = ICameraControl::kPictureMode; config.mPreviewSize.width = aCapability.width; config.mPreviewSize.height = aCapability.height; - mCameraControl->SetConfiguration(config); + mCameraControl->Start(&config); mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, config.mPreviewSize); } @@ -522,7 +534,7 @@ void MediaEngineWebRTCVideoSource::StopImpl() { MOZ_ASSERT(NS_IsMainThread()); - mCameraControl->StopPreview(); + mCameraControl->Stop(); } void @@ -535,25 +547,23 @@ void MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState) { ReentrantMonitorAutoEnter sync(mCallbackMonitor); - if (aState == CameraControlListener::kHardwareOpen) { - mState = kAllocated; + if (aState == CameraControlListener::kHardwareClosed) { + // When the first CameraControl listener is added, it gets pushed + // the current state of the camera--normally 'closed'. We only + // pay attention to that state if we've progressed out of the + // allocated state. + if (mState != kAllocated) { + mState = kReleased; + mCallbackMonitor.Notify(); + } } else { - mState = kReleased; - mCameraControl->RemoveListener(this); + mState = kStarted; + mCallbackMonitor.Notify(); } - mCallbackMonitor.Notify(); } void -MediaEngineWebRTCVideoSource::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) -{ - ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mState = kStarted; - mCallbackMonitor.Notify(); -} - -void -MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, const nsACString& aError) +MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, CameraError aError) { ReentrantMonitorAutoEnter sync(mCallbackMonitor); mCallbackMonitor.Notify(); diff --git a/dom/browser-element/mochitest/test_browserElement_NoPref.html b/dom/browser-element/mochitest/test_browserElement_NoPref.html index 12c289e6e3fa..d7924850f96b 100644 --- a/dom/browser-element/mochitest/test_browserElement_NoPref.html +++ b/dom/browser-element/mochitest/test_browserElement_NoPref.html @@ -41,7 +41,7 @@ function runTest() { iframe.src = browserElementTestHelpers.emptyPage1; } -addEventListener('load', function() { SimpleTest.executeSoon(runTest); }); +addEventListener('testready', runTest); diff --git a/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html b/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html index 1d9971577c1c..6e71280372ca 100644 --- a/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html +++ b/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html @@ -42,7 +42,7 @@ function runTest() { iframe.src = browserElementTestHelpers.emptyPage1; } -addEventListener('load', function() { SimpleTest.executeSoon(runTest); }); +addEventListener('testready', runTest); diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index 8896ddcee527..17902486645b 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -235,12 +235,33 @@ CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext, RwLockAutoEnterRead lock(mListenerLock); #ifdef PR_LOGGING - const char* error[] = { "camera-service-failed", "unknown" }; - if (static_cast(aError) < sizeof(error) / sizeof(error[0])) { - DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext=%u, msg='%s'\n", - aContext, error[aError]); + const char* error[] = { + "api-failed", + "init-failed", + "invalid-configuration", + "service-failed", + "set-picture-size-failred", + "set-thumbnail-size-failed", + "unknown" + }; + const char* context[] = { + "StartCamera", + "StopCamera", + "AutoFocus", + "TakePicture", + "StartRecording", + "StopRecording", + "SetConfiguration", + "StartPreview", + "StopPreview", + "Unspecified" + }; + if (static_cast(aError) < sizeof(error) / sizeof(error[0]) && + static_cast(aContext) < sizeof(context) / sizeof(context[0])) { + DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext='%s' (%u), aError='%s' (%u)\n", + context[aContext], aContext, error[aError], aError); } else { - DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, unknown error=%d\n", + DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, aError=%d\n", aContext, aError); } #endif @@ -293,6 +314,42 @@ protected: CameraControlListener::CameraErrorContext mContext; }; +nsresult +CameraControlImpl::Start(const Configuration* aConfig) +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext, + const Configuration* aConfig) + : ControlMessage(aCameraControl, aContext) + , mHaveInitialConfig(false) + { + if (aConfig) { + mConfig = *aConfig; + mHaveInitialConfig = true; + } + } + + nsresult + RunImpl() MOZ_OVERRIDE + { + if (mHaveInitialConfig) { + return mCameraControl->StartImpl(&mConfig); + } + return mCameraControl->StartImpl(); + } + + protected: + bool mHaveInitialConfig; + Configuration mConfig; + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInStartCamera, aConfig), NS_DISPATCH_NORMAL); +} + nsresult CameraControlImpl::SetConfiguration(const Configuration& aConfig) { @@ -475,7 +532,7 @@ CameraControlImpl::StopPreview() } nsresult -CameraControlImpl::ReleaseHardware() +CameraControlImpl::Stop() { class Message : public ControlMessage { @@ -488,12 +545,12 @@ CameraControlImpl::ReleaseHardware() nsresult RunImpl() MOZ_OVERRIDE { - return mCameraControl->ReleaseHardwareImpl(); + return mCameraControl->StopImpl(); } }; return mCameraThread->Dispatch( - new Message(this, CameraControlListener::kInReleaseHardware), NS_DISPATCH_NORMAL); + new Message(this, CameraControlListener::kInStopCamera), NS_DISPATCH_NORMAL); } class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage @@ -515,6 +572,7 @@ CameraControlImpl::AddListenerImpl(already_AddRefed aList RwLockAutoEnterWrite lock(mListenerLock); CameraControlListener* l = *mListeners.AppendElement() = aListener; + DOM_CAMERA_LOGI("Added camera control listener %p\n", l); // Update the newly-added listener's state l->OnConfigurationChange(mCurrentConfiguration); @@ -551,6 +609,7 @@ CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener) nsRefPtr l(aListener); mListeners.RemoveElement(l); + DOM_CAMERA_LOGI("Removed camera control listener %p\n", l.get()); // XXXmikeh - do we want to notify the listener that it has been removed? } diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index cb0a8ea57545..93e6ff40f93a 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -36,6 +36,9 @@ public: virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE; virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE; + virtual nsresult Start(const Configuration* aConfig = nullptr) MOZ_OVERRIDE; + virtual nsresult Stop() MOZ_OVERRIDE; + virtual nsresult SetConfiguration(const Configuration& aConfig) MOZ_OVERRIDE; virtual nsresult StartPreview() MOZ_OVERRIDE; virtual nsresult StopPreview() MOZ_OVERRIDE; @@ -44,7 +47,6 @@ public: virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor, const StartRecordingOptions* aOptions) MOZ_OVERRIDE; virtual nsresult StopRecording() MOZ_OVERRIDE; - virtual nsresult ReleaseHardware() MOZ_OVERRIDE; already_AddRefed GetRecorderProfileManager(); uint32_t GetCameraId() { return mCameraId; } @@ -63,7 +65,7 @@ protected: bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); void OnRecorderStateChange(CameraControlListener::RecorderState aState, - int32_t aStatus, int32_t aTrackNumber); + int32_t aStatus = -1, int32_t aTrackNumber = -1); void OnPreviewStateChange(CameraControlListener::PreviewState aState); void OnHardwareStateChange(CameraControlListener::HardwareState aState); void OnConfigurationChange(); @@ -90,6 +92,8 @@ protected: class ControlMessage; class ListenerMessage; + virtual nsresult StartImpl(const Configuration* aConfig = nullptr) = 0; + virtual nsresult StopImpl() = 0; virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0; virtual nsresult StartPreviewImpl() = 0; virtual nsresult StopPreviewImpl() = 0; @@ -100,7 +104,6 @@ protected: virtual nsresult StopRecordingImpl() = 0; virtual nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; - virtual nsresult ReleaseHardwareImpl() = 0; virtual already_AddRefed GetRecorderProfileManagerImpl() = 0; void OnShutterInternal(); diff --git a/dom/camera/CameraControlListener.h b/dom/camera/CameraControlListener.h index 76065ddb3003..c7a58fc16d0b 100644 --- a/dom/camera/CameraControlListener.h +++ b/dom/camera/CameraControlListener.h @@ -17,7 +17,15 @@ namespace layers { class CameraControlListener { public: - virtual ~CameraControlListener() { } + CameraControlListener() + { + MOZ_COUNT_CTOR(CameraControlListener); + } + + virtual ~CameraControlListener() + { + MOZ_COUNT_DTOR(CameraControlListener); + } NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener); @@ -71,13 +79,13 @@ public: enum CameraErrorContext { - kInGetCamera, + kInStartCamera, + kInStopCamera, kInAutoFocus, kInTakePicture, kInStartRecording, kInStopRecording, kInSetConfiguration, - kInReleaseHardware, kInStartPreview, kInStopPreview, kInUnspecified diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index 8b550923fe52..c9e750759264 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -201,7 +201,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight; config.mRecorderProfile = aInitialConfig.mRecorderProfile; - mCameraControl = ICameraControl::Create(aCameraId, &config); + mCameraControl = ICameraControl::Create(aCameraId); mCurrentConfiguration = initialConfig.forget(); // Attach our DOM-facing media stream to our viewfinder stream. @@ -214,6 +214,13 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, // Register a listener for camera events. mListener = new DOMCameraControlListener(this, mInput); mCameraControl->AddListener(mListener); + + // Start the camera... + nsresult rv = mCameraControl->Start(&config); + if (NS_FAILED(rv)) { + mListener->OnError(DOMCameraControlListener::kInStartCamera, + DOMCameraControlListener::kErrorApiFailed); + } } nsDOMCameraControl::~nsDOMCameraControl() @@ -911,7 +918,7 @@ nsDOMCameraControl::ReleaseHardware(const OptionalReleaseHardware(); + aRv = mCameraControl->Stop(); } void @@ -1157,11 +1164,16 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, nsCOMPtr* errorCb; switch (aContext) { - case CameraControlListener::kInGetCamera: + case CameraControlListener::kInStartCamera: mGetCameraOnSuccessCb = nullptr; errorCb = &mGetCameraOnErrorCb; break; + case CameraControlListener::kInStopCamera: + mReleaseOnSuccessCb = nullptr; + errorCb = &mReleaseOnErrorCb; + break; + case CameraControlListener::kInSetConfiguration: mSetConfigurationOnSuccessCb = nullptr; errorCb = &mSetConfigurationOnErrorCb; @@ -1182,11 +1194,6 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, errorCb = &mStartRecordingOnErrorCb; break; - case CameraControlListener::kInReleaseHardware: - mReleaseOnSuccessCb = nullptr; - errorCb = &mReleaseOnErrorCb; - break; - case CameraControlListener::kInStopRecording: NS_WARNING("Failed to stop recording (which shouldn't happen)!"); MOZ_CRASH(); diff --git a/dom/camera/DOMCameraControlListener.cpp b/dom/camera/DOMCameraControlListener.cpp index de91c59043f3..4918a2e76588 100644 --- a/dom/camera/DOMCameraControlListener.cpp +++ b/dom/camera/DOMCameraControlListener.cpp @@ -13,14 +13,33 @@ using namespace mozilla; using namespace mozilla::dom; +DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, + CameraPreviewMediaStream* aStream) + : mDOMCameraControl(new nsMainThreadPtrHolder(aDOMCameraControl)) + , mStream(aStream) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p, camera=%p, stream=%p\n", + __func__, __LINE__, this, aDOMCameraControl, aStream); +} + +DOMCameraControlListener::~DOMCameraControlListener() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + // Boilerplate callback runnable class DOMCameraControlListener::DOMCallback : public nsRunnable { public: DOMCallback(nsMainThreadPtrHandle aDOMCameraControl) : mDOMCameraControl(aDOMCameraControl) - { } - virtual ~DOMCallback() { } + { + MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback); + } + virtual ~DOMCallback() + { + MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback); + } virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0; @@ -178,7 +197,8 @@ DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguratio break; default: - MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!"); + DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n"); + return; } // Map CameraControl parameters to their DOM-facing equivalents @@ -298,7 +318,7 @@ DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aErro , mError(aError) { } - void + virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE { nsString error; diff --git a/dom/camera/DOMCameraControlListener.h b/dom/camera/DOMCameraControlListener.h index c8638f7a9f10..5840a347f55e 100644 --- a/dom/camera/DOMCameraControlListener.h +++ b/dom/camera/DOMCameraControlListener.h @@ -15,29 +15,28 @@ class CameraPreviewMediaStream; class DOMCameraControlListener : public CameraControlListener { +public: + DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream); + + virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) MOZ_OVERRIDE; + virtual void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) MOZ_OVERRIDE; + + virtual void OnHardwareStateChange(HardwareState aState) MOZ_OVERRIDE; + virtual void OnPreviewStateChange(PreviewState aState) MOZ_OVERRIDE; + virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) MOZ_OVERRIDE; + virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) MOZ_OVERRIDE; + virtual void OnShutter() MOZ_OVERRIDE; + virtual bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) MOZ_OVERRIDE; + virtual void OnError(CameraErrorContext aContext, CameraError aError) MOZ_OVERRIDE; + protected: + virtual ~DOMCameraControlListener(); + nsMainThreadPtrHandle mDOMCameraControl; CameraPreviewMediaStream* mStream; class DOMCallback; -public: - DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream) - : mDOMCameraControl(new nsMainThreadPtrHolder(aDOMCameraControl)) - , mStream(aStream) - { } - - void OnAutoFocusComplete(bool aAutoFocusSucceeded); - void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType); - - void OnHardwareStateChange(HardwareState aState); - void OnPreviewStateChange(PreviewState aState); - void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum); - void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration); - void OnShutter(); - bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); - void OnError(CameraErrorContext aContext, CameraError aError); - private: DOMCameraControlListener(const DOMCameraControlListener&) MOZ_DELETE; DOMCameraControlListener& operator=(const DOMCameraControlListener&) MOZ_DELETE; diff --git a/dom/camera/FallbackCameraManager.cpp b/dom/camera/FallbackCameraManager.cpp index 513db97d35c6..720b3931a4eb 100644 --- a/dom/camera/FallbackCameraManager.cpp +++ b/dom/camera/FallbackCameraManager.cpp @@ -26,7 +26,7 @@ ICameraControl::GetListOfCameras(nsTArray& aList) } already_AddRefed -ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig) +ICameraControl::Create(uint32_t aCameraId) { return nullptr; } diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index de4973b8ac0b..db47acbb1188 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -77,90 +77,50 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId) } nsresult -nsGonkCameraControl::Init(const Configuration* aInitialConfig) +nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig) { - class InitGonkCameraControl : public nsRunnable - { - public: - InitGonkCameraControl(nsGonkCameraControl* aCameraControl, - const Configuration* aConfig) - : mCameraControl(aCameraControl) - , mHaveInitialConfig(false) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - if (aConfig) { - mConfig = *aConfig; - mHaveInitialConfig = true; - } + /** + * For initialization, we try to return the camera control to the upper + * upper layer (i.e. the DOM) as quickly as possible. To do this, the + * camera is initialized in the following stages: + * + * 0. Initialize() initializes the hardware; + * 1. SetConfigurationInternal() does the minimal configuration + * required so that we can start the preview -and- report a valid + * configuration to the upper layer; + * 2. OnHardwareStateChange() reports that the hardware is ready, + * which the upper (e.g. DOM) layer can (and does) use to return + * the camera control object; + * 3. StartPreviewImpl() starts the flow of preview frames from the + * camera hardware. + * + * The intent of the above flow is to let the Main Thread do as much work + * up-front as possible without waiting for blocking Camera Thread calls + * to complete. + */ + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + + nsresult rv = Initialize(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aInitialConfig) { + rv = SetConfigurationInternal(*aInitialConfig); + if (NS_WARN_IF(NS_FAILED(rv))) { + // The initial configuration failed, close up the hardware + StopImpl(); + return rv; } + } - ~InitGonkCameraControl() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - /** - * For initialization, we try to return the camera control to the upper - * upper layer (i.e. the DOM) as quickly as possible. To do this, the - * camera is initialized in the following stages: - * - * 0. InitImpl() initializes the hardware; - * 1. SetConfigurationInternal() does the minimal configuration - * required so that we can start the preview -and- report a valid - * configuration to the upper layer; - * 2. OnHardwareStateChange() reports that the hardware is ready, - * which the upper layer can (and does) use to return the camera - * control object; - * 3. StartPreviewImpl() starts the flow of preview frames from the - * camera hardware. - * - * The intent of the above flow is to let the Main Thread do as much work - * up-front as possible without waiting for blocking Camera Thread calls - * to complete. - */ - NS_IMETHODIMP - Run() MOZ_OVERRIDE - { - nsresult rv = mCameraControl->InitImpl(); - if (NS_FAILED(rv)) { - mCameraControl->OnError(CameraControlListener::kInGetCamera, - CameraControlListener::kErrorInitFailed); - // The hardware failed to initialize, so close it up - mCameraControl->ReleaseHardware(); - return rv; - } - - if (mHaveInitialConfig) { - rv = mCameraControl->SetConfigurationInternal(mConfig); - if (NS_FAILED(rv)) { - mCameraControl->OnError(CameraControlListener::kInGetCamera, - CameraControlListener::kErrorInvalidConfiguration); - // The initial configuration failed, close up the hardware - mCameraControl->ReleaseHardware(); - return rv; - } - } - - mCameraControl->OnHardwareStateChange(CameraControlListener::kHardwareOpen); - return mCameraControl->StartPreviewImpl(); - } - - protected: - nsRefPtr mCameraControl; - Configuration mConfig; - bool mHaveInitialConfig; - }; - - // Initialization is carried out on the camera thread. - return mCameraThread->Dispatch( - new InitGonkCameraControl(this, aInitialConfig), NS_DISPATCH_NORMAL); + OnHardwareStateChange(CameraControlListener::kHardwareOpen); + return StartPreviewImpl(); } nsresult -nsGonkCameraControl::InitImpl() +nsGonkCameraControl::Initialize() { - MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); - mCameraHw = GonkCameraHardware::Connect(this, mCameraId); if (!mCameraHw.get()) { DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this); @@ -211,7 +171,7 @@ nsGonkCameraControl::~nsGonkCameraControl() { DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get()); - ReleaseHardwareImpl(); + StopImpl(); DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); } @@ -255,11 +215,18 @@ nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig) MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); // Stop any currently running preview - StopPreviewImpl(); + nsresult rv = PausePreview(); + if (NS_FAILED(rv)) { + // warn, but plow ahead + NS_WARNING("PausePreview() in SetConfigurationImpl() failed"); + } DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = SetConfigurationInternal(aConfig); - NS_ENSURE_SUCCESS(rv, rv); + rv = SetConfigurationInternal(aConfig); + if (NS_WARN_IF(NS_FAILED(rv))) { + StopPreviewImpl(); + return rv; + } // Restart the preview DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); @@ -546,11 +513,22 @@ nsGonkCameraControl::StopPreviewImpl() DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this); mCameraHw->StopPreview(); - OnPreviewStateChange(CameraControlListener::kPreviewStopped); return NS_OK; } +nsresult +nsGonkCameraControl::PausePreview() +{ + RETURN_IF_NO_CAMERA_HW(); + + DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this); + + mCameraHw->StopPreview(); + OnPreviewStateChange(CameraControlListener::kPreviewPaused); + return NS_OK; +} + nsresult nsGonkCameraControl::AutoFocusImpl(bool aCancelExistingCall) { @@ -892,7 +870,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri return NS_ERROR_FAILURE; } - OnRecorderStateChange(CameraControlListener::kRecorderStarted, -1, -1); + OnRecorderStateChange(CameraControlListener::kRecorderStarted); return NS_OK; } @@ -929,7 +907,7 @@ nsGonkCameraControl::StopRecordingImpl() mRecorder->stop(); mRecorder = nullptr; - OnRecorderStateChange(CameraControlListener::kRecorderStopped, -1, -1); + OnRecorderStateChange(CameraControlListener::kRecorderStopped); // notify DeviceStorage that the new video file is closed and ready return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL); @@ -1339,15 +1317,16 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation, int64_t aMaxFileSize } nsresult -nsGonkCameraControl::ReleaseHardwareImpl() +nsGonkCameraControl::StopImpl() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); // if we're recording, stop recording if (mRecorder) { - DOM_CAMERA_LOGI("shutting down existing video recorder\n"); + DOM_CAMERA_LOGI("Stopping existing video recorder\n"); mRecorder->stop(); mRecorder = nullptr; + OnRecorderStateChange(CameraControlListener::kRecorderStopped); } // stop the preview diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index c16950ff5f87..c62aec903cd6 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Mozilla Foundation + * Copyright (C) 2012-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,8 +47,6 @@ class nsGonkCameraControl : public CameraControlImpl { public: nsGonkCameraControl(uint32_t aCameraId); - nsresult Init(const Configuration* aInitialConfig); - nsresult InitImpl(); void OnAutoFocusComplete(bool aSuccess); void OnTakePictureComplete(uint8_t* aData, uint32_t aLength); @@ -92,6 +90,10 @@ protected: virtual void BeginBatchParameterSet() MOZ_OVERRIDE; virtual void EndBatchParameterSet() MOZ_OVERRIDE; + virtual nsresult StartImpl(const Configuration* aInitialConfig = nullptr) MOZ_OVERRIDE; + virtual nsresult StopImpl() MOZ_OVERRIDE; + nsresult Initialize(); + virtual nsresult SetConfigurationImpl(const Configuration& aConfig) MOZ_OVERRIDE; nsresult SetConfigurationInternal(const Configuration& aConfig); nsresult SetPictureConfiguration(const Configuration& aConfig); @@ -108,13 +110,13 @@ protected: virtual nsresult StopRecordingImpl() MOZ_OVERRIDE; virtual nsresult PushParametersImpl() MOZ_OVERRIDE; virtual nsresult PullParametersImpl() MOZ_OVERRIDE; - virtual nsresult ReleaseHardwareImpl() MOZ_OVERRIDE; virtual already_AddRefed GetRecorderProfileManagerImpl() MOZ_OVERRIDE; already_AddRefed GetGonkRecorderProfileManager(); nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs); nsresult SetupVideoMode(const nsAString& aProfile); nsresult SetPreviewSize(const Size& aSize); + nsresult PausePreview(); friend class SetPictureSize; friend class SetThumbnailSize; diff --git a/dom/camera/GonkCameraHwMgr.cpp b/dom/camera/GonkCameraHwMgr.cpp index e93e8caa1569..c71a4634e2c7 100644 --- a/dom/camera/GonkCameraHwMgr.cpp +++ b/dom/camera/GonkCameraHwMgr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Mozilla Foundation + * Copyright (C) 2012-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,14 @@ */ #include "GonkCameraHwMgr.h" +#include "TestGonkCameraHardware.h" #include #include #include "base/basictypes.h" #include "nsDebug.h" +#include "mozilla/Preferences.h" #include "GonkCameraControl.h" #include "GonkNativeWindow.h" #include "CameraCommon.h" @@ -35,11 +37,9 @@ GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, ui , mNumFrames(0) , mCamera(aCamera) , mTarget(aTarget) - , mInitialized(false) , mSensorOrientation(0) { DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget); - Init(); } void @@ -127,7 +127,7 @@ GonkCameraHardware::postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, cons } } -void +nsresult GonkCameraHardware::Init() { DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this); @@ -136,7 +136,7 @@ GonkCameraHardware::Init() int rv = Camera::getCameraInfo(mCameraId, &info); if (rv != 0) { DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId); - return; + return NS_ERROR_FAILURE; } mRawSensorOrientation = info.orientation; @@ -168,7 +168,8 @@ GonkCameraHardware::Init() #else mCamera->setPreviewTexture(mNativeWindow); #endif - mInitialized = true; + + return NS_OK; } sp @@ -180,18 +181,33 @@ GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCam sp camera = Camera::connect(aCameraId); #endif - if (camera.get() == nullptr) { return nullptr; } - sp cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera); + + const nsAdoptingCString& test = + mozilla::Preferences::GetCString("camera.control.test.enabled"); + sp cameraHardware; + if (test.EqualsASCII("hardware")) { + NS_WARNING("Using test Gonk hardware layer"); + cameraHardware = new TestGonkCameraHardware(aTarget, aCameraId, camera); + } else { + cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera); + } + + nsresult rv = cameraHardware->Init(); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to initialize camera hardware (0x%X)\n", rv); + return nullptr; + } + return cameraHardware; - } +} void GonkCameraHardware::Close() { - DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this ); + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this); mClosing = true; mCamera->stopPreview(); @@ -208,7 +224,7 @@ GonkCameraHardware::Close() GonkCameraHardware::~GonkCameraHardware() { - DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this ); + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this); mCamera.clear(); mNativeWindow.clear(); @@ -308,7 +324,7 @@ void GonkCameraHardware::StopPreview() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - return mCamera->stopPreview(); + mCamera->stopPreview(); } int diff --git a/dom/camera/GonkCameraHwMgr.h b/dom/camera/GonkCameraHwMgr.h index 948eeed05797..936f6b278d6e 100644 --- a/dom/camera/GonkCameraHwMgr.h +++ b/dom/camera/GonkCameraHwMgr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Mozilla Foundation + * Copyright (C) 2012-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,14 +43,14 @@ class GonkCameraHardware : public GonkNativeWindowNewFrameCallback protected: GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp& aCamera); virtual ~GonkCameraHardware(); - void Init(); + virtual nsresult Init(); public: - static sp Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId); - void Close(); + static sp Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId); + virtual void Close(); // derived from GonkNativeWindowNewFrameCallback - virtual void OnNewFrame() MOZ_OVERRIDE; + virtual void OnNewFrame() MOZ_OVERRIDE; // derived from CameraListener virtual void notify(int32_t aMsgType, int32_t ext1, int32_t ext2); @@ -75,26 +75,25 @@ public: RAW_SENSOR_ORIENTATION, OFFSET_SENSOR_ORIENTATION }; - int GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION); + virtual int GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION); - int AutoFocus(); - void CancelAutoFocus(); - int TakePicture(); - void CancelTakePicture(); - int StartPreview(); - void StopPreview(); - int PushParameters(const mozilla::GonkCameraParameters& aParams); - int PushParameters(const CameraParameters& aParams); - nsresult PullParameters(mozilla::GonkCameraParameters& aParams); - void PullParameters(CameraParameters& aParams); - int StartRecording(); - int StopRecording(); - int SetListener(const sp& aListener); - void ReleaseRecordingFrame(const sp& aFrame); - int StoreMetaDataInBuffers(bool aEnabled); + virtual int AutoFocus(); + virtual void CancelAutoFocus(); + virtual int TakePicture(); + virtual void CancelTakePicture(); + virtual int StartPreview(); + virtual void StopPreview(); + virtual int PushParameters(const mozilla::GonkCameraParameters& aParams); + virtual int PushParameters(const CameraParameters& aParams); + virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams); + virtual void PullParameters(CameraParameters& aParams); + virtual int StartRecording(); + virtual int StopRecording(); + virtual int SetListener(const sp& aListener); + virtual void ReleaseRecordingFrame(const sp& aFrame); + virtual int StoreMetaDataInBuffers(bool aEnabled); protected: - uint32_t mCameraId; bool mClosing; uint32_t mNumFrames; @@ -102,15 +101,9 @@ protected: mozilla::nsGonkCameraControl* mTarget; sp mNativeWindow; sp mListener; - bool mInitialized; int mRawSensorOrientation; int mSensorOrientation; - bool IsInitialized() - { - return mInitialized; - } - private: GonkCameraHardware(const GonkCameraHardware&) MOZ_DELETE; GonkCameraHardware& operator=(const GonkCameraHardware&) MOZ_DELETE; diff --git a/dom/camera/GonkCameraManager.cpp b/dom/camera/GonkCameraManager.cpp index 46d7bacde161..80631383feaa 100644 --- a/dom/camera/GonkCameraManager.cpp +++ b/dom/camera/GonkCameraManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Mozilla Foundation + * Copyright (C) 2012-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -115,18 +115,8 @@ ICameraControl::GetListOfCameras(nsTArray& aList) // implementation-specific camera factory already_AddRefed -ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig) +ICameraControl::Create(uint32_t aCameraId) { - if (aInitialConfig) { - DOM_CAMERA_LOGI("Creating camera %d control, initial mode '%s'\n", - aCameraId, aInitialConfig->mMode == kVideoMode ? "video" : "picture"); - } else { - DOM_CAMERA_LOGI("Creating camera %d control, no intial configuration\n", aCameraId); - } - nsRefPtr control = new nsGonkCameraControl(aCameraId); - nsresult rv = control->Init(aInitialConfig); - NS_ENSURE_SUCCESS(rv, nullptr); - return control.forget(); } diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 0c68d1571f7e..c6ca140743c1 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -117,8 +117,11 @@ public: Size mPreviewSize; nsString mRecorderProfile; }; - static already_AddRefed Create(uint32_t aCameraId, - const Configuration* aInitialConfig); + static already_AddRefed Create(uint32_t aCameraId); + + virtual nsresult Start(const Configuration* aInitialConfig = nullptr) = 0; + virtual nsresult Stop() = 0; + virtual nsresult SetConfiguration(const Configuration& aConfig) = 0; virtual void AddListener(CameraControlListener* aListener) = 0; @@ -131,7 +134,6 @@ public: virtual nsresult StartRecording(DeviceStorageFileDescriptor *aFileDescriptor, const StartRecordingOptions* aOptions = nullptr) = 0; virtual nsresult StopRecording() = 0; - virtual nsresult ReleaseHardware() = 0; virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0; virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0; diff --git a/dom/camera/TestGonkCameraHardware.cpp b/dom/camera/TestGonkCameraHardware.cpp new file mode 100644 index 000000000000..05ed6c6a850c --- /dev/null +++ b/dom/camera/TestGonkCameraHardware.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013-2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestGonkCameraHardware.h" + +#include "mozilla/Preferences.h" +#include "nsThreadUtils.h" + +using namespace android; +using namespace mozilla; + +TestGonkCameraHardware::TestGonkCameraHardware(nsGonkCameraControl* aTarget, + uint32_t aCameraId, + const sp& aCamera) + : GonkCameraHardware(aTarget, aCameraId, aCamera) +{ + DOM_CAMERA_LOGA("+===== Created TestGonkCameraHardware =====+\n"); + DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", + __func__, __LINE__, this, aTarget); + MOZ_COUNT_CTOR(TestGonkCameraHardware); +} + +TestGonkCameraHardware::~TestGonkCameraHardware() +{ + MOZ_COUNT_DTOR(TestGonkCameraHardware); + DOM_CAMERA_LOGA("+===== Destroyed TestGonkCameraHardware =====+\n"); +} + +nsresult +TestGonkCameraHardware::Init() +{ + if (IsTestCase("init-failure")) { + return NS_ERROR_FAILURE; + } + + return GonkCameraHardware::Init(); +} + +const nsCString +TestGonkCameraHardware::TestCase() +{ + const nsCString test = Preferences::GetCString("camera.control.test.hardware"); + return test; +} + +bool +TestGonkCameraHardware::IsTestCaseInternal(const char* aTest, const char* aFile, int aLine) +{ + if (TestCase().EqualsASCII(aTest)) { + DOM_CAMERA_LOGA("TestGonkCameraHardware : test-case '%s' (%s:%d)\n", + aTest, aFile, aLine); + return true; + } + + return false; +} + +int +TestGonkCameraHardware::TestCaseError(int aDefaultError) +{ + // for now, just return the default error + return aDefaultError; +} + +int +TestGonkCameraHardware::AutoFocus() +{ + class AutoFocusFailure : public nsRunnable + { + public: + AutoFocusFailure(nsGonkCameraControl* aTarget) + : mTarget(aTarget) + { } + + NS_IMETHODIMP + Run() + { + OnAutoFocusComplete(mTarget, false); + return NS_OK; + } + + protected: + nsGonkCameraControl* mTarget; + }; + + if (IsTestCase("auto-focus-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + if (IsTestCase("auto-focus-process-failure")) { + nsresult rv = NS_DispatchToCurrentThread(new AutoFocusFailure(mTarget)); + if (NS_SUCCEEDED(rv)) { + return OK; + } + DOM_CAMERA_LOGE("Failed to dispatch AutoFocusFailure runnable (0x%08x)\n", rv); + return UNKNOWN_ERROR; + } + + return GonkCameraHardware::AutoFocus(); +} + +int +TestGonkCameraHardware::TakePicture() +{ + class TakePictureFailure : public nsRunnable + { + public: + TakePictureFailure(nsGonkCameraControl* aTarget) + : mTarget(aTarget) + { } + + NS_IMETHODIMP + Run() + { + OnTakePictureError(mTarget); + return NS_OK; + } + + protected: + nsGonkCameraControl* mTarget; + }; + + if (IsTestCase("take-picture-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + if (IsTestCase("take-picture-process-failure")) { + nsresult rv = NS_DispatchToCurrentThread(new TakePictureFailure(mTarget)); + if (NS_SUCCEEDED(rv)) { + return OK; + } + DOM_CAMERA_LOGE("Failed to dispatch TakePictureFailure runnable (0x%08x)\n", rv); + return UNKNOWN_ERROR; + } + + return GonkCameraHardware::TakePicture(); +} + +int +TestGonkCameraHardware::StartPreview() +{ + if (IsTestCase("start-preview-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::StartPreview(); +} + +int +TestGonkCameraHardware::PushParameters(const GonkCameraParameters& aParams) +{ + if (IsTestCase("push-parameters-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::PushParameters(aParams); +} + +nsresult +TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams) +{ + if (IsTestCase("pull-parameters-failure")) { + return static_cast(TestCaseError(UNKNOWN_ERROR)); + } + + return GonkCameraHardware::PullParameters(aParams); +} + +int +TestGonkCameraHardware::StartRecording() +{ + if (IsTestCase("start-recording-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::StartRecording(); +} + +int +TestGonkCameraHardware::StopRecording() +{ + if (IsTestCase("stop-recording-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::StopRecording(); +} + +int +TestGonkCameraHardware::SetListener(const sp& aListener) +{ + if (IsTestCase("set-listener-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::SetListener(aListener); +} + +int +TestGonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled) +{ + if (IsTestCase("store-metadata-in-buffers-failure")) { + return TestCaseError(UNKNOWN_ERROR); + } + + return GonkCameraHardware::StoreMetaDataInBuffers(aEnabled); +} diff --git a/dom/camera/TestGonkCameraHardware.h b/dom/camera/TestGonkCameraHardware.h new file mode 100644 index 000000000000..2ee7d700bb24 --- /dev/null +++ b/dom/camera/TestGonkCameraHardware.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013-2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOM_CAMERA_TESTGONKCAMERAHARDWARE_H +#define DOM_CAMERA_TESTGONKCAMERAHARDWARE_H + +#include "GonkCameraHwMgr.h" + +namespace android { + +class TestGonkCameraHardware : public android::GonkCameraHardware +{ +public: + virtual int AutoFocus() MOZ_OVERRIDE; + virtual int TakePicture() MOZ_OVERRIDE; + virtual int StartPreview() MOZ_OVERRIDE; + virtual int PushParameters(const mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE; + virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE; + virtual int StartRecording() MOZ_OVERRIDE; + virtual int StopRecording() MOZ_OVERRIDE; + virtual int SetListener(const sp& aListener) MOZ_OVERRIDE; + virtual int StoreMetaDataInBuffers(bool aEnabled) MOZ_OVERRIDE; + + virtual int + PushParameters(const CameraParameters& aParams) MOZ_OVERRIDE + { + return GonkCameraHardware::PushParameters(aParams); + } + + virtual void + PullParameters(CameraParameters& aParams) MOZ_OVERRIDE + { + GonkCameraHardware::PullParameters(aParams); + } + + TestGonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, + uint32_t aCameraId, + const sp& aCamera); + virtual ~TestGonkCameraHardware(); + + virtual nsresult Init() MOZ_OVERRIDE; + +protected: + const nsCString TestCase(); + bool IsTestCaseInternal(const char* aTest, const char* aFile, int aLine); + int TestCaseError(int aDefaultError); + +private: + TestGonkCameraHardware(const TestGonkCameraHardware&) MOZ_DELETE; + TestGonkCameraHardware& operator=(const TestGonkCameraHardware&) MOZ_DELETE; +}; + +#define IsTestCase(test) IsTestCaseInternal((test), __FILE__, __LINE__) + +} // namespace android + +#endif // DOM_CAMERA_TESTGONKCAMERAHARDWARE_H diff --git a/dom/camera/moz.build b/dom/camera/moz.build index 02de152bdff7..70729eb71383 100644 --- a/dom/camera/moz.build +++ b/dom/camera/moz.build @@ -33,6 +33,7 @@ if CONFIG['MOZ_B2G_CAMERA']: 'GonkCameraSource.cpp', 'GonkRecorder.cpp', 'GonkRecorderProfiles.cpp', + 'TestGonkCameraHardware.cpp', ] else: SOURCES += [ diff --git a/dom/camera/test/camera_common.js b/dom/camera/test/camera_common.js new file mode 100644 index 000000000000..0718e7f63de3 --- /dev/null +++ b/dom/camera/test/camera_common.js @@ -0,0 +1,105 @@ +var CameraTest = (function() { + 'use strict'; + + /** + * 'camera.control.test.enabled' is queried in Gecko to enable different + * test modes in the camera stack. The only currently-supported setting + * is 'hardware', which wraps the Gonk camera abstraction class in a + * shim class that supports injecting hardware camera API failures into + * the execution path. + * + * The affected API is specified by the 'camera.control.test.hardware' + * pref. Currently supported values should be determined by inspecting + * TestGonkCameraHardware.cpp. + * + * Some API calls are simple: e.g. 'start-recording-failure' will cause + * the DOM-facing startRecording() call to fail. More complex tests like + * 'take-picture-failure' will cause the takePicture() API to fail, while + * 'take-picture-process-failure' will simulate a failure of the + * asynchronous picture-taking process, even if the initial API call + * path seems to have succeeded. + */ + const PREF_TEST_ENABLED = "camera.control.test.enabled"; + const PREF_TEST_HARDWARE = "camera.control.test.hardware"; + var oldTestEnabled; + var oldTestHw; + var testMode; + + function testHardwareSet(test, callback) { + SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() { + var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE); + ise(setTest, test, "Test subtype set to " + setTest); + if (callback) { + callback(setTest); + } + }); + } + + function testHardwareDone(callback) { + testMode = null; + if (oldTestHw) { + SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, oldTestHw]]}, callback); + } else { + SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_HARDWARE]]}, callback); + } + } + + function testBegin(mode, callback) { + SimpleTest.waitForExplicitFinish(); + try { + oldTestEnabled = SpecialPowers.getCharPref(PREF_TEST_ENABLED); + } catch(e) { } + SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, mode]]}, function() { + var setMode = SpecialPowers.getCharPref(PREF_TEST_ENABLED); + ise(setMode, mode, "Test mode set to " + setMode); + if (setMode === "hardware") { + try { + oldTestHw = SpecialPowers.getCharPref(PREF_TEST_HARDWARE); + } catch(e) { } + testMode = { + set: testHardwareSet, + done: testHardwareDone + }; + if (callback) { + callback(testMode); + } + } + }); + } + + function testEnd(callback) { + function allDone(cb) { + function cb2() { + SimpleTest.finish(); + if (cb) { + cb(); + } + } + if (oldTestEnabled) { + SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, oldTestEnabled]]}, cb2); + } else { + SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_ENABLED]]}, cb2); + } + } + + if (testMode) { + testMode.done(function() { + allDone(callback); + }); + testMode = null; + } else { + allDone(function() { + if (callback) { + callback(); + } + }); + } + } + + ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check"); + return { + begin: testBegin, + end: testEnd + }; + +})(); diff --git a/dom/camera/test/mochitest.ini b/dom/camera/test/mochitest.ini index 8bd4a3a65701..187fe40fca67 100644 --- a/dom/camera/test/mochitest.ini +++ b/dom/camera/test/mochitest.ini @@ -1,4 +1,8 @@ [DEFAULT] +support-files = camera_common.js [test_camera.html] [test_camera_2.html] +[test_camera_3.html] +[test_camera_hardware_init_failure.html] +[test_camera_hardware_failures.html] diff --git a/dom/camera/test/test_camera.html b/dom/camera/test/test_camera.html index ed425ee73545..37a1615ad88d 100644 --- a/dom/camera/test/test_camera.html +++ b/dom/camera/test/test_camera.html @@ -7,7 +7,7 @@ - This image is going to load + + + + + +This image is going to load + + + + diff --git a/dom/camera/test/test_camera_hardware_failures.html b/dom/camera/test/test_camera_hardware_failures.html new file mode 100644 index 000000000000..9bc62ddba73f --- /dev/null +++ b/dom/camera/test/test_camera_hardware_failures.html @@ -0,0 +1,145 @@ + + + + + Bug 940424 - Test camera hardware API failure handling + + + + + + + Mozilla Bug 940424 + + This image is going to load + + + + + diff --git a/dom/camera/test/test_camera_hardware_init_failure.html b/dom/camera/test/test_camera_hardware_init_failure.html new file mode 100644 index 000000000000..d5e3ec800492 --- /dev/null +++ b/dom/camera/test/test_camera_hardware_init_failure.html @@ -0,0 +1,53 @@ + + + + + Bug 940424 - Test camera hardware init failure handling + + + + + + + Mozilla Bug 940424 + + This image is going to load + + + + + diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 9b148a2a656e..28ee9b0d9bec 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -1075,8 +1075,8 @@ var WifiManager = (function() { manager.getConfiguredNetworks = function(callback) { wifiCommand.listNetworks(function (reply) { var networks = Object.create(null); - var lines = reply.split("\n"); - if (lines.length === 1) { + var lines = reply ? reply.split("\n") : 0; + if (lines.length <= 1) { // We need to make sure we call the callback even if there are no // configured networks. callback(networks); diff --git a/testing/mochitest/b2g.json b/testing/mochitest/b2g.json index ababd69e7626..03770717ced5 100644 --- a/testing/mochitest/b2g.json +++ b/testing/mochitest/b2g.json @@ -249,9 +249,6 @@ "dom/browser-element/mochitest/test_browserElement_inproc_OpenWindowRejected.html":"", "dom/browser-element/mochitest/test_browserElement_inproc_SecurityChange.html":"", "dom/browser-element/mochitest/test_browserElement_inproc_TargetBlank.html":"", - - "dom/browser-element/mochitest/test_browserElement_NoPref.html":"bug 970290 - Should not send mozbrowserloadstart event.", - "dom/browser-element/mochitest/test_browserElement_NoWhitelist.html":"bug 970290 - Should not send mozbrowserloadstart event.", "dom/browser-element/mochitest/test_browserElement_oop_AppFramePermission.html":"", "dom/browser-element/mochitest/test_browserElement_oop_AppWindowNamespace.html":"",