diff options
Diffstat (limited to '')
-rw-r--r-- | dom/system/mac/CoreLocationLocationProvider.h | 61 | ||||
-rw-r--r-- | dom/system/mac/CoreLocationLocationProvider.mm | 246 | ||||
-rw-r--r-- | dom/system/mac/moz.build | 21 | ||||
-rw-r--r-- | dom/system/mac/nsOSPermissionRequest.h | 31 | ||||
-rw-r--r-- | dom/system/mac/nsOSPermissionRequest.mm | 91 |
5 files changed, 450 insertions, 0 deletions
diff --git a/dom/system/mac/CoreLocationLocationProvider.h b/dom/system/mac/CoreLocationLocationProvider.h new file mode 100644 index 0000000000..27b990cf9a --- /dev/null +++ b/dom/system/mac/CoreLocationLocationProvider.h @@ -0,0 +1,61 @@ +/* -*- 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/. */ + +#include "nsCOMPtr.h" +#include "nsIGeolocationProvider.h" +#include "mozilla/Attributes.h" + +/* + * The CoreLocationObjects class contains the CoreLocation objects + * we'll need. + * + * Declaring them directly in CoreLocationLocationProvider + * would require Objective-C++ syntax, which would contaminate all + * files that include this header and require them to be Objective-C++ + * as well. + * + * The solution then is to forward-declare CoreLocationObjects here and + * hold a pointer to it in CoreLocationLocationProvider, and only actually + * define it in CoreLocationLocationProvider.mm, thus making it safe + * for Geolocation.cpp, which is C++-only, to include this header. + */ +class CoreLocationObjects; +class MLSFallback; + +class CoreLocationLocationProvider : public nsIGeolocationProvider { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONPROVIDER + + CoreLocationLocationProvider(); + // MOZ_CAN_RUN_SCRIPT_BOUNDARY because we can't mark Objective-C methods as + // MOZ_CAN_RUN_SCRIPT as far as I can tell, and this method is called from + // Objective-C. + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void NotifyError(uint16_t aErrorCode); + void Update(nsIDOMGeoPosition* aSomewhere); + void CreateMLSFallbackProvider(); + void CancelMLSFallbackProvider(); + + private: + virtual ~CoreLocationLocationProvider() = default; + + CoreLocationObjects* mCLObjects; + nsCOMPtr<nsIGeolocationUpdate> mCallback; + RefPtr<MLSFallback> mMLSFallbackProvider; + + class MLSUpdate : public nsIGeolocationUpdate { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONUPDATE + + explicit MLSUpdate(CoreLocationLocationProvider& parentProvider); + + private: + CoreLocationLocationProvider& mParentLocationProvider; + virtual ~MLSUpdate() = default; + }; +}; diff --git a/dom/system/mac/CoreLocationLocationProvider.mm b/dom/system/mac/CoreLocationLocationProvider.mm new file mode 100644 index 0000000000..781eac9d46 --- /dev/null +++ b/dom/system/mac/CoreLocationLocationProvider.mm @@ -0,0 +1,246 @@ +/* -*- 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 "nsCOMPtr.h" +#include "GeolocationPosition.h" +#include "nsIConsoleService.h" +#include "nsServiceManagerUtils.h" +#include "CoreLocationLocationProvider.h" +#include "nsCocoaFeatures.h" +#include "prtime.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/Telemetry.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/GeolocationPositionErrorBinding.h" +#include "MLSFallback.h" + +#include <CoreLocation/CLError.h> +#include <CoreLocation/CLLocation.h> +#include <CoreLocation/CLLocationManager.h> +#include <CoreLocation/CLLocationManagerDelegate.h> + +#include <objc/objc.h> +#include <objc/objc-runtime.h> + +#include "nsObjCExceptions.h" + +using namespace mozilla; + +static const CLLocationAccuracy kHIGH_ACCURACY = kCLLocationAccuracyBest; +static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTenMeters; + +@interface LocationDelegate : NSObject <CLLocationManagerDelegate> { + CoreLocationLocationProvider* mProvider; +} + +- (id)init:(CoreLocationLocationProvider*)aProvider; +- (void)locationManager:(CLLocationManager*)aManager didFailWithError:(NSError*)aError; +- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)locations; + +@end + +@implementation LocationDelegate +- (id)init:(CoreLocationLocationProvider*)aProvider { + if ((self = [super init])) { + mProvider = aProvider; + } + + return self; +} + +- (void)locationManager:(CLLocationManager*)aManager didFailWithError:(NSError*)aError { + nsCOMPtr<nsIConsoleService> console = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + + NS_ENSURE_TRUE_VOID(console); + + NSString* message = + [@"Failed to acquire position: " stringByAppendingString:[aError localizedDescription]]; + + console->LogStringMessage(NS_ConvertUTF8toUTF16([message UTF8String]).get()); + + if ([aError code] == kCLErrorDenied) { + mProvider->NotifyError(dom::GeolocationPositionError_Binding::PERMISSION_DENIED); + return; + } + + // The CL provider does not fallback to GeoIP, so use NetworkGeolocationProvider for this. + // The concept here is: on error, hand off geolocation to MLS, which will then report + // back a location or error. + mProvider->CreateMLSFallbackProvider(); +} + +- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)aLocations { + if (aLocations.count < 1) { + return; + } + + mProvider->CancelMLSFallbackProvider(); + + CLLocation* location = [aLocations objectAtIndex:0]; + + double altitude; + double altitudeAccuracy; + + // A negative verticalAccuracy indicates that the altitude value is invalid. + if (location.verticalAccuracy >= 0) { + altitude = location.altitude; + altitudeAccuracy = location.verticalAccuracy; + } else { + altitude = UnspecifiedNaN<double>(); + altitudeAccuracy = UnspecifiedNaN<double>(); + } + + double speed = location.speed >= 0 ? location.speed : UnspecifiedNaN<double>(); + + double heading = location.course >= 0 ? location.course : UnspecifiedNaN<double>(); + + // nsGeoPositionCoords will convert NaNs to null for optional properties of + // the JavaScript Coordinates object. + nsCOMPtr<nsIDOMGeoPosition> geoPosition = new nsGeoPosition( + location.coordinate.latitude, location.coordinate.longitude, altitude, + location.horizontalAccuracy, altitudeAccuracy, heading, speed, PR_Now() / PR_USEC_PER_MSEC); + + mProvider->Update(geoPosition); + Telemetry::Accumulate(Telemetry::GEOLOCATION_OSX_SOURCE_IS_MLS, false); +} +@end + +NS_IMPL_ISUPPORTS(CoreLocationLocationProvider::MLSUpdate, nsIGeolocationUpdate); + +CoreLocationLocationProvider::MLSUpdate::MLSUpdate(CoreLocationLocationProvider& parentProvider) + : mParentLocationProvider(parentProvider) {} + +NS_IMETHODIMP +CoreLocationLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition* position) { + nsCOMPtr<nsIDOMGeoPositionCoords> coords; + position->GetCoords(getter_AddRefs(coords)); + if (!coords) { + return NS_ERROR_FAILURE; + } + mParentLocationProvider.Update(position); + Telemetry::Accumulate(Telemetry::GEOLOCATION_OSX_SOURCE_IS_MLS, true); + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error) { + mParentLocationProvider.NotifyError(error); + return NS_OK; +} + +class CoreLocationObjects { + public: + nsresult Init(CoreLocationLocationProvider* aProvider) { + mLocationManager = [[CLLocationManager alloc] init]; + NS_ENSURE_TRUE(mLocationManager, NS_ERROR_NOT_AVAILABLE); + + mLocationDelegate = [[LocationDelegate alloc] init:aProvider]; + NS_ENSURE_TRUE(mLocationDelegate, NS_ERROR_NOT_AVAILABLE); + + mLocationManager.desiredAccuracy = kDEFAULT_ACCURACY; + mLocationManager.delegate = mLocationDelegate; + + return NS_OK; + } + + ~CoreLocationObjects() { + if (mLocationManager) { + [mLocationManager release]; + } + + if (mLocationDelegate) { + [mLocationDelegate release]; + } + } + + LocationDelegate* mLocationDelegate; + CLLocationManager* mLocationManager; +}; + +NS_IMPL_ISUPPORTS(CoreLocationLocationProvider, nsIGeolocationProvider) + +CoreLocationLocationProvider::CoreLocationLocationProvider() + : mCLObjects(nullptr), mMLSFallbackProvider(nullptr) {} + +NS_IMETHODIMP +CoreLocationLocationProvider::Startup() { + if (!mCLObjects) { + auto clObjs = MakeUnique<CoreLocationObjects>(); + + nsresult rv = clObjs->Init(this); + NS_ENSURE_SUCCESS(rv, rv); + + mCLObjects = clObjs.release(); + } + + // Must be stopped before starting or response (success or failure) is not guaranteed + [mCLObjects->mLocationManager stopUpdatingLocation]; + [mCLObjects->mLocationManager startUpdatingLocation]; + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::Watch(nsIGeolocationUpdate* aCallback) { + if (mCallback) { + return NS_OK; + } + + mCallback = aCallback; + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::Shutdown() { + NS_ENSURE_STATE(mCLObjects); + + [mCLObjects->mLocationManager stopUpdatingLocation]; + + delete mCLObjects; + mCLObjects = nullptr; + + if (mMLSFallbackProvider) { + mMLSFallbackProvider->Shutdown(); + mMLSFallbackProvider = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::SetHighAccuracy(bool aEnable) { + NS_ENSURE_STATE(mCLObjects); + + mCLObjects->mLocationManager.desiredAccuracy = (aEnable ? kHIGH_ACCURACY : kDEFAULT_ACCURACY); + + return NS_OK; +} + +void CoreLocationLocationProvider::Update(nsIDOMGeoPosition* aSomewhere) { + if (aSomewhere && mCallback) { + mCallback->Update(aSomewhere); + } +} +void CoreLocationLocationProvider::NotifyError(uint16_t aErrorCode) { + nsCOMPtr<nsIGeolocationUpdate> callback(mCallback); + callback->NotifyError(aErrorCode); +} +void CoreLocationLocationProvider::CreateMLSFallbackProvider() { + if (mMLSFallbackProvider) { + return; + } + + mMLSFallbackProvider = new MLSFallback(0); + mMLSFallbackProvider->Startup(new MLSUpdate(*this)); +} + +void CoreLocationLocationProvider::CancelMLSFallbackProvider() { + if (!mMLSFallbackProvider) { + return; + } + + mMLSFallbackProvider->Shutdown(); + mMLSFallbackProvider = nullptr; +} diff --git a/dom/system/mac/moz.build b/dom/system/mac/moz.build new file mode 100644 index 0000000000..6a10090793 --- /dev/null +++ b/dom/system/mac/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SOURCES += [ + "CoreLocationLocationProvider.mm", + "nsOSPermissionRequest.mm", +] + +EXPORTS += [ + "nsOSPermissionRequest.h", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" +LOCAL_INCLUDES += [ + "/dom/geolocation", +] diff --git a/dom/system/mac/nsOSPermissionRequest.h b/dom/system/mac/nsOSPermissionRequest.h new file mode 100644 index 0000000000..62e4360fee --- /dev/null +++ b/dom/system/mac/nsOSPermissionRequest.h @@ -0,0 +1,31 @@ +/* -*- 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 GetScreenCapturePermissionState(uint16_t* aScreen) override; + + NS_IMETHOD RequestVideoCapturePermission( + JSContext* aCx, mozilla::dom::Promise** aPromiseOut) override; + + NS_IMETHOD RequestAudioCapturePermission( + JSContext* aCx, mozilla::dom::Promise** aPromiseOut) override; + + NS_IMETHOD MaybeRequestScreenCapturePermission() override; +}; + +#endif diff --git a/dom/system/mac/nsOSPermissionRequest.mm b/dom/system/mac/nsOSPermissionRequest.mm new file mode 100644 index 0000000000..ccb4516fdc --- /dev/null +++ b/dom/system/mac/nsOSPermissionRequest.mm @@ -0,0 +1,91 @@ +/* -*- 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::GetScreenCapturePermissionState(uint16_t* aScreen) { + MOZ_ASSERT(aScreen); + + if (!nsCocoaFeatures::OnCatalinaOrLater()) { + return nsOSPermissionRequestBase::GetScreenCapturePermissionState(aScreen); + } + + return nsCocoaUtils::GetScreenCapturePermissionState(*aScreen); +} + +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; +} + +NS_IMETHODIMP +nsOSPermissionRequest::MaybeRequestScreenCapturePermission() { + if (!nsCocoaFeatures::OnCatalinaOrLater()) { + return nsOSPermissionRequestBase::MaybeRequestScreenCapturePermission(); + } + + return nsCocoaUtils::MaybeRequestScreenCapturePermission(); +} |