michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "DOMCameraManager.h" michael@0: #include "nsDebug.h" michael@0: #include "jsapi.h" michael@0: #include "Navigator.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsContentPermissionHelper.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPermissionManager.h" michael@0: #include "DOMCameraControl.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "CameraCommon.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/CameraManagerBinding.h" michael@0: #include "mozilla/dom/PermissionMessageUtils.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "PCOMContentPermissionRequestChild.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager) michael@0: michael@0: /** michael@0: * Global camera logging object michael@0: * michael@0: * Set the NSPR_LOG_MODULES environment variable to enable logging michael@0: * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5 michael@0: */ michael@0: PRLogModuleInfo* michael@0: GetCameraLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) { michael@0: sLog = PR_NewLogModule("Camera"); michael@0: } michael@0: return sLog; michael@0: } michael@0: michael@0: WindowTable* nsDOMCameraManager::sActiveWindows = nullptr; michael@0: michael@0: nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) michael@0: : mWindowId(aWindow->WindowID()) michael@0: , mPermission(nsIPermissionManager::DENY_ACTION) michael@0: , mWindow(aWindow) michael@0: { michael@0: /* member initializers and constructor code */ michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId); michael@0: MOZ_COUNT_CTOR(nsDOMCameraManager); michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: nsDOMCameraManager::~nsDOMCameraManager() michael@0: { michael@0: /* destructor code */ michael@0: MOZ_COUNT_DTOR(nsDOMCameraManager); michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsDOMCameraManager::GetListOfCameras(nsTArray& aList, ErrorResult& aRv) michael@0: { michael@0: aRv = ICameraControl::GetListOfCameras(aList); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsDOMCameraManager::HasSupport(JSContext* aCx, JSObject* aGlobal) michael@0: { michael@0: return Navigator::HasCameraSupport(aCx, aGlobal); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow) michael@0: { michael@0: nsCOMPtr permMgr = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: NS_ENSURE_TRUE(permMgr, false); michael@0: michael@0: uint32_t permission = nsIPermissionManager::DENY_ACTION; michael@0: permMgr->TestPermissionFromWindow(aWindow, "camera", &permission); michael@0: if (permission != nsIPermissionManager::ALLOW_ACTION && michael@0: permission != nsIPermissionManager::PROMPT_ACTION) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ michael@0: already_AddRefed michael@0: nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow) michael@0: { michael@0: // Initialize the shared active window tracker michael@0: if (!sActiveWindows) { michael@0: sActiveWindows = new WindowTable(); michael@0: } michael@0: michael@0: nsRefPtr cameraManager = michael@0: new nsDOMCameraManager(aWindow); michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: obs->AddObserver(cameraManager, "xpcom-shutdown", true); michael@0: michael@0: return cameraManager.forget(); michael@0: } michael@0: michael@0: class CameraPermissionRequest : public nsIContentPermissionRequest michael@0: , public PCOMContentPermissionRequestChild michael@0: , public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSICONTENTPERMISSIONREQUEST michael@0: NS_DECL_NSIRUNNABLE michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest, michael@0: nsIContentPermissionRequest) michael@0: michael@0: CameraPermissionRequest(nsIPrincipal* aPrincipal, michael@0: nsPIDOMWindow* aWindow, michael@0: nsRefPtr aManager, michael@0: uint32_t aCameraId, michael@0: const CameraConfiguration& aInitialConfig, michael@0: nsRefPtr aOnSuccess, michael@0: nsRefPtr aOnError) michael@0: : mPrincipal(aPrincipal) michael@0: , mWindow(aWindow) michael@0: , mCameraManager(aManager) michael@0: , mCameraId(aCameraId) michael@0: , mInitialConfig(aInitialConfig) michael@0: , mOnSuccess(aOnSuccess) michael@0: , mOnError(aOnError) michael@0: { michael@0: } michael@0: michael@0: virtual ~CameraPermissionRequest() michael@0: { michael@0: } michael@0: michael@0: bool Recv__delete__(const bool& aAllow, michael@0: const InfallibleTArray& choices); michael@0: michael@0: void IPDLRelease() michael@0: { michael@0: Release(); michael@0: } michael@0: michael@0: protected: michael@0: nsresult DispatchCallback(uint32_t aPermission); michael@0: void CallAllow(); michael@0: void CallCancel(); michael@0: nsCOMPtr mPrincipal; michael@0: nsCOMPtr mWindow; michael@0: nsRefPtr mCameraManager; michael@0: uint32_t mCameraId; michael@0: CameraConfiguration mInitialConfig; michael@0: nsRefPtr mOnSuccess; michael@0: nsRefPtr mOnError; michael@0: }; michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mOnSuccess, mOnError) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRunnable) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest) michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::Run() michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); michael@0: if (!child) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Retain a reference so the object isn't deleted without IPDL's knowledge. michael@0: // Corresponding release occurs in DeallocPContentPermissionRequest. michael@0: AddRef(); michael@0: michael@0: nsTArray permArray; michael@0: nsTArray emptyOptions; michael@0: permArray.AppendElement(PermissionRequest( michael@0: NS_LITERAL_CSTRING("camera"), michael@0: NS_LITERAL_CSTRING("unused"), michael@0: emptyOptions)); michael@0: child->SendPContentPermissionRequestConstructor(this, permArray, michael@0: IPC::Principal(mPrincipal)); michael@0: michael@0: Sendprompt(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr prompt = michael@0: do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); michael@0: if (prompt) { michael@0: prompt->Prompt(this); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: CameraPermissionRequest::Recv__delete__(const bool& aAllow, michael@0: const InfallibleTArray& choices) michael@0: { michael@0: if (aAllow) { michael@0: Allow(JS::UndefinedHandleValue); michael@0: } else { michael@0: Cancel(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal) michael@0: { michael@0: NS_ADDREF(*aRequestingPrincipal = mPrincipal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) michael@0: { michael@0: NS_ADDREF(*aRequestingWindow = mWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::GetElement(nsIDOMElement** aElement) michael@0: { michael@0: *aElement = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::Cancel() michael@0: { michael@0: return DispatchCallback(nsIPermissionManager::DENY_ACTION); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::Allow(JS::HandleValue aChoices) michael@0: { michael@0: MOZ_ASSERT(aChoices.isUndefined()); michael@0: return DispatchCallback(nsIPermissionManager::ALLOW_ACTION); michael@0: } michael@0: michael@0: nsresult michael@0: CameraPermissionRequest::DispatchCallback(uint32_t aPermission) michael@0: { michael@0: nsCOMPtr callbackRunnable; michael@0: if (aPermission == nsIPermissionManager::ALLOW_ACTION) { michael@0: callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); michael@0: } else { michael@0: callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); michael@0: } michael@0: return NS_DispatchToMainThread(callbackRunnable); michael@0: } michael@0: michael@0: void michael@0: CameraPermissionRequest::CallAllow() michael@0: { michael@0: mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError); michael@0: } michael@0: michael@0: void michael@0: CameraPermissionRequest::CallCancel() michael@0: { michael@0: mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CameraPermissionRequest::GetTypes(nsIArray** aTypes) michael@0: { michael@0: nsTArray emptyOptions; michael@0: return CreatePermissionArray(NS_LITERAL_CSTRING("camera"), michael@0: NS_LITERAL_CSTRING("unused"), michael@0: emptyOptions, michael@0: aTypes); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::GetCamera(const nsAString& aCamera, michael@0: const CameraConfiguration& aInitialConfig, michael@0: GetCameraCallback& aOnSuccess, michael@0: const OptionalNonNullCameraErrorCallback& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: michael@0: uint32_t cameraId = 0; // back (or forward-facing) camera by default michael@0: if (aCamera.EqualsLiteral("front")) { michael@0: cameraId = 1; michael@0: } michael@0: michael@0: nsRefPtr errorCallback = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: errorCallback = &aOnError.Value(); michael@0: } michael@0: michael@0: if (mPermission == nsIPermissionManager::ALLOW_ACTION) { michael@0: PermissionAllowed(cameraId, aInitialConfig, &aOnSuccess, errorCallback); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr sop = do_QueryInterface(mWindow); michael@0: if (!sop) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr principal = sop->GetPrincipal(); michael@0: michael@0: nsCOMPtr permissionRequest = michael@0: new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig, michael@0: &aOnSuccess, errorCallback); michael@0: michael@0: NS_DispatchToMainThread(permissionRequest); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId, michael@0: const CameraConfiguration& aInitialConfig, michael@0: GetCameraCallback* aOnSuccess, michael@0: CameraErrorCallback* aOnError) michael@0: { michael@0: mPermission = nsIPermissionManager::ALLOW_ACTION; michael@0: michael@0: // Creating this object will trigger the aOnSuccess callback michael@0: // (or the aOnError one, if it fails). michael@0: nsRefPtr cameraControl = michael@0: new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, mWindow); michael@0: michael@0: Register(cameraControl); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId, michael@0: const CameraConfiguration& aInitialConfig, michael@0: GetCameraCallback* aOnSuccess, michael@0: CameraErrorCallback* aOnError) michael@0: { michael@0: mPermission = nsIPermissionManager::DENY_ACTION; michael@0: michael@0: if (aOnError) { michael@0: ErrorResult ignored; michael@0: aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) michael@0: { michael@0: DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Put the camera control into the hash table michael@0: CameraControls* controls = sActiveWindows->Get(mWindowId); michael@0: if (!controls) { michael@0: controls = new CameraControls; michael@0: sActiveWindows->Put(mWindowId, controls); michael@0: } michael@0: controls->AppendElement(aDOMCameraControl); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::Shutdown(uint64_t aWindowId) michael@0: { michael@0: DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: CameraControls* controls = sActiveWindows->Get(aWindowId); michael@0: if (!controls) { michael@0: return; michael@0: } michael@0: michael@0: uint32_t length = controls->Length(); michael@0: for (uint32_t i = 0; i < length; i++) { michael@0: nsRefPtr cameraControl = controls->ElementAt(i); michael@0: cameraControl->Shutdown(); michael@0: } michael@0: controls->Clear(); michael@0: michael@0: sActiveWindows->Remove(aWindowId); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::XpComShutdown() michael@0: { michael@0: DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n"); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: obs->RemoveObserver(this, "xpcom-shutdown"); michael@0: michael@0: delete sActiveWindows; michael@0: sActiveWindows = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) michael@0: { michael@0: if (strcmp(aTopic, "xpcom-shutdown") == 0) { michael@0: XpComShutdown(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraManager::OnNavigation(uint64_t aWindowId) michael@0: { michael@0: DOM_CAMERA_LOGI(">>> OnNavigation event\n"); michael@0: Shutdown(aWindowId); michael@0: } michael@0: michael@0: bool michael@0: nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!sActiveWindows) { michael@0: return false; michael@0: } michael@0: michael@0: return !!sActiveWindows->Get(aWindowId); michael@0: } michael@0: michael@0: JSObject* michael@0: nsDOMCameraManager::WrapObject(JSContext* aCx) michael@0: { michael@0: return CameraManagerBinding::Wrap(aCx, this); michael@0: }