Bug 1487204 - Add platform support for calling authorizationStatusForMediaType and requestAccessForMediaType from JS r=spohl
Add a new interface nsIOSPermissionRequest for querying the staus of access permissions for audio/video media capture and requesting access to audio/video capture devices. Provides an implementation for macOS 10.14 and a default implementation (nsOSPermissionRequestBase) for earlier macOS versions and other platforms. The default implementation always returns status indicating access is allowed. Differential Revision: https://phabricator.services.mozilla.com/D4601
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "nsURILoader.h"
|
||||
#include "nsDocLoader.h"
|
||||
#include "nsOSHelperAppService.h"
|
||||
#include "nsOSPermissionRequest.h"
|
||||
#include "nsExternalProtocolHandler.h"
|
||||
#include "nsPrefetchService.h"
|
||||
#include "nsOfflineCacheUpdate.h"
|
||||
@@ -91,6 +92,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalURLHandlerService)
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ContentHandlerService, Init)
|
||||
|
||||
// OS access permissions
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsOSPermissionRequest)
|
||||
|
||||
// session history
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHEntry)
|
||||
|
||||
@@ -106,6 +110,7 @@ NS_DEFINE_NAMED_CID(NS_PREFETCHSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_OFFLINECACHEUPDATESERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_OFFLINECACHEUPDATE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_LOCALHANDLERAPP_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_OSPERMISSIONREQUEST_CID);
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
NS_DEFINE_NAMED_CID(NS_DBUSHANDLERAPP_CID);
|
||||
#endif
|
||||
@@ -125,6 +130,7 @@ const mozilla::Module::CIDEntry kDocShellCIDs[] = {
|
||||
{ &kNS_URI_LOADER_CID, false, nullptr, nsURILoaderConstructor },
|
||||
{ &kNS_DOCUMENTLOADER_SERVICE_CID, false, nullptr, nsDocLoaderConstructor },
|
||||
{ &kNS_EXTERNALHELPERAPPSERVICE_CID, false, nullptr, nsOSHelperAppServiceConstructor },
|
||||
{ &kNS_OSPERMISSIONREQUEST_CID, false, nullptr, nsOSPermissionRequestConstructor },
|
||||
{ &kNS_CONTENTHANDLERSERVICE_CID, false, nullptr, ContentHandlerServiceConstructor,
|
||||
mozilla::Module::CONTENT_PROCESS_ONLY },
|
||||
{ &kNS_EXTERNALPROTOCOLHANDLER_CID, false, nullptr, nsExternalProtocolHandlerConstructor },
|
||||
@@ -197,6 +203,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
|
||||
{ NS_SHENTRY_CONTRACTID, &kNS_SHENTRY_CID },
|
||||
{ NS_LOADCONTEXT_CONTRACTID, &kNS_LOADCONTEXT_CID },
|
||||
{ NS_PRIVATELOADCONTEXT_CONTRACTID, &kNS_PRIVATELOADCONTEXT_CID },
|
||||
{ NS_OSPERMISSIONREQUEST_CONTRACTID, &kNS_OSPERMISSIONREQUEST_CID, mozilla::Module::MAIN_PROCESS_ONLY },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
# 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/.
|
||||
|
||||
SOURCES += ['CoreLocationLocationProvider.mm']
|
||||
SOURCES += [
|
||||
'CoreLocationLocationProvider.mm',
|
||||
'nsOSPermissionRequest.mm',
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
'nsOSPermissionRequest.h',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
|
||||
30
dom/system/mac/nsOSPermissionRequest.h
Normal file
30
dom/system/mac/nsOSPermissionRequest.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsOSPermissionRequest_h__
|
||||
#define nsOSPermissionRequest_h__
|
||||
|
||||
#include "nsOSPermissionRequestBase.h"
|
||||
|
||||
class nsOSPermissionRequest : public nsOSPermissionRequestBase
|
||||
{
|
||||
public:
|
||||
nsOSPermissionRequest() {};
|
||||
|
||||
NS_IMETHOD GetAudioCapturePermissionState(uint16_t* aAudio) override;
|
||||
|
||||
NS_IMETHOD GetVideoCapturePermissionState(uint16_t* aVideo) override;
|
||||
|
||||
NS_IMETHOD RequestVideoCapturePermission(JSContext* aCx,
|
||||
mozilla::dom::Promise** aPromiseOut)
|
||||
override;
|
||||
|
||||
NS_IMETHOD RequestAudioCapturePermission(JSContext* aCx,
|
||||
mozilla::dom::Promise** aPromiseOut)
|
||||
override;
|
||||
};
|
||||
|
||||
#endif
|
||||
77
dom/system/mac/nsOSPermissionRequest.mm
Normal file
77
dom/system/mac/nsOSPermissionRequest.mm
Normal file
@@ -0,0 +1,77 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "nsOSPermissionRequest.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCocoaFeatures.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
using mozilla::dom::Promise;
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequest::GetAudioCapturePermissionState(uint16_t* aAudio)
|
||||
{
|
||||
MOZ_ASSERT(aAudio);
|
||||
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return nsOSPermissionRequestBase::GetAudioCapturePermissionState(aAudio);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::GetAudioCapturePermissionState(*aAudio);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequest::GetVideoCapturePermissionState(uint16_t* aVideo)
|
||||
{
|
||||
MOZ_ASSERT(aVideo);
|
||||
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return nsOSPermissionRequestBase::GetVideoCapturePermissionState(aVideo);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::GetVideoCapturePermissionState(*aVideo);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequest::RequestVideoCapturePermission(JSContext* aCx,
|
||||
Promise** aPromiseOut)
|
||||
{
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return nsOSPermissionRequestBase::RequestVideoCapturePermission(aCx, aPromiseOut);
|
||||
}
|
||||
|
||||
RefPtr<Promise> promiseHandle;
|
||||
nsresult rv = GetPromise(aCx, promiseHandle);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = nsCocoaUtils::RequestVideoCapturePermission(promiseHandle);
|
||||
promiseHandle.forget(aPromiseOut);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequest::RequestAudioCapturePermission(JSContext* aCx,
|
||||
Promise** aPromiseOut)
|
||||
{
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return nsOSPermissionRequestBase::RequestAudioCapturePermission(aCx, aPromiseOut);
|
||||
}
|
||||
|
||||
RefPtr<Promise> promiseHandle;
|
||||
nsresult rv = GetPromise(aCx, promiseHandle);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = nsCocoaUtils::RequestAudioCapturePermission(promiseHandle);
|
||||
promiseHandle.forget(aPromiseOut);
|
||||
return rv;
|
||||
}
|
||||
@@ -20,6 +20,9 @@ with Files("windows/*LocationProvider*"):
|
||||
with Files("mac/*LocationProvider*"):
|
||||
BUG_COMPONENT = ("Core", "Geolocation")
|
||||
|
||||
with Files("mac/*OSPermissionRequest*"):
|
||||
BUG_COMPONENT = ("Firefox", "Device Permissions")
|
||||
|
||||
with Files("linux/*LocationProvider*"):
|
||||
BUG_COMPONENT = ("Core", "Geolocation")
|
||||
|
||||
@@ -49,14 +52,21 @@ elif toolkit == 'android':
|
||||
elif toolkit == 'gtk3':
|
||||
DIRS += ['linux']
|
||||
|
||||
if toolkit != 'cocoa':
|
||||
EXPORTS += [
|
||||
'nsOSPermissionRequest.h',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIOSFileConstantsService.idl',
|
||||
'nsIOSPermissionRequest.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_system'
|
||||
|
||||
EXPORTS += [
|
||||
'nsDeviceSensors.h',
|
||||
'nsOSPermissionRequestBase.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
@@ -65,6 +75,7 @@ EXPORTS.mozilla += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsDeviceSensors.cpp',
|
||||
'nsOSPermissionRequestBase.cpp',
|
||||
'OSFileConstants.cpp',
|
||||
]
|
||||
|
||||
|
||||
55
dom/system/nsIOSPermissionRequest.idl
Normal file
55
dom/system/nsIOSPermissionRequest.idl
Normal file
@@ -0,0 +1,55 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=40: */
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(95790842-75a0-430d-98bf-f5ce3788ea6d)]
|
||||
interface nsIOSPermissionRequest: nsISupports
|
||||
{
|
||||
/*
|
||||
* The permission state is not known. As an example, on macOS
|
||||
* this is used to indicate the user has not been prompted to
|
||||
* authorize or deny access and there is no policy in place to
|
||||
* deny access.
|
||||
*/
|
||||
const uint16_t PERMISSION_STATE_NOTDETERMINED = 0;
|
||||
|
||||
/* A policy prevents the application from accessing the resource */
|
||||
const uint16_t PERMISSION_STATE_RESTRICTED = 1;
|
||||
|
||||
/* Access to the resource is denied */
|
||||
const uint16_t PERMISSION_STATE_DENIED = 2;
|
||||
|
||||
/* Access to the resource is allowed */
|
||||
const uint16_t PERMISSION_STATE_AUTHORIZED = 3;
|
||||
|
||||
/* Get the permission state for both audio and video capture */
|
||||
void getMediaCapturePermissionState(out uint16_t aVideo,
|
||||
out uint16_t aAudio);
|
||||
|
||||
/* Get the permission state for audio capture */
|
||||
void getAudioCapturePermissionState(out uint16_t aAudio);
|
||||
|
||||
/* Get the permission state for video capture */
|
||||
void getVideoCapturePermissionState(out uint16_t aVideo);
|
||||
|
||||
/*
|
||||
* Request permission to access video capture devices. Returns a
|
||||
* promise that resolves with |true| after the browser has been
|
||||
* granted permission to capture video. If capture access is denied,
|
||||
* the promise is resolved with |false|. The promise is rejected if
|
||||
* an error occurs.
|
||||
*/
|
||||
[implicit_jscontext, must_use]
|
||||
Promise requestVideoCapturePermission();
|
||||
|
||||
/*
|
||||
* Request permission to access audio capture devices. Returns a
|
||||
* promise with the same semantics as |requestVideoCapturePermission|.
|
||||
*/
|
||||
[implicit_jscontext, must_use]
|
||||
Promise requestAudioCapturePermission();
|
||||
};
|
||||
20
dom/system/nsOSPermissionRequest.h
Normal file
20
dom/system/nsOSPermissionRequest.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsOSPermissionRequest_h__
|
||||
#define nsOSPermissionRequest_h__
|
||||
|
||||
#include "nsOSPermissionRequestBase.h"
|
||||
|
||||
/*
|
||||
* The default implementation of nsOSPermissionRequestBase used on platforms
|
||||
* that don't have a platform-specific version.
|
||||
*/
|
||||
class nsOSPermissionRequest : public nsOSPermissionRequestBase
|
||||
{
|
||||
};
|
||||
|
||||
#endif /* nsOSPermissionRequest_h__ */
|
||||
93
dom/system/nsOSPermissionRequestBase.cpp
Normal file
93
dom/system/nsOSPermissionRequestBase.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "nsOSPermissionRequestBase.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
using mozilla::dom::Promise;
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
nsOSPermissionRequestBase,
|
||||
nsIOSPermissionRequest,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequestBase::GetMediaCapturePermissionState(uint16_t* aCamera,
|
||||
uint16_t* aMicrophone)
|
||||
{
|
||||
nsresult rv = GetVideoCapturePermissionState(aCamera);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return GetAudioCapturePermissionState(aMicrophone);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequestBase::GetAudioCapturePermissionState(uint16_t* aAudio)
|
||||
{
|
||||
MOZ_ASSERT(aAudio);
|
||||
*aAudio = PERMISSION_STATE_AUTHORIZED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequestBase::GetVideoCapturePermissionState(uint16_t* aVideo)
|
||||
{
|
||||
MOZ_ASSERT(aVideo);
|
||||
*aVideo = PERMISSION_STATE_AUTHORIZED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOSPermissionRequestBase::GetPromise(JSContext* aCx,
|
||||
RefPtr<Promise>& aPromiseOut)
|
||||
{
|
||||
nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
ErrorResult result;
|
||||
aPromiseOut = Promise::Create(globalObject, result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequestBase::RequestVideoCapturePermission(JSContext* aCx,
|
||||
Promise** aPromiseOut)
|
||||
{
|
||||
RefPtr<Promise> promiseHandle;
|
||||
nsresult rv = GetPromise(aCx, promiseHandle);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promiseHandle->MaybeResolve(true /* access authorized */);
|
||||
promiseHandle.forget(aPromiseOut);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOSPermissionRequestBase::RequestAudioCapturePermission(JSContext* aCx,
|
||||
Promise** aPromiseOut)
|
||||
{
|
||||
RefPtr<Promise> promiseHandle;
|
||||
nsresult rv = GetPromise(aCx, promiseHandle);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promiseHandle->MaybeResolve(true /* access authorized */);
|
||||
promiseHandle.forget(aPromiseOut);
|
||||
return NS_OK;
|
||||
}
|
||||
48
dom/system/nsOSPermissionRequestBase.h
Normal file
48
dom/system/nsOSPermissionRequestBase.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsOSPermissionRequestBase_h__
|
||||
#define nsOSPermissionRequestBase_h__
|
||||
|
||||
#include "nsIOSPermissionRequest.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#define NS_OSPERMISSIONREQUEST_CID \
|
||||
{ 0x95790842, 0x75a0, 0x430d, \
|
||||
{ 0x98, 0xbf, 0xf5, 0xce, 0x37, 0x88, 0xea, 0x6d } }
|
||||
#define NS_OSPERMISSIONREQUEST_CONTRACTID \
|
||||
"@mozilla.org/ospermissionrequest;1"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Promise;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
using mozilla::dom::Promise;
|
||||
|
||||
/*
|
||||
* The base implementation of nsIOSPermissionRequest to be subclassed on
|
||||
* platforms that require permission requests for access to resources such
|
||||
* as media captures devices. This implementation always returns results
|
||||
* indicating access is permitted.
|
||||
*/
|
||||
class nsOSPermissionRequestBase
|
||||
: public nsIOSPermissionRequest,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOSPERMISSIONREQUEST
|
||||
|
||||
nsOSPermissionRequestBase() {};
|
||||
|
||||
protected:
|
||||
nsresult GetPromise(JSContext* aCx, RefPtr<Promise>& aPromiseOut);
|
||||
virtual ~nsOSPermissionRequestBase() = default;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
// Declare the backingScaleFactor method that we want to call
|
||||
// on NSView/Window/Screen objects, if they recognize it.
|
||||
@@ -38,8 +39,14 @@ class TimeStamp;
|
||||
namespace gfx {
|
||||
class SourceSurface;
|
||||
} // namespace gfx
|
||||
namespace dom {
|
||||
class Promise;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
using mozilla::StaticAutoPtr;
|
||||
using mozilla::StaticMutex;
|
||||
|
||||
// Used to retain a Cocoa object for the remainder of a method's execution.
|
||||
class nsAutoRetainCocoaObject {
|
||||
public:
|
||||
@@ -102,11 +109,18 @@ struct KeyBindingsCommand
|
||||
|
||||
@end // NativeKeyBindingsRecorder
|
||||
|
||||
#if !defined(MAC_OS_X_VERSION_10_14) || \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_14
|
||||
typedef NSString* AVMediaType;
|
||||
#endif
|
||||
|
||||
class nsCocoaUtils
|
||||
{
|
||||
typedef mozilla::gfx::SourceSurface SourceSurface;
|
||||
typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
|
||||
typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
|
||||
typedef mozilla::dom::Promise Promise;
|
||||
typedef StaticAutoPtr<nsTArray<RefPtr<Promise>>> PromiseArray;
|
||||
|
||||
public:
|
||||
|
||||
@@ -396,6 +410,84 @@ public:
|
||||
* If aEventTime is 0, this returns current timestamp.
|
||||
*/
|
||||
static mozilla::TimeStamp GetEventTimeStamp(NSTimeInterval aEventTime);
|
||||
|
||||
/**
|
||||
* Get the current video capture permission status.
|
||||
* Returns NS_ERROR_NOT_IMPLEMENTED on 10.13 and earlier macOS versions.
|
||||
*/
|
||||
static nsresult GetVideoCapturePermissionState(uint16_t& aPermissionState);
|
||||
|
||||
/**
|
||||
* Get the current audio capture permission status.
|
||||
* Returns NS_ERROR_NOT_IMPLEMENTED on 10.13 and earlier macOS versions.
|
||||
*/
|
||||
static nsresult GetAudioCapturePermissionState(uint16_t& aPermissionState);
|
||||
|
||||
/**
|
||||
* Request video capture permission from the OS. Caller must be running
|
||||
* on the main thread and the promise will be resolved on the main thread.
|
||||
* Returns NS_ERROR_NOT_IMPLEMENTED on 10.13 and earlier macOS versions.
|
||||
*/
|
||||
static nsresult RequestVideoCapturePermission(RefPtr<Promise>& aPromise);
|
||||
|
||||
/**
|
||||
* Request audio capture permission from the OS. Caller must be running
|
||||
* on the main thread and the promise will be resolved on the main thread.
|
||||
* Returns NS_ERROR_NOT_IMPLEMENTED on 10.13 and earlier macOS versions.
|
||||
*/
|
||||
static nsresult RequestAudioCapturePermission(RefPtr<Promise>& aPromise);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Completion handlers used as an argument to the macOS API to
|
||||
* request media capture permission. These are called asynchronously
|
||||
* on an arbitrary dispatch queue.
|
||||
*/
|
||||
static void (^AudioCompletionHandler)(BOOL);
|
||||
static void (^VideoCompletionHandler)(BOOL);
|
||||
|
||||
/**
|
||||
* Called from the audio and video completion handlers in order to
|
||||
* dispatch the handling back to the main thread.
|
||||
*/
|
||||
static void ResolveAudioCapturePromises(bool aGranted);
|
||||
static void ResolveVideoCapturePromises(bool aGranted);
|
||||
|
||||
/**
|
||||
* Main implementation for Request{Audio,Video}CapturePermission.
|
||||
* @param aType the AVMediaType to request capture permission for
|
||||
* @param aPromise the Promise to resolve when capture permission
|
||||
* is either allowed or denied
|
||||
* @param aPromiseList the array of promises to save |aPromise| in
|
||||
* @param aHandler the block function (either ResolveAudioCapturePromises
|
||||
* or ResolveVideoCapturePromises) to be used as
|
||||
* the requestAccessForMediaType callback.
|
||||
*/
|
||||
static nsresult RequestCapturePermission(NSString* aType,
|
||||
RefPtr<Promise>& aPromise,
|
||||
PromiseArray& aPromiseList,
|
||||
void (^aHandler)(BOOL granted));
|
||||
/**
|
||||
* Resolves the pending promises that are waiting for a response
|
||||
* to a request video or audio capture permission.
|
||||
*/
|
||||
static void ResolveMediaCapturePromises(bool aGranted,
|
||||
PromiseArray& aPromiseList);
|
||||
|
||||
/**
|
||||
* Array of promises waiting to be resolved due to a video capture request.
|
||||
*/
|
||||
static PromiseArray sVideoCapturePromises;
|
||||
|
||||
/**
|
||||
* Array of promises waiting to be resolved due to an audio capture request.
|
||||
*/
|
||||
static PromiseArray sAudioCapturePromises;
|
||||
|
||||
/**
|
||||
* Lock protecting |sVideoCapturePromises| and |sAudioCapturePromises|.
|
||||
*/
|
||||
static StaticMutex sMediaCaptureMutex;
|
||||
};
|
||||
|
||||
#endif // nsCocoaUtils_h_
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* 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/. */
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "gfx2DGlue.h"
|
||||
@@ -16,6 +18,8 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIAppShellService.h"
|
||||
#include "nsIOSPermissionRequest.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIXULWindow.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsIServiceManager.h"
|
||||
@@ -23,14 +27,19 @@
|
||||
#include "nsToolkit.h"
|
||||
#include "nsCRT.h"
|
||||
#include "SVGImageContext.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/MiscEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
using mozilla::dom::Promise;
|
||||
using mozilla::gfx::BackendType;
|
||||
using mozilla::gfx::DataSourceSurface;
|
||||
using mozilla::gfx::DrawTarget;
|
||||
@@ -44,6 +53,21 @@ using mozilla::gfx::SourceSurface;
|
||||
using mozilla::image::ImageRegion;
|
||||
using std::ceil;
|
||||
|
||||
LazyLogModule gCocoaUtilsLog("nsCocoaUtils");
|
||||
#undef LOG
|
||||
#define LOG(...) MOZ_LOG(gCocoaUtilsLog, LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
/*
|
||||
* For each audio and video capture request, we hold an owning reference
|
||||
* to a promise to be resolved when the request's async callback is invoked.
|
||||
* sVideoCapturePromises and sAudioCapturePromises are arrays of video and
|
||||
* audio promises waiting for to be resolved. Each array is protected by a
|
||||
* mutex.
|
||||
*/
|
||||
nsCocoaUtils::PromiseArray nsCocoaUtils::sVideoCapturePromises;
|
||||
nsCocoaUtils::PromiseArray nsCocoaUtils::sAudioCapturePromises;
|
||||
StaticMutex nsCocoaUtils::sMediaCaptureMutex;
|
||||
|
||||
static float
|
||||
MenuBarScreenHeight()
|
||||
{
|
||||
@@ -1123,3 +1147,259 @@ nsCocoaUtils::GetEventTimeStamp(NSTimeInterval aEventTime)
|
||||
BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime * 1000.0);
|
||||
return TimeStamp::FromSystemTime(tick);
|
||||
}
|
||||
|
||||
// AVAuthorizationStatus is not needed unless we are running on 10.14.
|
||||
// However, on pre-10.14 SDK's, AVAuthorizationStatus and its enum values
|
||||
// are both defined and prohibited from use by compile-time checks. We
|
||||
// define a copy of AVAuthorizationStatus to allow compilation on pre-10.14
|
||||
// SDK's. The enum values must match what is defined in the 10.14 SDK.
|
||||
// We use ASSERTS for 10.14 SDK builds to check the enum values match.
|
||||
enum GeckoAVAuthorizationStatus {
|
||||
GeckoAVAuthorizationStatusNotDetermined = 0,
|
||||
GeckoAVAuthorizationStatusRestricted = 1,
|
||||
GeckoAVAuthorizationStatusDenied = 2,
|
||||
GeckoAVAuthorizationStatusAuthorized = 3
|
||||
};
|
||||
|
||||
#if !defined(MAC_OS_X_VERSION_10_14) || \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_14
|
||||
// Define authorizationStatusForMediaType: as returning
|
||||
// GeckoAVAuthorizationStatus instead of AVAuthorizationStatus to allow
|
||||
// compilation on pre-10.14 SDK's.
|
||||
@interface AVCaptureDevice(GeckoAVAuthorizationStatus)
|
||||
+ (GeckoAVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType;
|
||||
@end
|
||||
|
||||
@interface AVCaptureDevice(WithCompletionHandler)
|
||||
+ (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL granted))handler;
|
||||
@end
|
||||
#endif
|
||||
|
||||
static const char*
|
||||
AVMediaTypeToString(AVMediaType aType)
|
||||
{
|
||||
if (aType == AVMediaTypeVideo) {
|
||||
return "video";
|
||||
}
|
||||
|
||||
if (aType == AVMediaTypeAudio) {
|
||||
return "audio";
|
||||
}
|
||||
|
||||
return "unexpected type";
|
||||
}
|
||||
|
||||
static void
|
||||
LogAuthorizationStatus(AVMediaType aType, int aState)
|
||||
{
|
||||
const char* stateString;
|
||||
|
||||
switch (aState) {
|
||||
case GeckoAVAuthorizationStatusAuthorized:
|
||||
stateString = "AVAuthorizationStatusAuthorized";
|
||||
break;
|
||||
case GeckoAVAuthorizationStatusDenied:
|
||||
stateString = "AVAuthorizationStatusDenied";
|
||||
break;
|
||||
case GeckoAVAuthorizationStatusNotDetermined:
|
||||
stateString = "AVAuthorizationStatusNotDetermined";
|
||||
break;
|
||||
case GeckoAVAuthorizationStatusRestricted:
|
||||
stateString = "AVAuthorizationStatusRestricted";
|
||||
break;
|
||||
default:
|
||||
stateString = "Invalid state";
|
||||
}
|
||||
|
||||
LOG("%s authorization status: %s\n", AVMediaTypeToString(aType), stateString);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetPermissionState(AVMediaType aMediaType, uint16_t& aState)
|
||||
{
|
||||
MOZ_ASSERT(aMediaType == AVMediaTypeVideo || aMediaType == AVMediaTypeAudio);
|
||||
|
||||
// Only attempt to check authorization status on 10.14+.
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
GeckoAVAuthorizationStatus authStatus =
|
||||
[AVCaptureDevice authorizationStatusForMediaType:aMediaType];
|
||||
LogAuthorizationStatus(aMediaType, authStatus);
|
||||
|
||||
// Convert GeckoAVAuthorizationStatus to nsIOSPermissionRequest const
|
||||
switch (authStatus) {
|
||||
case GeckoAVAuthorizationStatusAuthorized:
|
||||
aState = nsIOSPermissionRequest::PERMISSION_STATE_AUTHORIZED;
|
||||
return NS_OK;
|
||||
case GeckoAVAuthorizationStatusDenied:
|
||||
aState = nsIOSPermissionRequest::PERMISSION_STATE_DENIED;
|
||||
return NS_OK;
|
||||
case GeckoAVAuthorizationStatusNotDetermined:
|
||||
aState = nsIOSPermissionRequest::PERMISSION_STATE_NOTDETERMINED;
|
||||
return NS_OK;
|
||||
case GeckoAVAuthorizationStatusRestricted:
|
||||
aState = nsIOSPermissionRequest::PERMISSION_STATE_RESTRICTED;
|
||||
return NS_OK;
|
||||
default:
|
||||
MOZ_ASSERT(false, "Invalid authorization status");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCocoaUtils::GetVideoCapturePermissionState(uint16_t& aPermissionState)
|
||||
{
|
||||
return GetPermissionState(AVMediaTypeVideo, aPermissionState);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCocoaUtils::GetAudioCapturePermissionState(uint16_t& aPermissionState)
|
||||
{
|
||||
return GetPermissionState(AVMediaTypeAudio, aPermissionState);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCocoaUtils::RequestVideoCapturePermission(RefPtr<Promise>& aPromise)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return nsCocoaUtils::RequestCapturePermission(AVMediaTypeVideo,
|
||||
aPromise,
|
||||
sVideoCapturePromises,
|
||||
VideoCompletionHandler);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCocoaUtils::RequestAudioCapturePermission(RefPtr<Promise>& aPromise)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return nsCocoaUtils::RequestCapturePermission(AVMediaTypeAudio,
|
||||
aPromise,
|
||||
sAudioCapturePromises,
|
||||
AudioCompletionHandler);
|
||||
}
|
||||
|
||||
//
|
||||
// Stores |aPromise| on |aPromiseList| and starts an asynchronous media
|
||||
// capture request for the given media type |aType|. If we are already
|
||||
// waiting for a capture request for this media type, don't start a new
|
||||
// request. |aHandler| is invoked on an arbitrary dispatch queue when the
|
||||
// request completes and must resolve any waiting Promises on the main
|
||||
// thread.
|
||||
//
|
||||
nsresult
|
||||
nsCocoaUtils::RequestCapturePermission(AVMediaType aType,
|
||||
RefPtr<Promise>& aPromise,
|
||||
PromiseArray& aPromiseList,
|
||||
void (^aHandler)(BOOL granted))
|
||||
{
|
||||
MOZ_ASSERT(aType == AVMediaTypeVideo || aType == AVMediaTypeAudio);
|
||||
#if defined(MAC_OS_X_VERSION_10_14)
|
||||
// Ensure our enum constants match. We can only do this when
|
||||
// compiling on 10.14+ because AVAuthorizationStatus is
|
||||
// prohibited by preprocessor checks on earlier OS versions.
|
||||
MOZ_ASSERT((int)GeckoAVAuthorizationStatusNotDetermined ==
|
||||
(int)AVAuthorizationStatusNotDetermined);
|
||||
MOZ_ASSERT((int)GeckoAVAuthorizationStatusRestricted ==
|
||||
(int)AVAuthorizationStatusRestricted);
|
||||
MOZ_ASSERT((int)GeckoAVAuthorizationStatusDenied ==
|
||||
(int)AVAuthorizationStatusDenied);
|
||||
MOZ_ASSERT((int)GeckoAVAuthorizationStatusAuthorized ==
|
||||
(int)AVAuthorizationStatusAuthorized);
|
||||
#endif
|
||||
LOG("RequestCapturePermission(%s)", AVMediaTypeToString(aType));
|
||||
|
||||
// Only attempt to request authorization on 10.14+.
|
||||
if (!nsCocoaFeatures::OnMojaveOrLater()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
sMediaCaptureMutex.Lock();
|
||||
|
||||
// Initialize our list of promises on first invocation
|
||||
if (aPromiseList == nullptr) {
|
||||
aPromiseList = new nsTArray<RefPtr<Promise>>;
|
||||
ClearOnShutdown(&aPromiseList);
|
||||
}
|
||||
|
||||
aPromiseList->AppendElement(aPromise);
|
||||
size_t nPromises = aPromiseList->Length();
|
||||
|
||||
sMediaCaptureMutex.Unlock();
|
||||
|
||||
LOG("RequestCapturePermission(%s): %ld promise(s) unresolved",
|
||||
AVMediaTypeToString(aType), nPromises);
|
||||
|
||||
// If we had one or more more existing promises waiting to be resolved
|
||||
// by the completion handler, we don't need to start another request.
|
||||
if (nPromises > 1) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Start the request
|
||||
[AVCaptureDevice requestAccessForMediaType:aType completionHandler:aHandler];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Audio capture request completion handler. Called from an arbitrary
|
||||
// dispatch queue.
|
||||
//
|
||||
void (^nsCocoaUtils::AudioCompletionHandler)(BOOL) = ^void (BOOL granted)
|
||||
{
|
||||
nsCocoaUtils::ResolveAudioCapturePromises(granted);
|
||||
};
|
||||
|
||||
//
|
||||
// Video capture request completion handler. Called from an arbitrary
|
||||
// dispatch queue.
|
||||
//
|
||||
void (^nsCocoaUtils::VideoCompletionHandler)(BOOL) = ^void (BOOL granted)
|
||||
{
|
||||
nsCocoaUtils::ResolveVideoCapturePromises(granted);
|
||||
};
|
||||
|
||||
void
|
||||
nsCocoaUtils::ResolveMediaCapturePromises(bool aGranted,
|
||||
PromiseArray& aPromiseList)
|
||||
{
|
||||
StaticMutexAutoLock lock(sMediaCaptureMutex);
|
||||
|
||||
// Remove each promise from the list and resolve it.
|
||||
while (aPromiseList->Length() > 0) {
|
||||
RefPtr<Promise> promise = aPromiseList->LastElement();
|
||||
aPromiseList->RemoveLastElement();
|
||||
|
||||
// Resolve on main thread
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
"ResolveMediaAccessPromise",
|
||||
[aGranted, aPromise = std::move(promise)]() {
|
||||
aPromise->MaybeResolve(aGranted);
|
||||
}));
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
nsCocoaUtils::ResolveAudioCapturePromises(bool aGranted)
|
||||
{
|
||||
// Resolve on main thread
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
"ResolveAudioCapturePromise", [aGranted]() {
|
||||
ResolveMediaCapturePromises(aGranted, sAudioCapturePromises);
|
||||
}));
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
}
|
||||
|
||||
void
|
||||
nsCocoaUtils::ResolveVideoCapturePromises(bool aGranted)
|
||||
{
|
||||
// Resolve on main thread
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
"ResolveVideoCapturePromise", [aGranted]() {
|
||||
ResolveMediaCapturePromises(aGranted, sVideoCapturePromises);
|
||||
}));
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user