GeolocationServiceCoreLocation.mm [plain text]
/*
* Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
*
*/
#import "config.h"
#import "GeolocationServiceCoreLocation.h"
#import "Geoposition.h"
#import "PositionError.h"
#import "PositionOptions.h"
#import "SoftLinking.h"
#import "WebCoreThread.h"
#import <CoreLocation/CoreLocation.h>
#import <CoreLocation/CoreLocationPriv.h>
#import <objc/objc-runtime.h>
#import <wtf/RefPtr.h>
#import <wtf/UnusedParam.h>
SOFT_LINK_FRAMEWORK(CoreLocation)
SOFT_LINK_CLASS(CoreLocation, CLLocationManager)
SOFT_LINK_CLASS(CoreLocation, CLLocation)
SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyBest, double)
SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyHundredMeters, double)
#define kCLLocationAccuracyBest getkCLLocationAccuracyBest()
#define kCLLocationAccuracyHundredMeters getkCLLocationAccuracyHundredMeters()
using namespace WebCore;
@interface GeoLocationManager : NSObject<CLLocationManagerDelegate>
{
CLLocationAccuracy m_accuracy;
GeolocationServiceCoreLocation* m_callback;
CLLocationManager *m_locationMgr;
}
- (id)initWithAccuracy:(CLLocationAccuracy)accuracy withCallback:(GeolocationServiceCoreLocation *)callback;
- (void)createOnMainThread;
- (void)start;
- (void)stop;
- (void)suspend;
- (void)resume;
- (void)sendLocation:(CLLocation *)newLocation;
- (void)sendError:(NSNumber *)code withString:(NSString *)string;
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation;
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;
@end
namespace WebCore {
GeolocationService* GeolocationService::create(GeolocationServiceClient* client)
{
return new GeolocationServiceCoreLocation(client);
}
GeolocationServiceCoreLocation::GeolocationServiceCoreLocation(GeolocationServiceClient* client)
: GeolocationService(client)
, m_shouldClearCache(false)
{
}
GeolocationServiceCoreLocation::~GeolocationServiceCoreLocation()
{
[m_locationManager.get() stop];
}
bool GeolocationServiceCoreLocation::startUpdating(PositionOptions* options)
{
// CLLocationAccuracy values suggested by Ron Huang.
CLLocationAccuracy accuracy = kCLLocationAccuracyBest;
if (options && !options->enableHighAccuracy())
accuracy = kCLLocationAccuracyHundredMeters;
if (!m_locationManager.get())
m_locationManager.adoptNS([[GeoLocationManager alloc] initWithAccuracy:accuracy withCallback:this]);
else
[m_locationManager.get() start];
return true;
}
void GeolocationServiceCoreLocation::stopUpdating()
{
[m_locationManager.get() stop];
}
void GeolocationServiceCoreLocation::suspend()
{
[m_locationManager.get() suspend];
}
void GeolocationServiceCoreLocation::resume()
{
[m_locationManager.get() resume];
}
void GeolocationServiceCoreLocation::positionChanged(PassRefPtr<Geoposition> position)
{
m_lastPosition = position;
GeolocationService::positionChanged();
}
void GeolocationServiceCoreLocation::errorOccurred(PassRefPtr<PositionError> error)
{
m_lastError = error;
GeolocationService::errorOccurred();
}
} // namespace WebCore
@implementation GeoLocationManager
- (id)initWithAccuracy:(CLLocationAccuracy)accuracy withCallback:(GeolocationServiceCoreLocation *)callback
{
self = [super init];
if (self) {
m_accuracy = accuracy;
m_callback = callback;
[self performSelectorOnMainThread:@selector(createOnMainThread) withObject:nil waitUntilDone:NO];
}
return self;
}
- (void)dealloc
{
m_locationMgr.delegate = nil;
[m_locationMgr release];
[super dealloc];
}
- (void)createOnMainThread
{
ASSERT(!WebThreadIsCurrent());
#define CLLocationManager getCLLocationManagerClass()
m_locationMgr = [[CLLocationManager alloc] init];
#undef CLLocationManager
m_locationMgr.desiredAccuracy = m_accuracy;
m_locationMgr.delegate = self;
[self start];
}
- (void)start
{
if (WebThreadIsCurrent()) {
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
return;
}
ASSERT(m_locationMgr);
if (m_locationMgr.locationServicesEnabled)
[m_locationMgr startUpdatingLocation];
else
[self sendError:[NSNumber numberWithInt:PositionError::PERMISSION_DENIED] withString:@"Unable to Start"];
}
- (void)stop
{
if (WebThreadIsCurrent()) {
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
return;
}
[m_locationMgr stopUpdatingLocation];
}
- (void)suspend
{
[self stop];
}
- (void)resume
{
if (WebThreadIsCurrent()) {
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
return;
}
[m_locationMgr startUpdatingLocation];
}
- (void)sendLocation:(CLLocation *)newLocation
{
if (!WebThreadIsCurrent()) {
NSInvocation *invocation = WebThreadCreateNSInvocation(self, _cmd);
[invocation setArgument:&newLocation atIndex:2];
WebThreadCallAPI(invocation);
return;
}
// Normalize
bool canProvideAltitude = true;
bool canProvideAltitudeAccuracy = true;
double altitude = newLocation.altitude;
double altitudeAccuracy = newLocation.verticalAccuracy;
if (altitudeAccuracy < 0.0) {
canProvideAltitude = false;
canProvideAltitudeAccuracy = false;
}
bool canProvideSpeed = true;
double speed = newLocation.speed;
if (speed < 0.0)
canProvideSpeed = false;
bool canProvideHeading = true;
double heading = newLocation.course;
if (heading < 0.0)
canProvideHeading = false;
WTF::RefPtr<WebCore::Coordinates> newCoordinates = WebCore::Coordinates::create(
newLocation.coordinate.latitude,
newLocation.coordinate.longitude,
canProvideAltitude,
altitude,
newLocation.horizontalAccuracy,
canProvideAltitudeAccuracy,
altitudeAccuracy,
canProvideHeading,
heading,
canProvideSpeed,
speed);
WTF::RefPtr<WebCore::Geoposition> newPosition = WebCore::Geoposition::create(
newCoordinates.release(),
[newLocation.timestamp timeIntervalSince1970] * 1000.0); // seconds -> milliseconds
// FIXME: Need to figure out best way to clear web cache (<rdar://problem/6845619>)
if (m_callback->shouldClearCache()) {
m_callback->setShouldClearCache(YES);
m_callback->cachePolicyChanged();
}
m_callback->positionChanged(newPosition.release());
}
- (void)sendError:(NSNumber *)code withString:(NSString *)string;
{
if (!WebThreadIsCurrent()) {
NSInvocation *invocation = WebThreadCreateNSInvocation(self, _cmd);
[invocation setArgument:&code atIndex:2];
[invocation setArgument:&string atIndex:3];
WebThreadCallAPI(invocation);
return;
}
m_callback->errorOccurred(PositionError::create(static_cast<PositionError::ErrorCode>([code intValue]), string));
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
ASSERT(m_callback);
ASSERT(newLocation);
UNUSED_PARAM(manager);
UNUSED_PARAM(oldLocation);
[self sendLocation:newLocation];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
ASSERT(m_callback);
ASSERT(error);
UNUSED_PARAM(manager);
PositionError::ErrorCode code;
switch ([error code]) {
case kCLErrorDenied:
code = PositionError::PERMISSION_DENIED;
break;
case kCLErrorLocationUnknown:
code = PositionError::POSITION_UNAVAILABLE;
break;
default:
code = PositionError::POSITION_UNAVAILABLE;
break;
}
[self sendError:[NSNumber numberWithInt:code] withString:[error localizedDescription]];
}
@end