summaryrefslogtreecommitdiffstats
path: root/dom/system/mac
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/system/mac/CoreLocationLocationProvider.h61
-rw-r--r--dom/system/mac/CoreLocationLocationProvider.mm246
-rw-r--r--dom/system/mac/moz.build21
-rw-r--r--dom/system/mac/nsOSPermissionRequest.h31
-rw-r--r--dom/system/mac/nsOSPermissionRequest.mm91
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();
+}