diff --git a/dom/apps/src/PermissionsTable.jsm b/dom/apps/src/PermissionsTable.jsm index cc318a37ef7..9b6e0ca3ace 100644 --- a/dom/apps/src/PermissionsTable.jsm +++ b/dom/apps/src/PermissionsTable.jsm @@ -39,7 +39,7 @@ this.PermissionsTable = { geolocation: { }, camera: { app: DENY_ACTION, - privileged: DENY_ACTION, + privileged: PROMPT_ACTION, certified: ALLOW_ACTION }, alarms: { diff --git a/dom/camera/DOMCameraManager.cpp b/dom/camera/DOMCameraManager.cpp index 2d08ee90084..6fd9ffdefb8 100644 --- a/dom/camera/DOMCameraManager.cpp +++ b/dom/camera/DOMCameraManager.cpp @@ -7,6 +7,7 @@ #include "jsapi.h" #include "nsPIDOMWindow.h" #include "mozilla/Services.h" +#include "nsContentPermissionHelper.h" #include "nsObserverService.h" #include "nsIPermissionManager.h" #include "DOMCameraControl.h" @@ -14,6 +15,9 @@ #include "CameraCommon.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/CameraManagerBinding.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/TabChild.h" +#include "PCOMContentPermissionRequestChild.h" using namespace mozilla; using namespace mozilla::dom; @@ -50,6 +54,7 @@ WindowTable* nsDOMCameraManager::sActiveWindows = nullptr; nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) : mWindowId(aWindow->WindowID()) + , mPermission(nsIPermissionManager::DENY_ACTION) , mWindow(aWindow) { /* member initializers and constructor code */ @@ -80,7 +85,8 @@ nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow) uint32_t permission = nsIPermissionManager::DENY_ACTION; permMgr->TestPermissionFromWindow(aWindow, "camera", &permission); - if (permission != nsIPermissionManager::ALLOW_ACTION) { + if (permission != nsIPermissionManager::ALLOW_ACTION && + permission != nsIPermissionManager::PROMPT_ACTION) { return false; } @@ -105,11 +111,190 @@ nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow) return cameraManager.forget(); } +class CameraPermissionRequest : public nsIContentPermissionRequest + , public PCOMContentPermissionRequestChild + , public nsIRunnable +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSICONTENTPERMISSIONREQUEST + NS_DECL_NSIRUNNABLE + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest, + nsIContentPermissionRequest) + + CameraPermissionRequest(nsIPrincipal* aPrincipal, + nsPIDOMWindow* aWindow, + nsRefPtr aManager, + uint32_t aCameraId, + const CameraConfiguration& aInitialConfig, + nsRefPtr aOnSuccess, + nsRefPtr aOnError) + : mPrincipal(aPrincipal) + , mWindow(aWindow) + , mCameraManager(aManager) + , mCameraId(aCameraId) + , mInitialConfig(aInitialConfig) + , mOnSuccess(aOnSuccess) + , mOnError(aOnError) + { + } + + virtual ~CameraPermissionRequest() + { + } + + bool Recv__delete__(const bool& aAllow, + const InfallibleTArray& choices); + + void IPDLRelease() + { + Release(); + } + +protected: + nsresult DispatchCallback(uint32_t aPermission); + void CallAllow(); + void CallCancel(); + nsCOMPtr mPrincipal; + nsCOMPtr mWindow; + nsRefPtr mCameraManager; + uint32_t mCameraId; + CameraConfiguration mInitialConfig; + nsRefPtr mOnSuccess; + nsRefPtr mOnError; +}; + +NS_IMPL_CYCLE_COLLECTION_3(CameraPermissionRequest, mWindow, mOnSuccess, mOnError) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest) + NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) + NS_INTERFACE_MAP_ENTRY(nsIRunnable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest) + +NS_IMETHODIMP +CameraPermissionRequest::Run() +{ + if (XRE_GetProcessType() == GeckoProcessType_Content) { + TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); + if (!child) { + return NS_ERROR_NOT_AVAILABLE; + } + + // Retain a reference so the object isn't deleted without IPDL's knowledge. + // Corresponding release occurs in DeallocPContentPermissionRequest. + AddRef(); + + nsTArray permArray; + nsTArray emptyOptions; + permArray.AppendElement(PermissionRequest( + NS_LITERAL_CSTRING("camera"), + NS_LITERAL_CSTRING("unused"), + emptyOptions)); + child->SendPContentPermissionRequestConstructor(this, permArray, + IPC::Principal(mPrincipal)); + + Sendprompt(); + return NS_OK; + } + + nsCOMPtr prompt = + do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); + if (prompt) { + prompt->Prompt(this); + } + + return NS_OK; +} + +bool +CameraPermissionRequest::Recv__delete__(const bool& aAllow, + const InfallibleTArray& choices) +{ + if (aAllow) { + Allow(JS::UndefinedHandleValue); + } else { + Cancel(); + } + return true; +} + +NS_IMETHODIMP +CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal) +{ + NS_ADDREF(*aRequestingPrincipal = mPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +CameraPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) +{ + NS_ADDREF(*aRequestingWindow = mWindow); + return NS_OK; +} + +NS_IMETHODIMP +CameraPermissionRequest::GetElement(nsIDOMElement** aElement) +{ + *aElement = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +CameraPermissionRequest::Cancel() +{ + return DispatchCallback(nsIPermissionManager::DENY_ACTION); +} + +NS_IMETHODIMP +CameraPermissionRequest::Allow(JS::HandleValue aChoices) +{ + MOZ_ASSERT(aChoices.isUndefined()); + return DispatchCallback(nsIPermissionManager::ALLOW_ACTION); +} + +nsresult +CameraPermissionRequest::DispatchCallback(uint32_t aPermission) +{ + nsCOMPtr callbackRunnable; + if (aPermission == nsIPermissionManager::ALLOW_ACTION) { + callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); + } else { + callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); + } + return NS_DispatchToMainThread(callbackRunnable); +} + +void +CameraPermissionRequest::CallAllow() +{ + mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError); +} + +void +CameraPermissionRequest::CallCancel() +{ + mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError); +} + +NS_IMETHODIMP +CameraPermissionRequest::GetTypes(nsIArray** aTypes) +{ + nsTArray emptyOptions; + return CreatePermissionArray(NS_LITERAL_CSTRING("camera"), + NS_LITERAL_CSTRING("unused"), + emptyOptions, + aTypes); +} + void nsDOMCameraManager::GetCamera(const nsAString& aCamera, const CameraConfiguration& aInitialConfig, GetCameraCallback& aOnSuccess, - const Optional >& aOnError, + const OptionalNonNullCameraErrorCallback& aOnError, ErrorResult& aRv) { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); @@ -124,14 +309,56 @@ nsDOMCameraManager::GetCamera(const nsAString& aCamera, errorCallback = &aOnError.Value(); } + if (mPermission == nsIPermissionManager::ALLOW_ACTION) { + PermissionAllowed(cameraId, aInitialConfig, &aOnSuccess, errorCallback); + return; + } + + nsCOMPtr sop = do_QueryInterface(mWindow); + if (!sop) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + nsCOMPtr principal = sop->GetPrincipal(); + + nsCOMPtr permissionRequest = + new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig, + &aOnSuccess, errorCallback); + + NS_DispatchToMainThread(permissionRequest); +} + +void +nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId, + const CameraConfiguration& aInitialConfig, + GetCameraCallback* aOnSuccess, + CameraErrorCallback* aOnError) +{ + mPermission = nsIPermissionManager::ALLOW_ACTION; + // Creating this object will trigger the aOnSuccess callback // (or the aOnError one, if it fails). nsRefPtr cameraControl = - new nsDOMCameraControl(cameraId, aInitialConfig, &aOnSuccess, errorCallback, mWindow); + new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, mWindow); Register(cameraControl); } +void +nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId, + const CameraConfiguration& aInitialConfig, + GetCameraCallback* aOnSuccess, + CameraErrorCallback* aOnError) +{ + mPermission = nsIPermissionManager::DENY_ACTION; + + if (aOnError) { + ErrorResult ignored; + aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored); + } +} + void nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) { diff --git a/dom/camera/DOMCameraManager.h b/dom/camera/DOMCameraManager.h index ac55937212c..364d3937b34 100644 --- a/dom/camera/DOMCameraManager.h +++ b/dom/camera/DOMCameraManager.h @@ -32,6 +32,8 @@ namespace mozilla { typedef nsTArray > CameraControls; typedef nsClassHashtable WindowTable; +typedef mozilla::dom::Optional> + OptionalNonNullCameraErrorCallback; class nsDOMCameraManager MOZ_FINAL : public nsIObserver @@ -52,11 +54,21 @@ public: void Register(mozilla::nsDOMCameraControl* aDOMCameraControl); void OnNavigation(uint64_t aWindowId); + void PermissionAllowed(uint32_t aCameraId, + const mozilla::dom::CameraConfiguration& aOptions, + mozilla::dom::GetCameraCallback* aOnSuccess, + mozilla::dom::CameraErrorCallback* aOnError); + + void PermissionCancelled(uint32_t aCameraId, + const mozilla::dom::CameraConfiguration& aOptions, + mozilla::dom::GetCameraCallback* aOnSuccess, + mozilla::dom::CameraErrorCallback* aOnError); + // WebIDL void GetCamera(const nsAString& aCamera, const mozilla::dom::CameraConfiguration& aOptions, mozilla::dom::GetCameraCallback& aOnSuccess, - const mozilla::dom::Optional >& aOnError, + const OptionalNonNullCameraErrorCallback& aOnError, mozilla::ErrorResult& aRv); void GetListOfCameras(nsTArray& aList, mozilla::ErrorResult& aRv); @@ -77,6 +89,7 @@ private: protected: uint64_t mWindowId; + uint32_t mPermission; nsCOMPtr mWindow; /** * 'sActiveWindows' is only ever accessed while in the Main Thread,