From 64db9b1b389eacf9f57cfe417d32e6074ca7d260 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 31 Oct 2014 18:51:23 -0400 Subject: [PATCH 1/2] Backed out changeset 3fde6fc99b0a (bug 1020368) for non-unified bustage. --- dom/bindings/Bindings.conf | 16 - dom/camera/CameraControlImpl.cpp | 11 +- dom/camera/CameraControlImpl.h | 5 + dom/camera/CameraControlListener.h | 4 +- dom/camera/CameraRecorderProfiles.cpp | 195 ++++++++ dom/camera/CameraRecorderProfiles.h | 274 +++++++++++ dom/camera/DOMCameraCapabilities.cpp | 454 +++++------------- dom/camera/DOMCameraCapabilities.h | 221 ++------- dom/camera/DOMCameraControl.cpp | 14 +- dom/camera/DOMCameraManager.cpp | 6 +- dom/camera/FallbackCameraControl.cpp | 4 +- dom/camera/GonkCameraControl.cpp | 121 ++--- dom/camera/GonkCameraControl.h | 18 +- dom/camera/GonkRecorderProfiles.cpp | 453 +++++++---------- dom/camera/GonkRecorderProfiles.h | 135 +++--- dom/camera/ICameraControl.h | 76 +-- dom/camera/moz.build | 1 + .../mochitest/general/test_interfaces.html | 8 - dom/webidl/CameraCapabilities.webidl | 53 +- 19 files changed, 947 insertions(+), 1122 deletions(-) create mode 100644 dom/camera/CameraRecorderProfiles.cpp create mode 100644 dom/camera/CameraRecorderProfiles.h diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index dbe8f8f0c2fb..a099a586f52d 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -208,22 +208,6 @@ DOMInterfaces = { 'headerFile': 'DOMCameraManager.h' }, -'CameraRecorderAudioProfile': { - 'headerFile': 'DOMCameraCapabilities.h' -}, - -'CameraRecorderProfile': { - 'headerFile': 'DOMCameraCapabilities.h' -}, - -'CameraRecorderProfiles': { - 'headerFile': 'DOMCameraCapabilities.h' -}, - -'CameraRecorderVideoProfile': { - 'headerFile': 'DOMCameraCapabilities.h' -}, - 'CanvasRenderingContext2D': { 'implicitJSContext': [ 'createImageData', 'getImageData' diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index 7e63473bd741..407d17ffffe4 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -8,6 +8,7 @@ #include "mozilla/unused.h" #include "nsPrintfCString.h" #include "nsIWeakReferenceUtils.h" +#include "CameraRecorderProfiles.h" #include "CameraCommon.h" #include "nsGlobalWindow.h" #include "DeviceStorageFileDescriptor.h" @@ -64,6 +65,12 @@ CameraControlImpl::~CameraControlImpl() } } +already_AddRefed +CameraControlImpl::GetRecorderProfileManager() +{ + return GetRecorderProfileManagerImpl(); +} + void CameraControlImpl::Shutdown() { @@ -107,7 +114,7 @@ CameraControlImpl::OnConfigurationChange() MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); RwLockAutoEnterRead lock(mListenerLock); - DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length()); + DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length()); for (uint32_t i = 0; i < mListeners.Length(); ++i) { CameraControlListener* l = mListeners[i]; @@ -262,7 +269,7 @@ CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uin // On Gonk, it is called from the camera driver's preview thread. RwLockAutoEnterRead lock(mListenerLock); - DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n", + DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n", mListeners.Length()); bool consumed = false; diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index 7d1e58dd5b24..6b6c1399306b 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -25,6 +25,8 @@ namespace layers { class Image; } +class RecorderProfileManager; + class CameraControlImpl : public ICameraControl { public: @@ -47,6 +49,7 @@ public: virtual nsresult StopRecording() MOZ_OVERRIDE; virtual nsresult ResumeContinuousFocus() MOZ_OVERRIDE; + already_AddRefed GetRecorderProfileManager(); uint32_t GetCameraId() { return mCameraId; } virtual void Shutdown() MOZ_OVERRIDE; @@ -125,6 +128,8 @@ protected: virtual nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; + virtual already_AddRefed GetRecorderProfileManagerImpl() = 0; + void OnShutterInternal(); void OnClosedInternal(); diff --git a/dom/camera/CameraControlListener.h b/dom/camera/CameraControlListener.h index a9d8e3219744..727324ac2b27 100644 --- a/dom/camera/CameraControlListener.h +++ b/dom/camera/CameraControlListener.h @@ -34,8 +34,8 @@ public: enum HardwareState { - kHardwareClosed, - kHardwareOpen + kHardwareOpen, + kHardwareClosed }; virtual void OnHardwareStateChange(HardwareState aState) { } diff --git a/dom/camera/CameraRecorderProfiles.cpp b/dom/camera/CameraRecorderProfiles.cpp new file mode 100644 index 000000000000..9c00b540cce6 --- /dev/null +++ b/dom/camera/CameraRecorderProfiles.cpp @@ -0,0 +1,195 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CameraRecorderProfiles.h" +#include "jsapi.h" +#include "CameraCommon.h" + +using namespace mozilla; + +/** + * Video profile implementation. + */ +RecorderVideoProfile::RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : mCameraId(aCameraId) + , mQualityIndex(aQualityIndex) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +RecorderVideoProfile::~RecorderVideoProfile() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +nsresult +RecorderVideoProfile::GetJsObject(JSContext* aCx, JSObject** aObject) +{ + NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG); + + JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY); + + const char* codec = GetCodecName(); + NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE); + + JS::Rooted s(aCx, JS_NewStringCopyZ(aCx, codec)); + JS::Rooted v(aCx, STRING_TO_JSVAL(s)); + if (!JS_SetProperty(aCx, o, "codec", v)) { + return NS_ERROR_FAILURE; + } + + if (mBitrate != -1) { + v = INT_TO_JSVAL(mBitrate); + if (!JS_SetProperty(aCx, o, "bitrate", v)) { + return NS_ERROR_FAILURE; + } + } + if (mFramerate != -1) { + v = INT_TO_JSVAL(mFramerate); + if (!JS_SetProperty(aCx, o, "framerate", v)) { + return NS_ERROR_FAILURE; + } + } + if (mWidth != -1) { + v = INT_TO_JSVAL(mWidth); + if (!JS_SetProperty(aCx, o, "width", v)) { + return NS_ERROR_FAILURE; + } + } + if (mHeight != -1) { + v = INT_TO_JSVAL(mHeight); + if (!JS_SetProperty(aCx, o, "height", v)) { + return NS_ERROR_FAILURE; + } + } + + *aObject = o; + return NS_OK; +} + +/** + * Audio profile implementation. + */ +RecorderAudioProfile::RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : mCameraId(aCameraId) + , mQualityIndex(aQualityIndex) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +RecorderAudioProfile::~RecorderAudioProfile() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +nsresult +RecorderAudioProfile::GetJsObject(JSContext* aCx, JSObject** aObject) +{ + NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG); + + JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY); + + const char* codec = GetCodecName(); + NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE); + + JS::Rooted s(aCx, JS_NewStringCopyZ(aCx, codec)); + JS::Rooted v(aCx, STRING_TO_JSVAL(s)); + if (!JS_SetProperty(aCx, o, "codec", v)) { + return NS_ERROR_FAILURE; + } + + if (mBitrate != -1) { + v = INT_TO_JSVAL(mBitrate); + if (!JS_SetProperty(aCx, o, "bitrate", v)) { + return NS_ERROR_FAILURE; + } + } + if (mSamplerate != -1) { + v = INT_TO_JSVAL(mSamplerate); + if (!JS_SetProperty(aCx, o, "samplerate", v)) { + return NS_ERROR_FAILURE; + } + } + if (mChannels != -1) { + v = INT_TO_JSVAL(mChannels); + if (!JS_SetProperty(aCx, o, "channels", v)) { + return NS_ERROR_FAILURE; + } + } + + *aObject = o; + return NS_OK; +} + +/** + * Recorder Profile + */ +RecorderProfile::RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : mCameraId(aCameraId) + , mQualityIndex(aQualityIndex) + , mName(nullptr) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +RecorderProfile::~RecorderProfile() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +/** + * Recorder profile manager implementation. + */ +RecorderProfileManager::RecorderProfileManager(uint32_t aCameraId) + : mCameraId(aCameraId) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +RecorderProfileManager::~RecorderProfileManager() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +nsresult +RecorderProfileManager::GetJsObject(JSContext* aCx, JSObject** aObject) const +{ + NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG); + + JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + if (!o) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t q = 0; q < GetMaxQualityIndex(); ++q) { + if (!IsSupported(q)) { + continue; + } + + nsRefPtr profile = Get(q); + if (!profile) { + return NS_ERROR_OUT_OF_MEMORY; + } + + const char* profileName = profile->GetName(); + if (!profileName) { + // don't allow anonymous recorder profiles + continue; + } + + JS::Rooted p(aCx); + nsresult rv = profile->GetJsObject(aCx, p.address()); + NS_ENSURE_SUCCESS(rv, rv); + JS::Rooted v(aCx, OBJECT_TO_JSVAL(p)); + + if (!JS_SetProperty(aCx, o, profileName, v)) { + return NS_ERROR_FAILURE; + } + } + + *aObject = o; + return NS_OK; +} diff --git a/dom/camera/CameraRecorderProfiles.h b/dom/camera/CameraRecorderProfiles.h new file mode 100644 index 000000000000..92bc9e69796d --- /dev/null +++ b/dom/camera/CameraRecorderProfiles.h @@ -0,0 +1,274 @@ +/* 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/. */ + +#ifndef DOM_CAMERA_CAMERA_RECORDER_PROFILES_H +#define DOM_CAMERA_CAMERA_RECORDER_PROFILES_H + +#include "nsISupportsImpl.h" +#include "nsMimeTypes.h" +#include "nsAutoPtr.h" +#include "nsTArray.h" +#include "jsapi.h" +#include "CameraCommon.h" + +namespace mozilla { + +class CameraControlImpl; + +class RecorderVideoProfile +{ +public: + RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex); + virtual ~RecorderVideoProfile(); + + int GetBitrate() const { return mBitrate; } + int GetFramerate() const { return mFramerate; } + int GetWidth() const { return mWidth; } + int GetHeight() const { return mHeight; } + + enum Codec { + H263, + H264, + MPEG4SP, + UNKNOWN + }; + Codec GetCodec() const { return mCodec; } + const char* GetCodecName() const + { + switch (mCodec) { + case H263: return "h263"; + case H264: return "h264"; + case MPEG4SP: return "mpeg4sp"; + default: return nullptr; + } + } + + // Get a representation of this video profile that can be returned + // to JS, possibly as a child member of another object. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aObject' is null; + // - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated; + // - NS_ERROR_FAILURE if construction of the JS object fails. + nsresult GetJsObject(JSContext* aCx, JSObject** aObject); + +protected: + uint32_t mCameraId; + uint32_t mQualityIndex; + Codec mCodec; + int mBitrate; + int mFramerate; + int mWidth; + int mHeight; +}; + +class RecorderAudioProfile +{ +public: + RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex); + virtual ~RecorderAudioProfile(); + + int GetBitrate() const { return mBitrate; } + int GetSamplerate() const { return mSamplerate; } + int GetChannels() const { return mChannels; } + + enum Codec { + AMRNB, + AMRWB, + AAC, + UNKNOWN + }; + + Codec GetCodec() const { return mCodec; } + const char* GetCodecName() const + { + switch (mCodec) { + case AMRNB: return "amrnb"; + case AMRWB: return "amrwb"; + case AAC: return "aac"; + default: return nullptr; + } + } + + // Get a representation of this audio profile that can be returned + // to JS, possibly as a child member of another object. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aObject' is null; + // - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated; + // - NS_ERROR_FAILURE if construction of the JS object fails. + nsresult GetJsObject(JSContext* aCx, JSObject** aObject); + +protected: + uint32_t mCameraId; + uint32_t mQualityIndex; + Codec mCodec; + int mBitrate; + int mSamplerate; + int mChannels; +}; + +class RecorderProfile +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile) + + RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex); + + virtual const RecorderVideoProfile* GetVideoProfile() const = 0; + virtual const RecorderAudioProfile* GetAudioProfile() const = 0; + const char* GetName() const { return mName; } + + enum FileFormat { + THREE_GPP, + MPEG4, + UNKNOWN + }; + FileFormat GetFileFormat() const { return mFileFormat; } + const char* GetFileFormatName() const + { + switch (mFileFormat) { + case THREE_GPP: return "3gp"; + case MPEG4: return "mp4"; + default: return nullptr; + } + } + const char* GetFileMimeType() const + { + switch (mFileFormat) { + case THREE_GPP: return VIDEO_3GPP; + case MPEG4: return VIDEO_MP4; + default: return nullptr; + } + } + + // Get a representation of this recorder profile that can be returned + // to JS, possibly as a child member of another object. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aObject' is null; + // - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated; + // - NS_ERROR_FAILURE if construction of the JS object fails. + virtual nsresult GetJsObject(JSContext* aCx, JSObject** aObject) = 0; + +protected: + virtual ~RecorderProfile(); + + uint32_t mCameraId; + uint32_t mQualityIndex; + const char* mName; + FileFormat mFileFormat; +}; + +template +class RecorderProfileBase : public RecorderProfile +{ +public: + RecorderProfileBase(uint32_t aCameraId, uint32_t aQualityIndex) + : RecorderProfile(aCameraId, aQualityIndex) + , mVideo(aCameraId, aQualityIndex) + , mAudio(aCameraId, aQualityIndex) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + virtual ~RecorderProfileBase() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + const RecorderVideoProfile* GetVideoProfile() const { return &mVideo; } + const RecorderAudioProfile* GetAudioProfile() const { return &mAudio; } + + // Get a representation of this recorder profile that can be returned + // to JS, possibly as a child member of another object. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aObject' is null; + // - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated; + // - NS_ERROR_NOT_AVAILABLE if the profile has no file format name; + // - NS_ERROR_FAILURE if construction of the JS object fails. + nsresult + GetJsObject(JSContext* aCx, JSObject** aObject) + { + NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG); + + const char* format = GetFileFormatName(); + if (!format) { + // the profile must have a file format + return NS_ERROR_NOT_AVAILABLE; + } + + JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + if (!o) { + return NS_ERROR_OUT_OF_MEMORY; + } + + JS::Rooted s(aCx, JS_NewStringCopyZ(aCx, format)); + JS::Rooted v(aCx, STRING_TO_JSVAL(s)); + if (!JS_SetProperty(aCx, o, "format", v)) { + return NS_ERROR_FAILURE; + } + + JS::Rooted video(aCx); + nsresult rv = mVideo.GetJsObject(aCx, video.address()); + NS_ENSURE_SUCCESS(rv, rv); + v = OBJECT_TO_JSVAL(video); + if (!JS_SetProperty(aCx, o, "video", v)) { + return NS_ERROR_FAILURE; + } + + JS::Rooted audio(aCx); + rv = mAudio.GetJsObject(aCx, audio.address()); + NS_ENSURE_SUCCESS(rv, rv); + v = OBJECT_TO_JSVAL(audio); + if (!JS_SetProperty(aCx, o, "audio", v)) { + return NS_ERROR_FAILURE; + } + + *aObject = o; + return NS_OK; + } + +protected: + Video mVideo; + Audio mAudio; +}; + +class RecorderProfileManager +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfileManager) + + virtual bool IsSupported(uint32_t aQualityIndex) const { return true; } + virtual already_AddRefed Get(uint32_t aQualityIndex) const = 0; + + uint32_t GetMaxQualityIndex() const { return mMaxQualityIndex; } + + // Get a representation of all supported recorder profiles that can be + // returned to JS. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aObject' is null; + // - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated; + // - NS_ERROR_NOT_AVAILABLE if the profile has no file format name; + // - NS_ERROR_FAILURE if construction of the JS object fails. + nsresult GetJsObject(JSContext* aCx, JSObject** aObject) const; + +protected: + explicit RecorderProfileManager(uint32_t aCameraId); + virtual ~RecorderProfileManager(); + + uint32_t mCameraId; + uint32_t mMaxQualityIndex; +}; + +} // namespace mozilla + +#endif // DOM_CAMERA_CAMERA_RECORDER_PROFILES_H diff --git a/dom/camera/DOMCameraCapabilities.cpp b/dom/camera/DOMCameraCapabilities.cpp index 8b3b85767a4e..5d9907eea79a 100644 --- a/dom/camera/DOMCameraCapabilities.cpp +++ b/dom/camera/DOMCameraCapabilities.cpp @@ -12,202 +12,28 @@ #include "Navigator.h" #include "CameraCommon.h" #include "ICameraControl.h" +#include "CameraRecorderProfiles.h" namespace mozilla { namespace dom { -/** - * CameraRecorderVideoProfile - */ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderVideoProfile, mParent) +NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities) -NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderVideoProfile) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderVideoProfile) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) + tmp->mRecorderProfiles = JS::UndefinedValue(); + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderVideoProfile) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -JSObject* -CameraRecorderVideoProfile::WrapObject(JSContext* aCx) -{ - return CameraRecorderVideoProfileBinding::Wrap(aCx, this); -} - -CameraRecorderVideoProfile::CameraRecorderVideoProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile::Video& aProfile) - : mParent(aParent) - , mCodec(aProfile.GetCodec()) - , mBitrate(aProfile.GetBitsPerSecond()) - , mFramerate(aProfile.GetFramesPerSecond()) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - - mSize.mWidth = aProfile.GetSize().width; - mSize.mHeight = aProfile.GetSize().height; - - DOM_CAMERA_LOGI(" video: '%s' %ux%u bps=%u fps=%u\n", - NS_ConvertUTF16toUTF8(mCodec).get(), mSize.mWidth, mSize.mHeight, mBitrate, mFramerate); -} - -CameraRecorderVideoProfile::~CameraRecorderVideoProfile() -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -/** - * CameraRecorderAudioProfile - */ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderAudioProfile, mParent) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderAudioProfile) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderAudioProfile) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderAudioProfile) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -JSObject* -CameraRecorderAudioProfile::WrapObject(JSContext* aCx) -{ - return CameraRecorderAudioProfileBinding::Wrap(aCx, this); -} - -CameraRecorderAudioProfile::CameraRecorderAudioProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile::Audio& aProfile) - : mParent(aParent) - , mCodec(aProfile.GetCodec()) - , mBitrate(aProfile.GetBitsPerSecond()) - , mSamplerate(aProfile.GetSamplesPerSecond()) - , mChannels(aProfile.GetChannels()) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - DOM_CAMERA_LOGI(" audio: '%s' bps=%u samples/s=%u channels=%u\n", - NS_ConvertUTF16toUTF8(mCodec).get(), mBitrate, mSamplerate, mChannels); -} - -CameraRecorderAudioProfile::~CameraRecorderAudioProfile() -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -/** - * CameraRecorderProfile - */ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfile, mParent) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfile) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfile) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfile) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -JSObject* -CameraRecorderProfile::WrapObject(JSContext* aCx) -{ - return CameraRecorderProfileBinding::Wrap(aCx, this); -} - -CameraRecorderProfile::CameraRecorderProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile& aProfile) - : mParent(aParent) - , mName(aProfile.GetName()) - , mContainerFormat(aProfile.GetContainer()) - , mMimeType(aProfile.GetMimeType()) - , mVideo(new CameraRecorderVideoProfile(this, aProfile.GetVideo())) - , mAudio(new CameraRecorderAudioProfile(this, aProfile.GetAudio())) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - DOM_CAMERA_LOGI("profile: '%s' container=%s mime-type=%s\n", - NS_ConvertUTF16toUTF8(mName).get(), - NS_ConvertUTF16toUTF8(mContainerFormat).get(), - NS_ConvertUTF16toUTF8(mMimeType).get()); -} - -CameraRecorderProfile::~CameraRecorderProfile() -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -/** - * CameraRecorderProfiles - */ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfiles, mParent) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfiles) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfiles) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfiles) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -JSObject* -CameraRecorderProfiles::WrapObject(JSContext* aCx) -{ - return CameraRecorderProfilesBinding::Wrap(aCx, this); -} - -CameraRecorderProfiles::CameraRecorderProfiles(nsISupports* aParent, - ICameraControl* aCameraControl) - : mParent(aParent) - , mCameraControl(aCameraControl) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -CameraRecorderProfiles::~CameraRecorderProfiles() -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -void -CameraRecorderProfiles::GetSupportedNames(unsigned aFlags, nsTArray& aNames) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p, flags=0x%x\n", - __func__, __LINE__, this, aFlags); - - nsresult rv = mCameraControl->GetRecorderProfiles(aNames); - if (NS_WARN_IF(NS_FAILED(rv))) { - aNames.Clear(); - } -} - -CameraRecorderProfile* -CameraRecorderProfiles::NamedGetter(const nsAString& aName, bool& aFound) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s'\n", __func__, __LINE__, this, - NS_ConvertUTF16toUTF8(aName).get()); - - CameraRecorderProfile* profile = mProfiles.GetWeak(aName, &aFound); - if (!aFound || !profile) { - nsRefPtr p = mCameraControl->GetProfileInfo(aName); - if (p) { - profile = new CameraRecorderProfile(this, *p); - mProfiles.Put(aName, profile); - aFound = true; - } - } - return profile; -} - -bool -CameraRecorderProfiles::NameIsEnumerable(const nsAString& aName) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s' (always returns true)\n", - __func__, __LINE__, this, NS_ConvertUTF16toUTF8(aName).get()); - - return true; -} - -/** - * CameraCapabilities - */ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraCapabilities, mWindow) +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mRecorderProfiles) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraCapabilities) NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraCapabilities) @@ -224,24 +50,20 @@ CameraCapabilities::HasSupport(JSContext* aCx, JSObject* aGlobal) return Navigator::HasCameraSupport(aCx, aGlobal); } -CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow, - ICameraControl* aCameraControl) - : mMaxFocusAreas(0) - , mMaxMeteringAreas(0) - , mMaxDetectedFaces(0) - , mMinExposureCompensation(0.0) - , mMaxExposureCompensation(0.0) - , mExposureCompensationStep(0.0) +CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow) + : mRecorderProfiles(JS::UndefinedValue()) , mWindow(aWindow) - , mCameraControl(aCameraControl) { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); MOZ_COUNT_CTOR(CameraCapabilities); + mozilla::HoldJSObjects(this); } CameraCapabilities::~CameraCapabilities() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + mRecorderProfiles = JS::UndefinedValue(); + mozilla::DropJSObjects(this); MOZ_COUNT_DTOR(CameraCapabilities); } @@ -260,12 +82,13 @@ CameraCapabilities::WrapObject(JSContext* aCx) } while(0) nsresult -CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray& aSizes) +CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl, + uint32_t aKey, nsTArray& aSizes) { nsresult rv; nsTArray sizes; - rv = mCameraControl->Get(aKey, sizes); + rv = aCameraControl->Get(aKey, sizes); if (NS_FAILED(rv)) { return rv; } @@ -281,218 +104,205 @@ CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray& a return NS_OK; } -void -CameraCapabilities::GetPreviewSizes(nsTArray& retval) +nsresult +CameraCapabilities::Populate(ICameraControl* aCameraControl) { - if (mPreviewSizes.Length() == 0) { - nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, - mPreviewSizes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES); + NS_ENSURE_TRUE(aCameraControl, NS_ERROR_INVALID_ARG); + + nsresult rv; + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, mPreviewSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PICTURESIZES, mPictureSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, mThumbnailSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_VIDEOSIZES, mVideoSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, mFileFormats); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, mWhiteBalanceModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, mEffects); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, mFlashModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, mIsoModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS); + + int32_t areas; + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS); + mMaxFocusAreas = areas < 0 ? 0 : areas; + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS); + mMaxMeteringAreas = areas < 0 ? 0 : areas; + + int32_t faces; + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES, faces); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES); + mMaxDetectedFaces = faces < 0 ? 0 : faces; + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mMinExposureCompensation); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, mMaxExposureCompensation); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP); + + mRecorderProfileManager = aCameraControl->GetRecorderProfileManager(); + if (!mRecorderProfileManager) { + DOM_CAMERA_LOGW("Unable to get recorder profile manager\n"); + } else { + AutoJSContext js; + + JS::Rooted o(js); + nsresult rv = mRecorderProfileManager->GetJsObject(js, o.address()); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to JS-objectify profile manager (0x%x)\n", rv); + } else { + mRecorderProfiles = JS::ObjectValue(*o); + } } + + // For now, always return success, since the presence or absence of capabilities + // indicates whether or not they are supported. + return NS_OK; +} + +void +CameraCapabilities::GetPreviewSizes(nsTArray& retval) const +{ retval = mPreviewSizes; } void -CameraCapabilities::GetPictureSizes(nsTArray& retval) +CameraCapabilities::GetPictureSizes(nsTArray& retval) const { - if (mPictureSizes.Length() == 0) { - nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PICTURESIZES, - mPictureSizes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES); - } retval = mPictureSizes; } void -CameraCapabilities::GetThumbnailSizes(nsTArray& retval) +CameraCapabilities::GetThumbnailSizes(nsTArray& retval) const { - if (mThumbnailSizes.Length() == 0) { - nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, - mThumbnailSizes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES); - } retval = mThumbnailSizes; } void -CameraCapabilities::GetVideoSizes(nsTArray& retval) +CameraCapabilities::GetVideoSizes(nsTArray& retval) const { - if (mVideoSizes.Length() == 0) { - nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, - mVideoSizes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES); - } retval = mVideoSizes; } void -CameraCapabilities::GetFileFormats(nsTArray& retval) +CameraCapabilities::GetFileFormats(nsTArray& retval) const { - if (mFileFormats.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, - mFileFormats); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS); - } retval = mFileFormats; } void -CameraCapabilities::GetWhiteBalanceModes(nsTArray& retval) +CameraCapabilities::GetWhiteBalanceModes(nsTArray& retval) const { - if (mWhiteBalanceModes.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, - mWhiteBalanceModes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES); - } retval = mWhiteBalanceModes; } void -CameraCapabilities::GetSceneModes(nsTArray& retval) +CameraCapabilities::GetSceneModes(nsTArray& retval) const { - if (mSceneModes.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, - mSceneModes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES); - } retval = mSceneModes; } void -CameraCapabilities::GetEffects(nsTArray& retval) +CameraCapabilities::GetEffects(nsTArray& retval) const { - if (mEffects.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, - mEffects); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS); - } retval = mEffects; } void -CameraCapabilities::GetFlashModes(nsTArray& retval) +CameraCapabilities::GetFlashModes(nsTArray& retval) const { - if (mFlashModes.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, - mFlashModes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES); - } retval = mFlashModes; } void -CameraCapabilities::GetFocusModes(nsTArray& retval) +CameraCapabilities::GetFocusModes(nsTArray& retval) const { - if (mFocusModes.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, - mFocusModes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES); - } retval = mFocusModes; } void -CameraCapabilities::GetZoomRatios(nsTArray& retval) +CameraCapabilities::GetZoomRatios(nsTArray& retval) const { - if (mZoomRatios.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, - mZoomRatios); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS); - } retval = mZoomRatios; } uint32_t -CameraCapabilities::MaxFocusAreas() +CameraCapabilities::MaxFocusAreas() const { - if (mMaxFocusAreas == 0) { - int32_t areas; - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, - areas); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS); - mMaxFocusAreas = areas < 0 ? 0 : areas; - } return mMaxFocusAreas; } uint32_t -CameraCapabilities::MaxMeteringAreas() +CameraCapabilities::MaxMeteringAreas() const { - if (mMaxMeteringAreas == 0) { - int32_t areas; - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, - areas); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS); - mMaxMeteringAreas = areas < 0 ? 0 : areas; - } return mMaxMeteringAreas; } uint32_t -CameraCapabilities::MaxDetectedFaces() +CameraCapabilities::MaxDetectedFaces() const { - if (mMaxDetectedFaces == 0) { - int32_t faces; - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES, - faces); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES); - mMaxDetectedFaces = faces < 0 ? 0 : faces; - } return mMaxDetectedFaces; } double -CameraCapabilities::MinExposureCompensation() +CameraCapabilities::MinExposureCompensation() const { - if (mMinExposureCompensation == 0.0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, - mMinExposureCompensation); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION); - } return mMinExposureCompensation; } double -CameraCapabilities::MaxExposureCompensation() +CameraCapabilities::MaxExposureCompensation() const { - if (mMaxExposureCompensation == 0.0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, - mMaxExposureCompensation); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION); - } return mMaxExposureCompensation; } double -CameraCapabilities::ExposureCompensationStep() +CameraCapabilities::ExposureCompensationStep() const { - if (mExposureCompensationStep == 0.0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, - mExposureCompensationStep); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP); - } return mExposureCompensationStep; } -CameraRecorderProfiles* -CameraCapabilities::RecorderProfiles() +void +CameraCapabilities::GetRecorderProfiles(JSContext* aCx, + JS::MutableHandle aRetval) const { - nsRefPtr profiles = mRecorderProfiles; - if (!mRecorderProfiles) { - profiles = new CameraRecorderProfiles(this, mCameraControl); - mRecorderProfiles = profiles; - } - return profiles; + JS::ExposeValueToActiveJS(mRecorderProfiles); + aRetval.set(mRecorderProfiles); } void -CameraCapabilities::GetIsoModes(nsTArray& retval) +CameraCapabilities::GetIsoModes(nsTArray& retval) const { - if (mIsoModes.Length() == 0) { - nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, - mIsoModes); - LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES); - } retval = mIsoModes; } diff --git a/dom/camera/DOMCameraCapabilities.h b/dom/camera/DOMCameraCapabilities.h index 725c74ff7a52..9a5a2b7f8012 100644 --- a/dom/camera/DOMCameraCapabilities.h +++ b/dom/camera/DOMCameraCapabilities.h @@ -9,173 +9,23 @@ #include "nsString.h" #include "nsAutoPtr.h" -#include "base/basictypes.h" #include "mozilla/Attributes.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/CameraManagerBinding.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" #include "nsPIDOMWindow.h" -#include "nsHashKeys.h" -#include "nsRefPtrHashtable.h" -#include "nsDataHashtable.h" -#include "ICameraControl.h" struct JSContext; +class nsPIDOMWindow; namespace mozilla { + +class ICameraControl; +class RecorderProfileManager; + namespace dom { -/** - * CameraRecorderVideoProfile - */ -class CameraRecorderVideoProfile MOZ_FINAL : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderVideoProfile) - - explicit CameraRecorderVideoProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile::Video& aProfile); - nsISupports* GetParentObject() const { return mParent; } - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - uint32_t BitsPerSecond() const { return mBitrate; } - uint32_t FramesPerSecond() const { return mFramerate; } - void GetCodec(nsAString& aCodec) const { aCodec = mCodec; } - - void GetSize(dom::CameraSize& aSize) const { aSize = mSize; } - - // XXXmikeh - legacy, remove these when the Camera app is updated - uint32_t Width() const { return mSize.mWidth; } - uint32_t Height() const { return mSize.mHeight; } - -protected: - virtual ~CameraRecorderVideoProfile(); - - nsCOMPtr mParent; - - const nsString mCodec; - uint32_t mBitrate; - uint32_t mFramerate; - dom::CameraSize mSize; - -private: - DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderVideoProfile); -}; - -/** - * CameraRecorderAudioProfile - */ -class CameraRecorderAudioProfile MOZ_FINAL : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderAudioProfile) - - explicit CameraRecorderAudioProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile::Audio& aProfile); - nsISupports* GetParentObject() const { return mParent; } - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - uint32_t BitsPerSecond() const { return mBitrate; } - uint32_t SamplesPerSecond() const { return mSamplerate; } - uint32_t Channels() const { return mChannels; } - void GetCodec(nsAString& aCodec) const { aCodec = mCodec; } - -protected: - virtual ~CameraRecorderAudioProfile(); - - nsCOMPtr mParent; - - const nsString mCodec; - uint32_t mBitrate; - uint32_t mSamplerate; - uint32_t mChannels; - -private: - DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderAudioProfile); -}; - -/** - * CameraRecorderProfile - */ -class CameraRecorderProfile MOZ_FINAL : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfile) - - explicit CameraRecorderProfile(nsISupports* aParent, - const ICameraControl::RecorderProfile& aProfile); - nsISupports* GetParentObject() const { return mParent; } - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - void GetMimeType(nsAString& aMimeType) const { aMimeType = mMimeType; } - - CameraRecorderVideoProfile* Video() { return mVideo; } - CameraRecorderAudioProfile* Audio() { return mAudio; } - - void GetName(nsAString& aName) const { aName = mName; } - - void - GetContainerFormat(nsAString& aContainerFormat) const - { - aContainerFormat = mContainerFormat; - } - -protected: - virtual ~CameraRecorderProfile(); - - nsCOMPtr mParent; - - const nsString mName; - const nsString mContainerFormat; - const nsString mMimeType; - - nsRefPtr mVideo; - nsRefPtr mAudio; - -private: - DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfile); -}; - -/** - * CameraRecorderProfiles - */ -class CameraRecorderProfiles MOZ_FINAL : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfiles) - - explicit CameraRecorderProfiles(nsISupports* aParent, - ICameraControl* aCameraControl); - nsISupports* GetParentObject() const { return mParent; } - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - CameraRecorderProfile* NamedGetter(const nsAString& aName, bool& aFound); - bool NameIsEnumerable(const nsAString& aName); - void GetSupportedNames(unsigned aFlags, nsTArray& aNames); - -protected: - virtual ~CameraRecorderProfiles(); - - nsCOMPtr mParent; - nsRefPtr mCameraControl; - nsRefPtrHashtable mProfiles; - -private: - DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfiles); -}; - -/** - * CameraCapabilities - */ class CameraCapabilities MOZ_FINAL : public nsISupports , public nsWrapperCache { @@ -190,38 +40,45 @@ public: // Great Renaming proposed in bug 983177. static bool HasSupport(JSContext* aCx, JSObject* aGlobal); - explicit CameraCapabilities(nsPIDOMWindow* aWindow, - ICameraControl* aCameraControl); + explicit CameraCapabilities(nsPIDOMWindow* aWindow); + + // Populate the camera capabilities interface from the specific + // camera control object. + // + // Return values: + // - NS_OK on success; + // - NS_ERROR_INVALID_ARG if 'aCameraControl' is null. + nsresult Populate(ICameraControl* aCameraControl); nsPIDOMWindow* GetParentObject() const { return mWindow; } virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - void GetPreviewSizes(nsTArray& aRetVal); - void GetPictureSizes(nsTArray& aRetVal); - void GetThumbnailSizes(nsTArray& aRetVal); - void GetVideoSizes(nsTArray& aRetVal); - void GetFileFormats(nsTArray& aRetVal); - void GetWhiteBalanceModes(nsTArray& aRetVal); - void GetSceneModes(nsTArray& aRetVal); - void GetEffects(nsTArray& aRetVal); - void GetFlashModes(nsTArray& aRetVal); - void GetFocusModes(nsTArray& aRetVal); - void GetZoomRatios(nsTArray& aRetVal); - uint32_t MaxFocusAreas(); - uint32_t MaxMeteringAreas(); - uint32_t MaxDetectedFaces(); - double MinExposureCompensation(); - double MaxExposureCompensation(); - double ExposureCompensationStep(); - void GetIsoModes(nsTArray& aRetVal); - - CameraRecorderProfiles* RecorderProfiles(); + void GetPreviewSizes(nsTArray& aRetVal) const; + void GetPictureSizes(nsTArray& aRetVal) const; + void GetThumbnailSizes(nsTArray& aRetVal) const; + void GetVideoSizes(nsTArray& aRetVal) const; + void GetFileFormats(nsTArray& aRetVal) const; + void GetWhiteBalanceModes(nsTArray& aRetVal) const; + void GetSceneModes(nsTArray& aRetVal) const; + void GetEffects(nsTArray& aRetVal) const; + void GetFlashModes(nsTArray& aRetVal) const; + void GetFocusModes(nsTArray& aRetVal) const; + void GetZoomRatios(nsTArray& aRetVal) const; + uint32_t MaxFocusAreas() const; + uint32_t MaxMeteringAreas() const; + uint32_t MaxDetectedFaces() const; + double MinExposureCompensation() const; + double MaxExposureCompensation() const; + double ExposureCompensationStep() const; + void GetRecorderProfiles(JSContext* aCx, JS::MutableHandle aRetval) const; + void GetIsoModes(nsTArray& aRetVal) const; protected: ~CameraCapabilities(); - nsresult TranslateToDictionary(uint32_t aKey, nsTArray& aSizes); + nsresult TranslateToDictionary(ICameraControl* aCameraControl, + uint32_t aKey, nsTArray& aSizes); nsTArray mPreviewSizes; nsTArray mPictureSizes; @@ -246,12 +103,10 @@ protected: double mMaxExposureCompensation; double mExposureCompensationStep; - nsRefPtr mWindow; - nsRefPtr mCameraControl; - nsRefPtr mRecorderProfiles; + nsRefPtr mRecorderProfileManager; + JS::Heap mRecorderProfiles; -private: - DISALLOW_EVIL_CONSTRUCTORS(CameraCapabilities); + nsRefPtr mWindow; }; } // namespace dom diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index eff6c9f1c442..1168c20796ca 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -664,7 +664,12 @@ nsDOMCameraControl::Capabilities() nsRefPtr caps = mCapabilities; if (!caps) { - caps = new CameraCapabilities(mWindow, mCameraControl); + caps = new CameraCapabilities(mWindow); + nsresult rv = caps->Populate(mCameraControl); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv); + return nullptr; + } mCapabilities = caps; } @@ -1135,9 +1140,10 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a MOZ_ASSERT(NS_IsMainThread()); ErrorResult ignored; + DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState); + switch (aState) { case CameraControlListener::kHardwareOpen: - DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n"); { // The hardware is open, so we can return a camera to JS, even if // the preview hasn't started yet. @@ -1158,7 +1164,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a break; case CameraControlListener::kHardwareClosed: - DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n"); { nsRefPtr promise = mReleasePromise.forget(); if (promise || mReleaseOnSuccessCb) { @@ -1183,7 +1188,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a break; default: - DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState); MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state"); } } @@ -1398,7 +1402,7 @@ nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving) void nsDOMCameraControl::OnFacesDetected(const nsTArray& aFaces) { - DOM_CAMERA_LOGI("DOM OnFacesDetected %zu face(s)\n", aFaces.Length()); + DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length()); MOZ_ASSERT(NS_IsMainThread()); Sequence > faces; diff --git a/dom/camera/DOMCameraManager.cpp b/dom/camera/DOMCameraManager.cpp index 7954180389f1..8dd3955958a2 100644 --- a/dom/camera/DOMCameraManager.cpp +++ b/dom/camera/DOMCameraManager.cpp @@ -59,7 +59,7 @@ nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) , mWindow(aWindow) { /* member initializers and constructor code */ - DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\n", __func__, __LINE__, this, mWindowId); + DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId); MOZ_COUNT_CTOR(nsDOMCameraManager); } @@ -364,7 +364,7 @@ nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId, void nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) { - DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\n", aDOMCameraControl, mWindowId); + DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId); MOZ_ASSERT(NS_IsMainThread()); // Put the camera control into the hash table @@ -379,7 +379,7 @@ nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) void nsDOMCameraManager::Shutdown(uint64_t aWindowId) { - DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\n", aWindowId); + DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId); MOZ_ASSERT(NS_IsMainThread()); CameraControls* controls = sActiveWindows->Get(aWindowId); diff --git a/dom/camera/FallbackCameraControl.cpp b/dom/camera/FallbackCameraControl.cpp index be1fde57f6f9..9f443a176496 100644 --- a/dom/camera/FallbackCameraControl.cpp +++ b/dom/camera/FallbackCameraControl.cpp @@ -45,9 +45,6 @@ public: virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } - virtual nsresult GetRecorderProfiles(nsTArray& aProfiles) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } - virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE { return nullptr; } - nsresult PushParameters() { return NS_ERROR_NOT_INITIALIZED; } nsresult PullParameters() { return NS_ERROR_NOT_INITIALIZED; } @@ -66,6 +63,7 @@ protected: virtual nsresult StopRecordingImpl() { return NS_ERROR_NOT_INITIALIZED; } virtual nsresult PushParametersImpl() { return NS_ERROR_NOT_INITIALIZED; } virtual nsresult PullParametersImpl() { return NS_ERROR_NOT_INITIALIZED; } + virtual already_AddRefed GetRecorderProfileManagerImpl() MOZ_OVERRIDE { return nullptr; } private: FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE; diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 9c741f655ee2..e3d5c0510824 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -71,8 +71,11 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId) , mAutoFlashModeOverridden(false) , mSeparateVideoAndPreviewSizesSupported(false) , mDeferConfigUpdate(0) + , mMediaProfiles(nullptr) , mRecorder(nullptr) , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor") + , mProfileManager(nullptr) + , mRecorderProfile(nullptr) , mVideoFile(nullptr) , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor") { @@ -142,7 +145,6 @@ nsGonkCameraControl::Initialize() } DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get()); - mCurrentConfiguration.mRecorderProfile.Truncate(); // Initialize our camera configuration database. PullParametersImpl(); @@ -303,6 +305,9 @@ nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig) { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + // remove any existing recorder profile + mRecorderProfile = nullptr; + nsresult rv = SetPreviewSize(aConfig.mPreviewSize); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -955,7 +960,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri ReentrantMonitorAutoEnter mon(mRecorderMonitor); - NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE); /** @@ -1363,25 +1368,32 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig) { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - RecorderProfile* profile; - if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) { - DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", - NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get()); + // read preferences for camcorder + mMediaProfiles = MediaProfiles::getInstance(); + + nsAutoCString profile = NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile); + mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get()); + if (!mRecorderProfile) { + DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get()); return NS_ERROR_INVALID_ARG; } - mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile; - const RecorderProfile::Video& video(profile->GetVideo()); - const Size& size = video.GetSize(); - int fps = video.GetFramesPerSecond(); - if (fps <= 0 || size.width <= 0 || size.height <= 0) { - DOM_CAMERA_LOGE("Can't configure video with fps=%d, width=%d, height=%d\n", - fps, size.width, size.height); + const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile(); + int width = video->GetWidth(); + int height = video->GetHeight(); + int fps = video->GetFramerate(); + if (fps == -1 || width < 0 || height < 0) { + DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", + fps, width, height); return NS_ERROR_FAILURE; } PullParametersImpl(); + Size size; + size.width = static_cast(width); + size.height = static_cast(height); + { ICameraControlParameterSetAutoEnter set(this); nsresult rv; @@ -1587,12 +1599,8 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation, mRecorder = new GonkRecorder(); CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE); - nsresult rv = - GonkRecorderProfile::ConfigureRecorder(*mRecorder, mCameraId, - mCurrentConfiguration.mRecorderProfile); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder); + NS_ENSURE_SUCCESS(rv, rv); CHECK_SETARG_RETURN(mRecorder->setCamera(mCameraHw), NS_ERROR_FAILURE); @@ -1663,76 +1671,27 @@ nsGonkCameraControl::StopImpl() return NS_OK; } -nsresult -nsGonkCameraControl::LoadRecorderProfiles() +already_AddRefed +nsGonkCameraControl::GetGonkRecorderProfileManager() { - if (mRecorderProfiles.Count() == 0) { - nsTArray> profiles; - nsresult rv = GonkRecorderProfile::GetAll(mCameraId, profiles); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - + if (!mProfileManager) { nsTArray sizes; - rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_NOT_AVAILABLE; - } + nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes); + NS_ENSURE_SUCCESS(rv, nullptr); - // Limit profiles to those video sizes supported by the camera hardware... - for (nsTArray::size_type i = 0; i < profiles.Length(); ++i) { - int width = profiles[i]->GetVideo().GetSize().width; - int height = profiles[i]->GetVideo().GetSize().height; - if (width < 0 || height < 0) { - DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n", - NS_ConvertUTF16toUTF8(profiles[i]->GetName()).get()); - continue; - } - for (nsTArray::size_type n = 0; n < sizes.Length(); ++n) { - if (static_cast(width) == sizes[n].width && - static_cast(height) == sizes[n].height) { - mRecorderProfiles.Put(profiles[i]->GetName(), profiles[i]); - break; - } - } - } + mProfileManager = new GonkRecorderProfileManager(mCameraId); + mProfileManager->SetSupportedResolutions(sizes); } - return NS_OK; + nsRefPtr profileMgr = mProfileManager; + return profileMgr.forget(); } -/* static */ PLDHashOperator -nsGonkCameraControl::Enumerate(const nsAString& aProfileName, - RecorderProfile* aProfile, - void* aUserArg) +already_AddRefed +nsGonkCameraControl::GetRecorderProfileManagerImpl() { - nsTArray* profiles = static_cast*>(aUserArg); - MOZ_ASSERT(profiles); - profiles->AppendElement(aProfileName); - return PL_DHASH_NEXT; -} - -nsresult -nsGonkCameraControl::GetRecorderProfiles(nsTArray& aProfiles) -{ - nsresult rv = LoadRecorderProfiles(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - aProfiles.Clear(); - mRecorderProfiles.EnumerateRead(Enumerate, static_cast(&aProfiles)); - return NS_OK; -} - -ICameraControl::RecorderProfile* -nsGonkCameraControl::GetProfileInfo(const nsAString& aProfile) -{ - RecorderProfile* profile; - if (!mRecorderProfiles.Get(aProfile, &profile)) { - return nullptr; - } - return profile; + nsRefPtr profileMgr = GetGonkRecorderProfileManager(); + return profileMgr.forget(); } void diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index f319411e1838..354f1e8defa4 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -18,7 +18,6 @@ #define DOM_CAMERA_GONKCAMERACONTROL_H #include "base/basictypes.h" -#include "nsRefPtrHashtable.h" #include #include "mozilla/ReentrantMonitor.h" #include "DeviceStorage.h" @@ -80,10 +79,6 @@ public: virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE; virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE; - virtual nsresult GetRecorderProfiles(nsTArray& aProfiles) MOZ_OVERRIDE; - virtual ICameraControl::RecorderProfile* - GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE; - nsresult PushParameters(); nsresult PullParameters(); @@ -125,6 +120,8 @@ protected: virtual nsresult ResumeContinuousFocusImpl() MOZ_OVERRIDE; virtual nsresult PushParametersImpl() MOZ_OVERRIDE; virtual nsresult PullParametersImpl() MOZ_OVERRIDE; + virtual already_AddRefed GetRecorderProfileManagerImpl() MOZ_OVERRIDE; + already_AddRefed GetGonkRecorderProfileManager(); nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes, uint64_t aMaxVideoLengthMs); @@ -134,11 +131,6 @@ protected: nsresult PausePreview(); nsresult GetSupportedSize(const Size& aSize, const nsTArray& supportedSizes, Size& best); - nsresult LoadRecorderProfiles(); - static PLDHashOperator Enumerate(const nsAString& aProfileName, - RecorderProfile* aProfile, - void* aUserArg); - friend class SetPictureSize; friend class SetThumbnailSize; nsresult SetPictureSize(const Size& aSize); @@ -165,14 +157,16 @@ protected: nsRefPtr mImageContainer; + android::MediaProfiles* mMediaProfiles; nsRefPtr mRecorder; // Touching mRecorder happens inside this monitor because the destructor // can run on any thread, and we need to be able to clean up properly if // GonkCameraControl goes away. ReentrantMonitor mRecorderMonitor; - // Supported recorder profiles - nsRefPtrHashtable mRecorderProfiles; + // Camcorder profile settings for the desired quality level + nsRefPtr mProfileManager; + nsRefPtr mRecorderProfile; nsRefPtr mVideoFile; nsString mFileFormat; diff --git a/dom/camera/GonkRecorderProfiles.cpp b/dom/camera/GonkRecorderProfiles.cpp index 77b2ca42c9d3..9ff1045885ae 100644 --- a/dom/camera/GonkRecorderProfiles.cpp +++ b/dom/camera/GonkRecorderProfiles.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Mozilla Foundation + * Copyright (C) 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ #include "GonkRecorderProfiles.h" #include -#include "nsMimeTypes.h" #include "GonkRecorder.h" #include "CameraControlImpl.h" #include "CameraCommon.h" @@ -39,318 +38,216 @@ static struct { { nullptr, 0 } }; -/* static */ nsClassHashtable GonkRecorderProfile::sProfiles; -/* static */ android::MediaProfiles* sMediaProfiles = nullptr; +static MediaProfiles* sMediaProfiles = nullptr; -static MediaProfiles* -GetMediaProfiles() +static bool +IsQualitySupported(uint32_t aCameraId, uint32_t aQualityIndex) { if (!sMediaProfiles) { sMediaProfiles = MediaProfiles::getInstance(); } - MOZ_ASSERT(sMediaProfiles); - return sMediaProfiles; -} - -static bool -IsProfileSupported(uint32_t aCameraId, uint32_t aProfileIndex) -{ - MediaProfiles* profiles = GetMediaProfiles(); - camcorder_quality q = static_cast(ProfileList[aProfileIndex].quality); - return profiles->hasCamcorderProfile(static_cast(aCameraId), q); + camcorder_quality q = static_cast(ProfileList[aQualityIndex].quality); + return sMediaProfiles->hasCamcorderProfile(static_cast(aCameraId), q); } static int -GetProfileParameter(uint32_t aCameraId, uint32_t aProfileIndex, const char* aParameter) +GetProfileParam(uint32_t aCameraId, uint32_t aQualityIndex, const char* aParam) { - MediaProfiles* profiles = GetMediaProfiles(); - camcorder_quality q = static_cast(ProfileList[aProfileIndex].quality); - return profiles->getCamcorderProfileParamByName(aParameter, static_cast(aCameraId), q); + if (!sMediaProfiles) { + sMediaProfiles = MediaProfiles::getInstance(); + } + camcorder_quality q = static_cast(ProfileList[aQualityIndex].quality); + return sMediaProfiles->getCamcorderProfileParamByName(aParam, static_cast(aCameraId), q); } -/* static */ bool -GonkRecorderVideo::Translate(video_encoder aCodec, nsAString& aCodecName) +/** + * Recorder profile. + */ +static RecorderProfile::FileFormat +TranslateFileFormat(output_format aFileFormat) { - switch (aCodec) { - case VIDEO_ENCODER_H263: - aCodecName.AssignASCII("h263"); - break; - - case VIDEO_ENCODER_H264: - aCodecName.AssignASCII("h264"); - break; - - case VIDEO_ENCODER_MPEG_4_SP: - aCodecName.AssignASCII("mpeg4sp"); - break; - - default: - return false; + switch (aFileFormat) { + case OUTPUT_FORMAT_THREE_GPP: return RecorderProfile::THREE_GPP; + case OUTPUT_FORMAT_MPEG_4: return RecorderProfile::MPEG4; + default: return RecorderProfile::UNKNOWN; } - - return true; } -int -GonkRecorderVideo::GetProfileParameter(const char* aParameter) +GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : RecorderProfileBase(aCameraId, aQualityIndex) { - return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter); + DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex); + mPlatformOutputFormat = static_cast(GetProfileParam(mCameraId, mQualityIndex, "file.format")); + mFileFormat = TranslateFileFormat(mPlatformOutputFormat); + if (aQualityIndex < PROFILE_COUNT) { + mName = ProfileList[aQualityIndex].name; + DOM_CAMERA_LOGI("Created camera %d profile index %d: '%s'\n", mCameraId, mQualityIndex, mName); + } } -GonkRecorderVideo::GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex) - : mCameraId(aCameraId) - , mProfileIndex(aProfileIndex) - , mIsValid(false) +GonkRecorderProfile::~GonkRecorderProfile() { - mPlatformEncoder = static_cast(GetProfileParameter("vid.codec")); - bool isValid = Translate(mPlatformEncoder, mCodec); - - int v = GetProfileParameter("vid.width"); - if (v >= 0) { - mSize.width = v; - } else { - isValid = false; - } - v = GetProfileParameter("vid.height"); - if (v >= 0) { - mSize.height = v; - } else { - isValid = false; - } - v = GetProfileParameter("vid.bps"); - if (v >= 0) { - mBitsPerSecond = v; - } else { - isValid = false; - } - v = GetProfileParameter("vid.fps"); - if (v >= 0) { - mFramesPerSecond = v; - } else { - isValid = false; - } - - mIsValid = isValid; -} - -/* static */ bool -GonkRecorderAudio::Translate(audio_encoder aCodec, nsAString& aCodecName) -{ - switch (aCodec) { - case AUDIO_ENCODER_AMR_NB: - aCodecName.AssignASCII("amrnb"); - break; - - case AUDIO_ENCODER_AMR_WB: - aCodecName.AssignASCII("amrwb"); - break; - - case AUDIO_ENCODER_AAC: - aCodecName.AssignASCII("aac"); - break; - - default: - return false; - } - - return true; -} - -int -GonkRecorderAudio::GetProfileParameter(const char* aParameter) -{ - return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter); -} - -GonkRecorderAudio::GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex) - : mCameraId(aCameraId) - , mProfileIndex(aProfileIndex) - , mIsValid(false) -{ - mPlatformEncoder = static_cast(GetProfileParameter("aud.codec")); - bool isValid = Translate(mPlatformEncoder, mCodec); - - int v = GetProfileParameter("aud.ch"); - if (v >= 0) { - mChannels = v; - } else { - isValid = false; - } - v = GetProfileParameter("aud.bps"); - if (v >= 0) { - mBitsPerSecond = v; - } else { - isValid = false; - } - v = GetProfileParameter("aud.hz"); - if (v >= 0) { - mSamplesPerSecond = v; - } else { - isValid = false; - } - - mIsValid = isValid; -} - -/* static */ bool -GonkRecorderProfile::Translate(output_format aContainer, nsAString& aContainerName) -{ - switch (aContainer) { - case OUTPUT_FORMAT_THREE_GPP: - aContainerName.AssignASCII("3gp"); - break; - - case OUTPUT_FORMAT_MPEG_4: - aContainerName.AssignASCII("mp4"); - break; - - default: - return false; - } - - return true; -} - -/* static */ bool -GonkRecorderProfile::GetMimeType(output_format aContainer, nsAString& aMimeType) -{ - switch (aContainer) { - case OUTPUT_FORMAT_THREE_GPP: - aMimeType.AssignASCII(VIDEO_3GPP); - break; - - case OUTPUT_FORMAT_MPEG_4: - aMimeType.AssignASCII(VIDEO_MP4); - break; - - default: - return false; - } - - return true; -} - -int -GonkRecorderProfile::GetProfileParameter(const char* aParameter) -{ - return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter); -} - -GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, - uint32_t aProfileIndex, - const nsAString& aName) - : GonkRecorderProfileBase(aCameraId, - aProfileIndex, aName) - , mCameraId(aCameraId) - , mProfileIndex(aProfileIndex) - , mIsValid(false) -{ - mOutputFormat = static_cast(GetProfileParameter("file.format")); - bool isValid = Translate(mOutputFormat, mContainer); - isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false; - - mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid(); -} - -/* static */ PLDHashOperator -GonkRecorderProfile::Enumerate(const nsAString& aProfileName, - GonkRecorderProfile* aProfile, - void* aUserArg) -{ - nsTArray>* profiles = - static_cast>*>(aUserArg); - MOZ_ASSERT(profiles); - profiles->AppendElement(aProfile); - return PL_DHASH_NEXT; -} - -/* static */ -ProfileHashtable* -GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId) -{ - ProfileHashtable* profiles = sProfiles.Get(aCameraId); - if (!profiles) { - profiles = new ProfileHashtable; - sProfiles.Put(aCameraId, profiles); - - for (uint32_t i = 0; ProfileList[i].name; ++i) { - if (IsProfileSupported(aCameraId, i)) { - DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", i, ProfileList[i].name); - nsAutoString name; - name.AssignASCII(ProfileList[i].name); - nsRefPtr profile = new GonkRecorderProfile(aCameraId, i, name); - if (!profile->IsValid()) { - DOM_CAMERA_LOGE("Profile %d '%s' is not valid\n", i, ProfileList[i].name); - continue; - } - profiles->Put(name, profile); - } else { - DOM_CAMERA_LOGI("Profile %d '%s' not supported by platform\n", i, ProfileList[i].name); - } - } - } - return profiles; -} - -/* static */ nsresult -GonkRecorderProfile::GetAll(uint32_t aCameraId, - nsTArray>& aProfiles) -{ - ProfileHashtable* profiles = GetProfileHashtable(aCameraId); - if (!profiles) { - return NS_ERROR_FAILURE; - } - - aProfiles.Clear(); - profiles->EnumerateRead(Enumerate, static_cast(&aProfiles)); - - return NS_OK; + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); } nsresult -GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder) +GonkRecorderProfile::ConfigureRecorder(GonkRecorder* aRecorder) { + if (!aRecorder) { + DOM_CAMERA_LOGW("ConfigureRecorder() called with null aRecorder\n"); + return NS_ERROR_INVALID_ARG; + } + static const size_t SIZE = 256; char buffer[SIZE]; // set all the params - CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER)); - CHECK_SETARG(aRecorder.setVideoSource(VIDEO_SOURCE_CAMERA)); - CHECK_SETARG(aRecorder.setOutputFormat(mOutputFormat)); - CHECK_SETARG(aRecorder.setVideoFrameRate(mVideo.GetFramesPerSecond())); - CHECK_SETARG(aRecorder.setVideoSize(mVideo.GetSize().width, mVideo.GetSize().height)); - CHECK_SETARG(aRecorder.setVideoEncoder(mVideo.GetPlatformEncoder())); - CHECK_SETARG(aRecorder.setAudioEncoder(mAudio.GetPlatformEncoder())); + CHECK_SETARG(aRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER)); + CHECK_SETARG(aRecorder->setVideoSource(VIDEO_SOURCE_CAMERA)); + CHECK_SETARG(aRecorder->setOutputFormat(GetOutputFormat())); + CHECK_SETARG(aRecorder->setVideoFrameRate(mVideo.GetFramerate())); + CHECK_SETARG(aRecorder->setVideoSize(mVideo.GetWidth(), mVideo.GetHeight())); + CHECK_SETARG(aRecorder->setVideoEncoder(mVideo.GetPlatformCodec())); + CHECK_SETARG(aRecorder->setAudioEncoder(mAudio.GetPlatformCodec())); - snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitsPerSecond()); - CHECK_SETARG(aRecorder.setParameters(String8(buffer))); + snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitrate()); + CHECK_SETARG(aRecorder->setParameters(String8(buffer))); - snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitsPerSecond()); - CHECK_SETARG(aRecorder.setParameters(String8(buffer))); + snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitrate()); + CHECK_SETARG(aRecorder->setParameters(String8(buffer))); snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels()); - CHECK_SETARG(aRecorder.setParameters(String8(buffer))); + CHECK_SETARG(aRecorder->setParameters(String8(buffer))); - snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond()); - CHECK_SETARG(aRecorder.setParameters(String8(buffer))); + snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplerate()); + CHECK_SETARG(aRecorder->setParameters(String8(buffer))); return NS_OK; } -/* static */ nsresult -GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder, - uint32_t aCameraId, - const nsAString& aProfileName) +/** + * Recorder audio profile. + */ +static RecorderAudioProfile::Codec +TranslateAudioCodec(audio_encoder aCodec) { - ProfileHashtable* profiles = GetProfileHashtable(aCameraId); - if (!profiles) { - return NS_ERROR_FAILURE; + switch (aCodec) { + case AUDIO_ENCODER_AMR_NB: return RecorderAudioProfile::AMRNB; + case AUDIO_ENCODER_AMR_WB: return RecorderAudioProfile::AMRWB; + case AUDIO_ENCODER_AAC: return RecorderAudioProfile::AAC; + default: return RecorderAudioProfile::UNKNOWN; } - - GonkRecorderProfile* profile; - if (!profiles->Get(aProfileName, &profile)) { - return NS_ERROR_INVALID_ARG; - } - - return profile->ConfigureRecorder(aRecorder); +} + +GonkRecorderAudioProfile::GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : RecorderAudioProfile(aCameraId, aQualityIndex) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex); + mPlatformCodec = static_cast(GetProfileParam(mCameraId, mQualityIndex, "aud.codec")); + mCodec = TranslateAudioCodec(mPlatformCodec); + mBitrate = GetProfileParam(mCameraId, mQualityIndex, "aud.bps"); + mSamplerate = GetProfileParam(mCameraId, mQualityIndex, "aud.hz"); + mChannels = GetProfileParam(mCameraId, mQualityIndex, "aud.ch"); +} + +GonkRecorderAudioProfile::~GonkRecorderAudioProfile() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +/** + * Recorder video profile. + */ +static RecorderVideoProfile::Codec +TranslateVideoCodec(video_encoder aCodec) +{ + switch (aCodec) { + case VIDEO_ENCODER_H263: return RecorderVideoProfile::H263; + case VIDEO_ENCODER_H264: return RecorderVideoProfile::H264; + case VIDEO_ENCODER_MPEG_4_SP: return RecorderVideoProfile::MPEG4SP; + default: return RecorderVideoProfile::UNKNOWN; + } +} + +GonkRecorderVideoProfile::GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex) + : RecorderVideoProfile(aCameraId, aQualityIndex) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex); + mPlatformCodec = static_cast(GetProfileParam(mCameraId, mQualityIndex, "vid.codec")); + mCodec = TranslateVideoCodec(mPlatformCodec); + mBitrate = GetProfileParam(mCameraId, mQualityIndex, "vid.bps"); + mFramerate = GetProfileParam(mCameraId, mQualityIndex, "vid.fps"); + mWidth = GetProfileParam(mCameraId, mQualityIndex, "vid.width"); + mHeight = GetProfileParam(mCameraId, mQualityIndex, "vid.height"); +} + +GonkRecorderVideoProfile::~GonkRecorderVideoProfile() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +GonkRecorderProfileManager::GonkRecorderProfileManager(uint32_t aCameraId) + : RecorderProfileManager(aCameraId) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + mMaxQualityIndex = sizeof(ProfileList) / sizeof(ProfileList[0]) - 1; +} + +GonkRecorderProfileManager::~GonkRecorderProfileManager() +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); +} + +bool +GonkRecorderProfileManager::IsSupported(uint32_t aQualityIndex) const +{ + if (!IsQualitySupported(mCameraId, aQualityIndex)) { + // This profile is not supported + return false; + } + + int width = GetProfileParam(mCameraId, aQualityIndex, "vid.width"); + int height = GetProfileParam(mCameraId, aQualityIndex, "vid.height"); + if (width == -1 || height == -1) { + // This would be unexpected, but we handle it just in case + DOM_CAMERA_LOGE("Camera %d recorder profile %d has width=%d, height=%d\n", mCameraId, aQualityIndex, width, height); + return false; + } + + for (uint32_t i = 0; i < mSupportedSizes.Length(); ++i) { + if (static_cast(width) == mSupportedSizes[i].width && + static_cast(height) == mSupportedSizes[i].height) + { + return true; + } + } + return false; +} + +already_AddRefed +GonkRecorderProfileManager::Get(uint32_t aQualityIndex) const +{ + // This overrides virtual RecorderProfileManager::Get(...) + DOM_CAMERA_LOGT("%s:%d : aQualityIndex=%d\n", __func__, __LINE__, aQualityIndex); + nsRefPtr profile = new GonkRecorderProfile(mCameraId, aQualityIndex); + return profile.forget(); +} + +already_AddRefed +GonkRecorderProfileManager::Get(const char* aProfileName) const +{ + DOM_CAMERA_LOGT("%s:%d : aProfileName='%s'\n", __func__, __LINE__, aProfileName); + for (uint32_t i = 0; i < mMaxQualityIndex; ++i) { + if (strcmp(ProfileList[i].name, aProfileName) == 0) { + nsRefPtr profile = nullptr; + if (IsSupported(i)) { + profile = new GonkRecorderProfile(mCameraId, i); + return profile.forget(); + } + return nullptr; + } + } + + DOM_CAMERA_LOGW("Couldn't file recorder profile named '%s'\n", aProfileName); + return nullptr; } diff --git a/dom/camera/GonkRecorderProfiles.h b/dom/camera/GonkRecorderProfiles.h index 2d3c4feea8b0..b66f62a6680d 100644 --- a/dom/camera/GonkRecorderProfiles.h +++ b/dom/camera/GonkRecorderProfiles.h @@ -6,6 +6,7 @@ #define DOM_CAMERA_GONK_RECORDER_PROFILES_H #include +#include "CameraRecorderProfiles.h" #include "ICameraControl.h" #ifndef CHECK_SETARG_RETURN @@ -23,125 +24,97 @@ #endif namespace android { - class GonkRecorder; +class GonkRecorder; }; namespace mozilla { /** - * class GonkRecorderProfileBase + * Gonk-specific video profile. */ -template -class GonkRecorderProfileBase : public ICameraControl::RecorderProfile +class GonkRecorderVideoProfile : public RecorderVideoProfile { public: - GonkRecorderProfileBase(uint32_t aCameraId, uint32_t aProfileIndex, const nsAString& aName) - : RecorderProfile(aName) - , mAudio(aCameraId, aProfileIndex) - , mVideo(aCameraId, aProfileIndex) - { } - - virtual const Audio& GetAudio() const MOZ_OVERRIDE { return mAudio; } - virtual const Video& GetVideo() const MOZ_OVERRIDE { return mVideo; } + GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex); + ~GonkRecorderVideoProfile(); + android::video_encoder GetPlatformCodec() const { return mPlatformCodec; } protected: - virtual ~GonkRecorderProfileBase() { } - A mAudio; - V mVideo; + android::video_encoder mPlatformCodec; }; /** - * class GonkRecorderVideo + * Gonk-specific audio profile. */ -class GonkRecorderVideo : public ICameraControl::RecorderProfile::Video +class GonkRecorderAudioProfile : public RecorderAudioProfile { public: - GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex); - virtual ~GonkRecorderVideo() { } - - android::video_encoder GetPlatformEncoder() const { return mPlatformEncoder; } - bool IsValid() const { return mIsValid; } + GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex); + ~GonkRecorderAudioProfile(); + android::audio_encoder GetPlatformCodec() const { return mPlatformCodec; } protected: - int GetProfileParameter(const char* aParameter); - static bool Translate(android::video_encoder aCodec, nsAString& aCodecName); - - uint32_t mCameraId; - uint32_t mProfileIndex; - bool mIsValid; - android::video_encoder mPlatformEncoder; + android::audio_encoder mPlatformCodec; }; /** - * class GonkRecorderAudio + * Gonk-specific recorder profile. */ -class GonkRecorderAudio : public ICameraControl::RecorderProfile::Audio +class GonkRecorderProfile : public RecorderProfileBase { public: - GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex); - virtual ~GonkRecorderAudio() { } + GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex); - android::audio_encoder GetPlatformEncoder() const { return mPlatformEncoder; } - bool IsValid() const { return mIsValid; } + GonkRecorderAudioProfile* GetGonkAudioProfile() { return &mAudio; } + GonkRecorderVideoProfile* GetGonkVideoProfile() { return &mVideo; } -protected: - int GetProfileParameter(const char* aParameter); - static bool Translate(android::audio_encoder aCodec, nsAString& aCodecName); + android::output_format GetOutputFormat() const { return mPlatformOutputFormat; } - uint32_t mCameraId; - uint32_t mProfileIndex; - bool mIsValid; - android::audio_encoder mPlatformEncoder; -}; - -/** - * class GonkRecorderProfile - */ -typedef nsRefPtrHashtable ProfileHashtable; - -class GonkRecorderProfile - : public GonkRecorderProfileBase -{ -public: - static nsresult GetAll(uint32_t aCameraId, - nsTArray>& aProfiles); - - // Configures the specified recorder using the specified profile. + // Configures the specified recorder using this profile. // // Return values: // - NS_OK on success; - // - NS_ERROR_INVALID_ARG if the profile isn't supported; - // - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile. - static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder, - uint32_t aCameraId, - const nsAString& aProfileName); + // - NS_ERROR_INVALID_ARG if 'aRecorder' is null; + // - NS_ERROR_NOT_AVAILABLE if the recorder rejected this profile. + nsresult ConfigureRecorder(android::GonkRecorder* aRecorder); protected: - GonkRecorderProfile(uint32_t aCameraId, - uint32_t aProfileIndex, - const nsAString& aName); + virtual ~GonkRecorderProfile(); - int GetProfileParameter(const char* aParameter); + android::output_format mPlatformOutputFormat; +}; - bool Translate(android::output_format aContainer, nsAString& aContainerName); - bool GetMimeType(android::output_format aContainer, nsAString& aMimeType); - bool IsValid() const { return mIsValid; }; +/** + * Gonk-specific profile manager. + */ +class GonkRecorderProfileManager : public RecorderProfileManager +{ +public: + GonkRecorderProfileManager(uint32_t aCameraId); - nsresult ConfigureRecorder(android::GonkRecorder& aRecorder); - static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId); - static PLDHashOperator Enumerate(const nsAString& aProfileName, - GonkRecorderProfile* aProfile, - void* aUserArg); + /** + * Call this function to indicate that the specified resolutions are in fact + * supported by the camera hardware. (Just because it appears in a recorder + * profile doesn't mean the hardware can handle it.) + */ + void SetSupportedResolutions(const nsTArray& aSizes) + { mSupportedSizes = aSizes; } - uint32_t mCameraId; - uint32_t mProfileIndex; - bool mIsValid; - android::output_format mOutputFormat; + /** + * Call this function to remove all resolutions set by calling + * SetSupportedResolutions(). + */ + void ClearSupportedResolutions() { mSupportedSizes.Clear(); } - static nsClassHashtable sProfiles; + bool IsSupported(uint32_t aQualityIndex) const; -private: - DISALLOW_EVIL_CONSTRUCTORS(GonkRecorderProfile); + already_AddRefed Get(uint32_t aQualityIndex) const; + already_AddRefed Get(const char* aProfileName) const; + +protected: + virtual ~GonkRecorderProfileManager(); + + nsTArray mSupportedSizes; }; }; // namespace mozilla diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 1b1407c34c2b..80445dae82e1 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -9,7 +9,6 @@ #include "nsString.h" #include "nsAutoPtr.h" #include "nsISupportsImpl.h" -#include "base/basictypes.h" struct DeviceStorageFileDescriptor; @@ -18,6 +17,7 @@ class nsIFile; namespace mozilla { class CameraControlListener; +class RecorderProfileManager; // XXXmikeh - In some future patch this should move into the ICameraControl // class as well, and the names updated to the 'k'-style; @@ -162,76 +162,6 @@ public: Point mouth; }; - class RecorderProfile - { - public: - class Video - { - public: - Video() { } - virtual ~Video() { } - - const nsString& GetCodec() const { return mCodec; } - const Size& GetSize() const { return mSize; } - uint32_t GetBitsPerSecond() const { return mBitsPerSecond; } - uint32_t GetFramesPerSecond() const { return mFramesPerSecond; } - - protected: - nsString mCodec; - Size mSize; - uint32_t mBitsPerSecond; - uint32_t mFramesPerSecond; - - private: - DISALLOW_EVIL_CONSTRUCTORS(Video); - }; - - class Audio - { - public: - Audio() { } - virtual ~Audio() { } - - const nsString& GetCodec() const { return mCodec; } - - uint32_t GetChannels() const { return mChannels; } - uint32_t GetBitsPerSecond() const { return mBitsPerSecond; } - uint32_t GetSamplesPerSecond() const { return mSamplesPerSecond; } - - protected: - nsString mCodec; - uint32_t mChannels; - uint32_t mBitsPerSecond; - uint32_t mSamplesPerSecond; - - private: - DISALLOW_EVIL_CONSTRUCTORS(Audio); - }; - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile) - - RecorderProfile(const nsAString& aName) - : mName(aName) - { } - - const nsString& GetName() const { return mName; } - const nsString& GetContainer() const { return mContainer; } - const nsString& GetMimeType() const { return mMimeType; } - - virtual const Video& GetVideo() const = 0; - virtual const Audio& GetAudio() const = 0; - - protected: - virtual ~RecorderProfile() { } - - nsString mName; - nsString mContainer; - nsString mMimeType; - - private: - DISALLOW_EVIL_CONSTRUCTORS(RecorderProfile); - }; - static already_AddRefed Create(uint32_t aCameraId); virtual void AddListener(CameraControlListener* aListener) = 0; @@ -290,9 +220,7 @@ public: virtual nsresult Get(uint32_t aKey, nsTArray& aValues) = 0; virtual nsresult Get(uint32_t aKey, nsTArray& aValues) = 0; - virtual nsresult GetRecorderProfiles(nsTArray& aProfiles) = 0; - virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) = 0; - + virtual already_AddRefed GetRecorderProfileManager() = 0; virtual uint32_t GetCameraId() = 0; virtual void Shutdown() = 0; diff --git a/dom/camera/moz.build b/dom/camera/moz.build index 04ded03a211c..f926e11dc3f3 100644 --- a/dom/camera/moz.build +++ b/dom/camera/moz.build @@ -17,6 +17,7 @@ UNIFIED_SOURCES += [ 'CameraControlImpl.cpp', 'CameraPreferences.cpp', 'CameraPreviewMediaStream.cpp', + 'CameraRecorderProfiles.cpp', 'DOMCameraCapabilities.cpp', 'DOMCameraControl.cpp', 'DOMCameraControlListener.cpp', diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index d2549fa7cf46..0879a5a3b96b 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -211,14 +211,6 @@ var interfaceNamesInGlobalScope = {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: "CameraRecorderAudioProfile", b2g: true}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "CameraRecorderProfile", b2g: true}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "CameraRecorderProfiles", b2g: true}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "CameraRecorderVideoProfile", 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! diff --git a/dom/webidl/CameraCapabilities.webidl b/dom/webidl/CameraCapabilities.webidl index c1fe6aee0e21..bf478b4aa454 100644 --- a/dom/webidl/CameraCapabilities.webidl +++ b/dom/webidl/CameraCapabilities.webidl @@ -5,55 +5,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* The capabilities of the video recorder. These are guaranteed not to change - over the lifetime of that partcicular instance. -*/ -[Func="CameraCapabilities::HasSupport"] -interface CameraRecorderAudioProfile -{ - [Constant, Cached] readonly attribute DOMString codec; - [Constant, Cached] readonly attribute unsigned long bitsPerSecond; - [Constant, Cached] readonly attribute unsigned long samplesPerSecond; - [Constant, Cached] readonly attribute unsigned long channels; - - jsonifier; -}; - -[Func="CameraCapabilities::HasSupport"] -interface CameraRecorderVideoProfile -{ - [Constant, Cached] readonly attribute DOMString codec; - [Constant, Cached] readonly attribute unsigned long bitsPerSecond; - [Constant, Cached] readonly attribute unsigned long framesPerSecond; - [Constant, Cached] readonly attribute CameraSize size; - - [Constant, Cached] readonly attribute unsigned long width; - [Constant, Cached] readonly attribute unsigned long height; - - jsonifier; -}; - -[Func="CameraCapabilities::HasSupport"] -interface CameraRecorderProfile -{ - [Constant, Cached] readonly attribute DOMString name; - [Constant, Cached] readonly attribute DOMString containerFormat; - [Constant, Cached] readonly attribute DOMString mimeType; - - [Constant, Cached] readonly attribute CameraRecorderAudioProfile audio; - [Constant, Cached] readonly attribute CameraRecorderVideoProfile video; - - jsonifier; -}; - -[Func="CameraCapabilities::HasSupport"] -interface CameraRecorderProfiles -{ - getter CameraRecorderProfile(DOMString profile); - - jsonifier; -}; - /* The capabilities of a CameraControl instance. These are guaranteed not to change over the lifetime of that particular instance. */ @@ -83,9 +34,7 @@ interface CameraCapabilities [Constant, Cached] readonly attribute double maxExposureCompensation; [Constant, Cached] readonly attribute double exposureCompensationStep; - [Constant, Cached] readonly attribute CameraRecorderProfiles recorderProfiles; + [Constant, Cached] readonly attribute any recorderProfiles; [Constant, Cached] readonly attribute sequence isoModes; - - jsonifier; }; From 776ece8f4827390eae5a3263857f6a6325a034d9 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 31 Oct 2014 18:58:42 -0400 Subject: [PATCH 2/2] Backed out changeset 10616214c160 (bug 1091916) for Android x86 S4 permafail. --- js/src/asmjs/AsmJSModule.cpp | 6 +- .../irregexp/NativeRegExpMacroAssembler.cpp | 4 +- js/src/jit/BaselineCompiler.cpp | 4 +- js/src/jit/CodeGenerator.cpp | 8 +- js/src/jit/CompileWrappers.cpp | 10 +- js/src/jit/CompileWrappers.h | 4 +- js/src/jit/Ion.cpp | 4 +- js/src/jit/IonMacroAssembler.cpp | 2 +- js/src/jit/ParallelFunctions.cpp | 2 +- js/src/jit/VMFunctions.cpp | 29 +++- js/src/jit/shared/Assembler-shared.h | 2 +- js/src/jsapi.cpp | 54 ++++--- js/src/jsapi.h | 3 - js/src/jscntxt.cpp | 85 +++++++++++ js/src/jscntxt.h | 32 +++- js/src/jsgcinlines.h | 2 +- js/src/jsnativestack.h | 1 - js/src/vm/ForkJoin.cpp | 17 ++- js/src/vm/ForkJoin.h | 2 + js/src/vm/RegExpObject.cpp | 10 +- js/src/vm/Runtime.cpp | 143 +++++------------- js/src/vm/Runtime.h | 105 +++++-------- 22 files changed, 290 insertions(+), 239 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index c6846478c942..8feb3ff9cf1f 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -500,7 +500,7 @@ AsmJSHandleExecutionInterrupt() { AsmJSActivation *act = PerThreadData::innermostAsmJSActivation(); act->module().setInterrupted(true); - bool ret = CheckForInterrupt(act->cx()); + bool ret = HandleExecutionInterrupt(act->cx()); act->module().setInterrupted(false); return ret; } @@ -672,8 +672,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx) switch (kind) { case AsmJSImm_Runtime: return cx->runtimeAddressForJit(); - case AsmJSImm_RuntimeInterruptUint32: - return cx->runtimeAddressOfInterruptUint32(); + case AsmJSImm_RuntimeInterrupt: + return cx->runtimeAddressOfInterrupt(); case AsmJSImm_StackLimit: return cx->stackLimitAddressForJitCode(StackForUntrustedScript); case AsmJSImm_ReportOverRecursed: diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index ba02bd6679cd..8cce4ee744b2 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -152,7 +152,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only) // Check if we have space on the stack. Label stack_ok; - void *stack_limit = runtime->mainThread.addressofJitStackLimit(); + void *stack_limit = &runtime->mainThread.jitStackLimit; masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok); // Exit with an exception. There is not enough space on the stack @@ -502,7 +502,7 @@ NativeRegExpMacroAssembler::Backtrack() // Check for an interrupt. Label noInterrupt; masm.branch32(Assembler::Equal, - AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0), + AbsoluteAddress(&runtime->interrupt), Imm32(0), &noInterrupt); masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); masm.jump(&exit_label_); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 302cca0bb5b8..9986acbcd30a 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -496,7 +496,7 @@ bool BaselineCompiler::emitStackCheck(bool earlyCheck) { Label skipCall; - void *limitAddr = cx->runtime()->mainThread.addressofJitStackLimit(); + uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit; uint32_t slotsSize = script->nslots() * sizeof(Value); uint32_t tolerance = earlyCheck ? slotsSize : 0; @@ -646,7 +646,7 @@ BaselineCompiler::emitInterruptCheck() frame.syncStack(0); Label done; - void *interrupt = cx->runtimeAddressOfInterruptUint32(); + void *interrupt = (void *)&cx->runtime()->interrupt; masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done); prepareVMCall(); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index a806fd85983f..867d9292ee62 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3747,7 +3747,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir) Register tempReg = ToRegister(lir->getTempReg()); masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg); - masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg); + masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg); // Conditional forward (unlikely) branch to failure. CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir); @@ -9789,7 +9789,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck *lir) if (!ool) return false; - AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32()); + AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt()); masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry()); masm.bind(ool->rejoin()); return true; @@ -9799,8 +9799,8 @@ bool CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir) { Register scratch = ToRegister(lir->scratch()); - masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch); - masm.load32(Address(scratch, 0), scratch); + masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch); + masm.load8ZeroExtend(Address(scratch, 0), scratch); Label rejoin; masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin); { diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 4ab511c76971..0030b92b26d8 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -43,7 +43,7 @@ CompileRuntime::addressOfJitTop() const void * CompileRuntime::addressOfJitStackLimit() { - return runtime()->mainThread.addressofJitStackLimit(); + return &runtime()->mainThread.jitStackLimit; } const void * @@ -73,15 +73,15 @@ CompileRuntime::addressOfGCZeal() #endif const void * -CompileRuntime::addressOfInterruptUint32() +CompileRuntime::addressOfInterrupt() { - return runtime()->addressOfInterruptUint32(); + return &runtime()->interrupt; } const void * -CompileRuntime::addressOfInterruptParUint32() +CompileRuntime::addressOfInterruptPar() { - return runtime()->addressOfInterruptParUint32(); + return &runtime()->interruptPar; } const void * diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index 879bca9e8f45..fe896315d0ed 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -50,8 +50,8 @@ class CompileRuntime const void *addressOfGCZeal(); #endif - const void *addressOfInterruptUint32(); - const void *addressOfInterruptParUint32(); + const void *addressOfInterrupt(); + const void *addressOfInterruptPar(); const void *addressOfThreadPool(); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index d4bec5c56bb4..5b388c0e2da2 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -423,7 +423,7 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt) ionCodeProtected_ = false; } - if (rt->hasPendingInterrupt()) { + if (rt->interrupt) { // The interrupt handler needs to be invoked by this thread, but we may // be inside a signal handler and have no idea what is above us on the // stack (probably we are executing Ion code at an arbitrary point, but @@ -1157,7 +1157,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code, // whether an interrupt is currently desired, matching the targets // established by ensureIonCodeAccessible() above. We don't handle the // interrupt immediately as the interrupt lock is held here. - if (cx->runtime()->hasPendingInterrupt()) + if (cx->runtime()->interrupt) PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck); else PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader); diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index e6d444961dcb..879c8c40f2b9 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -1168,7 +1168,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output) void MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail) { - movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg); + movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg); branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail); } diff --git a/js/src/jit/ParallelFunctions.cpp b/js/src/jit/ParallelFunctions.cpp index 58df34db82cc..2080bb959ad4 100644 --- a/js/src/jit/ParallelFunctions.cpp +++ b/js/src/jit/ParallelFunctions.cpp @@ -147,7 +147,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx) } #endif - if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) { + if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) { cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed); return false; } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 6ba66e12fc99..8536916d1e5a 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -113,17 +113,28 @@ NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap) bool CheckOverRecursed(JSContext *cx) { - // We just failed the jitStackLimit check. There are two possible reasons: - // - jitStackLimit was the real stack limit and we're over-recursed - // - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt - // and we need to call JSRuntime::handleInterrupt. + // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we + // request an interrupt, we set the jitStackLimit to nullptr, which causes + // the stack limit check to fail. + // + // There are two states we're concerned about here: + // (1) The interrupt bit is set, and we need to fire the interrupt callback. + // (2) The stack limit has been exceeded, and we need to throw an error. + // + // Note that we can reach here if jitStackLimit is MAXADDR, but interrupt + // has not yet been set to 1. That's okay; it will be set to 1 very shortly, + // and in the interim we might just fire a few useless calls to + // CheckOverRecursed. #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false); #else JS_CHECK_RECURSION(cx, return false); #endif - gc::MaybeVerifyBarriers(cx); - return cx->runtime()->handleInterrupt(cx); + + if (cx->runtime()->interrupt) + return InterruptCheck(cx); + + return true; } // This function can get called in two contexts. In the usual context, it's @@ -167,8 +178,10 @@ CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame, JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false); #endif - gc::MaybeVerifyBarriers(cx); - return cx->runtime()->handleInterrupt(cx); + if (cx->runtime()->interrupt) + return InterruptCheck(cx); + + return true; } bool diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 9b54186df22e..2144df9b0be3 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -783,7 +783,7 @@ enum AsmJSImmKind AsmJSImm_PowD = AsmJSExit::Builtin_PowD, AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D, AsmJSImm_Runtime, - AsmJSImm_RuntimeInterruptUint32, + AsmJSImm_RuntimeInterrupt, AsmJSImm_StackLimit, AsmJSImm_ReportOverRecursed, AsmJSImm_OnDetached, diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 723daccb058e..dba7e5cf7403 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2029,48 +2029,68 @@ JS_GetExternalStringFinalizer(JSString *str) } static void -SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize) +SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize) { rt->nativeStackQuota[kind] = stackSize; + if (rt->nativeStackBase) + RecomputeStackLimit(rt, kind); +} +void +js::RecomputeStackLimit(JSRuntime *rt, StackKind kind) +{ + size_t stackSize = rt->nativeStackQuota[kind]; #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX; } else { MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize); - rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1; + rt->mainThread.nativeStackLimit[kind] = + rt->nativeStackBase + stackSize - 1; } #else if (stackSize == 0) { rt->mainThread.nativeStackLimit[kind] = 0; } else { MOZ_ASSERT(rt->nativeStackBase >= stackSize); - rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1); + rt->mainThread.nativeStackLimit[kind] = + rt->nativeStackBase - (stackSize - 1); } #endif + + // If there's no pending interrupt request set on the runtime's main thread's + // jitStackLimit, then update it so that it reflects the new nativeStacklimit. + // + // Note that, for now, we use the untrusted limit for ion. This is fine, + // because it's the most conservative limit, and if we hit it, we'll bail + // out of ion into the interpeter, which will do a proper recursion check. + if (kind == StackForUntrustedScript) { + JSRuntime::AutoLockForInterrupt lock(rt); + if (rt->mainThread.jitStackLimit != uintptr_t(-1)) { + rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind]; +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + rt->mainThread.jitStackLimit = jit::Simulator::StackLimit(); +#endif + } + } } JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize, +JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, + size_t trustedScriptStackSize, size_t untrustedScriptStackSize) { - MOZ_ASSERT(rt->requestDepth == 0); - + MOZ_ASSERT_IF(trustedScriptStackSize, + trustedScriptStackSize < systemCodeStackSize); if (!trustedScriptStackSize) trustedScriptStackSize = systemCodeStackSize; - else - MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize); - + MOZ_ASSERT_IF(untrustedScriptStackSize, + untrustedScriptStackSize < trustedScriptStackSize); if (!untrustedScriptStackSize) untrustedScriptStackSize = trustedScriptStackSize; - else - MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize); - - SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize); - SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize); - SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize); - - rt->mainThread.initJitStackLimit(); + SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize); + SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize); + SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize); } /************************************************************************/ diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9e0b781e6f7f..2c0bf8f5c3b1 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2272,9 +2272,6 @@ JS_GetExternalStringFinalizer(JSString *str); * The stack quotas for each kind of code should be monotonically descending, * and may be specified with this function. If 0 is passed for a given kind * of code, it defaults to the value of the next-highest-priority kind. - * - * This function may only be called immediately after the runtime is initialized - * and before any code is executed and/or interrupts requested. */ extern JS_PUBLIC_API(void) JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize, diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 412ae616d270..cb861567edfe 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -41,6 +41,7 @@ #include "gc/Marking.h" #include "jit/Ion.h" #include "js/CharacterEncoding.h" +#include "vm/Debugger.h" #include "vm/HelperThreads.h" #include "vm/Shape.h" @@ -970,6 +971,90 @@ js_GetErrorMessage(void *userRef, const unsigned errorNumber) return nullptr; } +bool +js::InvokeInterruptCallback(JSContext *cx) +{ + MOZ_ASSERT(cx->runtime()->requestDepth >= 1); + + JSRuntime *rt = cx->runtime(); + MOZ_ASSERT(rt->interrupt); + + // Reset the callback counter first, then run GC and yield. If another + // thread is racing us here we will accumulate another callback request + // which will be serviced at the next opportunity. + rt->interrupt = false; + + // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt + // callbacks. + rt->resetJitStackLimit(); + + cx->gcIfNeeded(); + + rt->interruptPar = false; + + // A worker thread may have requested an interrupt after finishing an Ion + // compilation. + jit::AttachFinishedCompilations(cx); + + // Important: Additional callbacks can occur inside the callback handler + // if it re-enters the JS engine. The embedding must ensure that the + // callback is disconnected before attempting such re-entry. + JSInterruptCallback cb = cx->runtime()->interruptCallback; + if (!cb) + return true; + + if (cb(cx)) { + // Debugger treats invoking the interrupt callback as a "step", so + // invoke the onStep handler. + if (cx->compartment()->debugMode()) { + ScriptFrameIter iter(cx); + if (iter.script()->stepModeEnabled()) { + RootedValue rval(cx); + switch (Debugger::onSingleStep(cx, &rval)) { + case JSTRAP_ERROR: + return false; + case JSTRAP_CONTINUE: + return true; + case JSTRAP_RETURN: + // See note in Debugger::propagateForcedReturn. + Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval); + return false; + case JSTRAP_THROW: + cx->setPendingException(rval); + return false; + default:; + } + } + } + + return true; + } + + // No need to set aside any pending exception here: ComputeStackString + // already does that. + JSString *stack = ComputeStackString(cx); + JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr; + + const char16_t *chars; + AutoStableStringChars stableChars(cx); + if (flat && stableChars.initTwoByte(cx, flat)) + chars = stableChars.twoByteRange().start().get(); + else + chars = MOZ_UTF16("(stack not available)"); + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, + JSMSG_TERMINATED, chars); + + return false; +} + +bool +js::HandleExecutionInterrupt(JSContext *cx) +{ + if (cx->runtime()->interrupt) + return InvokeInterruptCallback(cx); + return true; +} + ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind) : ContextFriendFields(rt), contextKind_(kind), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 9e30af416258..91e1dd45f4b8 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -289,7 +289,7 @@ struct ThreadSafeContext : ContextFriendFields, PropertyName *emptyString() { return runtime_->emptyString; } FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } void *runtimeAddressForJit() { return runtime_; } - void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); } + void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; } void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } void *stackLimitAddressForJitCode(StackKind kind); size_t gcSystemPageSize() { return gc::SystemPageSize(); } @@ -782,15 +782,33 @@ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; namespace js { +/* + * Invoke the interrupt callback and return false if the current execution + * is to be terminated. + */ +bool +InvokeInterruptCallback(JSContext *cx); + +bool +HandleExecutionInterrupt(JSContext *cx); + +/* + * Process any pending interrupt requests. Long-running inner loops in C++ must + * call this periodically to make sure they are interruptible --- that is, to + * make sure they do not prevent the slow script dialog from appearing. + * + * This can run a full GC or call the interrupt callback, which could do + * anything. In the browser, it displays the slow script dialog. + * + * If this returns true, the caller can continue; if false, the caller must + * break out of its loop. This happens if, for example, the user clicks "Stop + * script" on the slow script dialog; treat it as an uncatchable error. + */ MOZ_ALWAYS_INLINE bool CheckForInterrupt(JSContext *cx) { - // Add an inline fast-path since we have to check for interrupts in some hot - // C++ loops of library builtins. - JSRuntime *rt = cx->runtime(); - if (rt->hasPendingInterrupt()) - return rt->handleInterrupt(cx); - return true; + MOZ_ASSERT(cx->runtime()->requestDepth >= 1); + return !cx->runtime()->interrupt || InvokeInterruptCallback(cx); } /************************************************************************/ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index bf4ec955c7fd..1921c6b8b776 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -526,7 +526,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind) rt->gc.runDebugGC(); #endif - if (rt->hasPendingInterrupt()) { + if (rt->interrupt) { // Invoking the interrupt callback can fail and we can't usefully // handle that here. Just check in case we need to collect instead. ncx->gcIfNeeded(); diff --git a/js/src/jsnativestack.h b/js/src/jsnativestack.h index bcfe0ad88d27..86abcd08ac5c 100644 --- a/js/src/jsnativestack.h +++ b/js/src/jsnativestack.h @@ -18,7 +18,6 @@ inline uintptr_t GetNativeStackBase() { uintptr_t stackBase = reinterpret_cast(GetNativeStackBaseImpl()); - MOZ_ASSERT(stackBase != 0); MOZ_ASSERT(stackBase % sizeof(void *) == 0); return stackBase; } diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 7e1471311716..dd828fd771a4 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -1440,7 +1440,7 @@ ForkJoinShared::execute() // Sometimes a GC request occurs *just before* we enter into the // parallel section. Rather than enter into the parallel section // and then abort, we just check here and abort early. - if (cx_->runtime()->hasPendingInterruptPar()) + if (cx_->runtime()->interruptPar) return TP_RETRY_SEQUENTIALLY; AutoLockMonitor lock(*this); @@ -1518,7 +1518,7 @@ ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the // lock has not been initialized in these cases. - thisThread.initJitStackLimitPar(stackLimit); + thisThread.jitStackLimit = stackLimit; executePortion(&thisThread, worker); TlsPerThreadData.set(nullptr); @@ -1551,7 +1551,7 @@ ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker) // // Thus, use GetNativeStackLimit instead of just propagating the // main thread's. - thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_)); + thisThread.jitStackLimit = GetNativeStackLimit(cx_); executePortion(&thisThread, worker); TlsPerThreadData.set(oldData); @@ -1647,7 +1647,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke void ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx) { - MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar()); + MOZ_ASSERT(cx_->runtime()->interruptPar); // The GC Needed flag should not be set during parallel // execution. Instead, one of the requestGC() or // requestZoneGC() methods should be invoked. @@ -1826,7 +1826,7 @@ ForkJoinContext::hasAcquiredJSContext() const bool ForkJoinContext::check() { - if (runtime()->hasPendingInterruptPar()) { + if (runtime()->interruptPar) { shared_->setAbortFlagDueToInterrupt(*this); return false; } @@ -2273,6 +2273,13 @@ js::ParallelTestsShouldPass(JSContext *cx) cx->runtime()->gcZeal() == 0; } +void +js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode) +{ + if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon) + rt->interruptPar = true; +} + bool js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp) { diff --git a/js/src/vm/ForkJoin.h b/js/src/vm/ForkJoin.h index 8554d4f87f7d..55af6f09f7a1 100644 --- a/js/src/vm/ForkJoin.h +++ b/js/src/vm/ForkJoin.h @@ -546,6 +546,8 @@ bool InExclusiveParallelSection(); bool ParallelTestsShouldPass(JSContext *cx); +void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode); + bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp); extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo; diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 6429469b750e..6e2ac67120dc 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -621,8 +621,14 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, // in the bytecode interpreter, which can execute while tolerating // future interrupts. Otherwise, if we keep getting interrupted we // will never finish executing the regexp. - if (cx->runtime()->hasPendingInterrupt()) { - if (!cx->runtime()->handleInterrupt(cx)) + bool interrupted; + { + JSRuntime::AutoLockForInterrupt lock(cx->runtime()); + interrupted = cx->runtime()->interrupt; + } + + if (interrupted) { + if (!InvokeInterruptCallback(cx)) return RegExpRunStatus_Error; break; } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 42c69c925f80..eea4a804e226 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -36,7 +36,6 @@ #include "jit/PcScriptCache.h" #include "js/MemoryMetrics.h" #include "js/SliceBudget.h" -#include "vm/Debugger.h" #include "jscntxtinlines.h" #include "jsgcinlines.h" @@ -74,7 +73,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime) runtime_(runtime), jitTop(nullptr), jitJSContext(nullptr), - jitStackLimit_(0xbad), + jitStackLimit(0), #ifdef JS_TRACE_LOGGING traceLogger(nullptr), #endif @@ -136,8 +135,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) ), mainThread(this), parentRuntime(parentRuntime), - interrupt_(false), - interruptPar_(false), + interrupt(false), + interruptPar(false), handlingSignal(false), interruptCallback(nullptr), interruptLock(nullptr), @@ -157,7 +156,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) execAlloc_(nullptr), jitRuntime_(nullptr), selfHostingGlobal_(nullptr), - nativeStackBase(GetNativeStackBase()), + nativeStackBase(0), cxCallback(nullptr), destroyCompartmentCallback(nullptr), destroyZoneCallback(nullptr), @@ -323,6 +322,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) return false; #endif + nativeStackBase = GetNativeStackBase(); + jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint(); jitSupportsSimd = js::jit::JitSupportsSimd(); @@ -464,6 +465,17 @@ NewObjectCache::clearNurseryObjects(JSRuntime *rt) #endif } +void +JSRuntime::resetJitStackLimit() +{ + AutoLockForInterrupt lock(this); + mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]); + +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + mainThread.setJitStackLimit(js::jit::Simulator::StackLimit()); +#endif +} + void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes) { @@ -518,120 +530,33 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim #endif } -static bool -InvokeInterruptCallback(JSContext *cx) -{ - MOZ_ASSERT(cx->runtime()->requestDepth >= 1); - - cx->gcIfNeeded(); - - // A worker thread may have requested an interrupt after finishing an Ion - // compilation. - jit::AttachFinishedCompilations(cx); - - // Important: Additional callbacks can occur inside the callback handler - // if it re-enters the JS engine. The embedding must ensure that the - // callback is disconnected before attempting such re-entry. - JSInterruptCallback cb = cx->runtime()->interruptCallback; - if (!cb) - return true; - - if (cb(cx)) { - // Debugger treats invoking the interrupt callback as a "step", so - // invoke the onStep handler. - if (cx->compartment()->debugMode()) { - ScriptFrameIter iter(cx); - if (iter.script()->stepModeEnabled()) { - RootedValue rval(cx); - switch (Debugger::onSingleStep(cx, &rval)) { - case JSTRAP_ERROR: - return false; - case JSTRAP_CONTINUE: - return true; - case JSTRAP_RETURN: - // See note in Debugger::propagateForcedReturn. - Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval); - return false; - case JSTRAP_THROW: - cx->setPendingException(rval); - return false; - default:; - } - } - } - - return true; - } - - // No need to set aside any pending exception here: ComputeStackString - // already does that. - JSString *stack = ComputeStackString(cx); - JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr; - - const char16_t *chars; - AutoStableStringChars stableChars(cx); - if (flat && stableChars.initTwoByte(cx, flat)) - chars = stableChars.twoByteRange().start().get(); - else - chars = MOZ_UTF16("(stack not available)"); - JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, - JSMSG_TERMINATED, chars); - - return false; -} - -void -PerThreadData::resetJitStackLimit() -{ - // Note that, for now, we use the untrusted limit for ion. This is fine, - // because it's the most conservative limit, and if we hit it, we'll bail - // out of ion into the interpeter, which will do a proper recursion check. -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - jitStackLimit_ = jit::Simulator::StackLimit(); -#else - jitStackLimit_ = nativeStackLimit[StackForUntrustedScript]; -#endif -} - -void -PerThreadData::initJitStackLimit() -{ - resetJitStackLimit(); -} - -void -PerThreadData::initJitStackLimitPar(uintptr_t limit) -{ - jitStackLimit_ = limit; -} - void JSRuntime::requestInterrupt(InterruptMode mode) { - interrupt_ = true; - interruptPar_ = true; - mainThread.jitStackLimit_ = UINTPTR_MAX; + AutoLockForInterrupt lock(this); + /* + * Invalidate ionTop to trigger its over-recursion check. Note this must be + * set before interrupt, to avoid racing with js::InvokeInterruptCallback, + * into a weird state where interrupt is stuck at 0 but jitStackLimit is + * MAXADDR. + */ + mainThread.setJitStackLimit(-1); + + interrupt = true; + + RequestInterruptForForkJoin(this, mode); + + /* + * asm.js and normal Ion code optionally use memory protection and signal + * handlers to halt running code. + */ if (canUseSignalHandlers()) { - AutoLockForInterrupt lock(this); RequestInterruptForAsmJSCode(this, mode); jit::RequestInterruptForIonCode(this, mode); } } -bool -JSRuntime::handleInterrupt(JSContext *cx) -{ - MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); - if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) { - interrupt_ = false; - interruptPar_ = false; - mainThread.resetJitStackLimit(); - return InvokeInterruptCallback(cx); - } - return true; -} - jit::ExecutableAllocator * JSRuntime::createExecutableAllocator(JSContext *cx) { diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 653478881cb7..0a40112557c4 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -519,20 +519,13 @@ class PerThreadData : public PerThreadDataFriendFields */ JSContext *jitJSContext; - /* See comment for JSRuntime::interrupt_. */ - private: - mozilla::Atomic jitStackLimit_; - void resetJitStackLimit(); - friend struct ::JSRuntime; - public: - void initJitStackLimit(); - void initJitStackLimitPar(uintptr_t limit); + /* + * The stack limit checked by JIT code. This stack limit may be temporarily + * set to null to force JIT code to exit (e.g., for the operation callback). + */ + uintptr_t jitStackLimit; - uintptr_t jitStackLimit() const { return jitStackLimit_; } - - // For read-only JIT use: - void *addressofJitStackLimit() { return &jitStackLimit_; } - static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); } + inline void setJitStackLimit(uintptr_t limit); // Information about the heap allocated backtrack stack used by RegExp JIT code. irregexp::RegExpStack regexpStack; @@ -685,6 +678,8 @@ class PerThreadData : public PerThreadDataFriendFields class AutoLockForExclusiveAccess; +void RecomputeStackLimit(JSRuntime *rt, StackKind kind); + } // namespace js struct JSRuntime : public JS::shadow::Runtime, @@ -708,56 +703,18 @@ struct JSRuntime : public JS::shadow::Runtime, */ JSRuntime *parentRuntime; - private: - mozilla::Atomic interrupt_; - mozilla::Atomic interruptPar_; - public: + /* + * If true, we've been asked to call the interrupt callback as soon as + * possible. + */ + mozilla::Atomic interrupt; - enum InterruptMode { - RequestInterruptMainThread, - RequestInterruptAnyThread, - RequestInterruptAnyThreadDontStopIon, - RequestInterruptAnyThreadForkJoin - }; - - // Any thread can call requestInterrupt() to request that the main JS thread - // stop running and call the interrupt callback (allowing the interrupt - // callback to halt execution). To stop the main JS thread, requestInterrupt - // sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to - // UINTPTR_MAX). The JS engine must continually poll one of these fields - // and call handleInterrupt if either field has the interrupt value. (The - // point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already - // needs to guard on jitStackLimit_ in every function prologue to avoid - // stack overflow, so we avoid a second branch on interrupt_ by setting - // jitStackLimit_ to a value that is guaranteed to fail the guard.) - // - // Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed - // Atomic so, while the writes are guaranteed to eventually be visible to - // the main thread, it can happen in any order. handleInterrupt calls the - // interrupt callback if either is set, so it really doesn't matter as long - // as the JS engine is continually polling at least one field. In corner - // cases, this relaxed ordering could lead to an interrupt handler being - // called twice in succession after a single requestInterrupt call, but - // that's fine. - void requestInterrupt(InterruptMode mode); - bool handleInterrupt(JSContext *cx); - - MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const { - return interrupt_; - } - MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const { - return interruptPar_; - } - - // For read-only JIT use: - void *addressOfInterruptUint32() { - static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers"); - return &interrupt_; - } - void *addressOfInterruptParUint32() { - static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers"); - return &interruptPar_; - } + /* + * If non-zero, ForkJoin should service an interrupt. This is a separate + * flag from |interrupt| because we cannot use the mprotect trick with PJS + * code and ignore the TriggerCallbackAnyThreadDontStopIon trigger. + */ + mozilla::Atomic interruptPar; /* Set when handling a signal for a thread associated with this runtime. */ bool handlingSignal; @@ -949,7 +906,7 @@ struct JSRuntime : public JS::shadow::Runtime, void setDefaultVersion(JSVersion v) { defaultVersion_ = v; } /* Base address of the native stack for the current thread. */ - const uintptr_t nativeStackBase; + uintptr_t nativeStackBase; /* The native stack size limit that runtime should not exceed. */ size_t nativeStackQuota[js::StackKindCount]; @@ -1315,6 +1272,10 @@ struct JSRuntime : public JS::shadow::Runtime, bool jitSupportsFloatingPoint; bool jitSupportsSimd; + // Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1) + // has been noticed by Ion/Baseline. + void resetJitStackLimit(); + // Cache for jit::GetPcScript(). js::jit::PcScriptCache *ionPcScriptCache; @@ -1375,6 +1336,17 @@ struct JSRuntime : public JS::shadow::Runtime, /* onOutOfMemory but can call the largeAllocationFailureCallback. */ JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes); + // Ways in which the interrupt callback on the runtime can be triggered, + // varying based on which thread is triggering the callback. + enum InterruptMode { + RequestInterruptMainThread, + RequestInterruptAnyThread, + RequestInterruptAnyThreadDontStopIon, + RequestInterruptAnyThreadForkJoin + }; + + void requestInterrupt(InterruptMode mode); + void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime); private: @@ -1598,6 +1570,13 @@ class MOZ_STACK_CLASS AutoKeepAtoms } }; +inline void +PerThreadData::setJitStackLimit(uintptr_t limit) +{ + MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock()); + jitStackLimit = limit; +} + inline JSRuntime * PerThreadData::runtimeFromMainThread() {