location_manager.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/api/location/location_manager.h" 6 7#include "base/bind.h" 8#include "base/lazy_instance.h" 9#include "chrome/browser/extensions/event_router.h" 10#include "chrome/browser/extensions/extension_system.h" 11#include "chrome/common/extensions/api/location.h" 12#include "chrome/common/extensions/permissions/permission_set.h" 13#include "content/public/browser/geolocation_provider.h" 14#include "content/public/common/geoposition.h" 15 16// TODO(vadimt): Add tests. 17namespace extensions { 18 19namespace location = api::location; 20 21// Request created by chrome.location.watchLocation() call. 22// Lives in the IO thread, except for the constructor. 23class LocationRequest 24 : public base::RefCountedThreadSafe<LocationRequest, 25 BrowserThread::DeleteOnIOThread> { 26 public: 27 LocationRequest( 28 const base::WeakPtr<LocationManager>& location_manager, 29 const std::string& extension_id, 30 const std::string& request_name); 31 32 // Finishes the necessary setup for this object. 33 // Call this method immediately after taking a strong reference 34 // to this object. 35 // 36 // Ideally, we would do this at construction time, but currently 37 // our refcount starts at zero. BrowserThread::PostTask will take a ref 38 // and potentially release it before we are done, destroying us in the 39 // constructor. 40 void Initialize(); 41 42 const std::string& request_name() const { return request_name_; } 43 44 // Grants permission for using geolocation. 45 static void GrantPermission(); 46 47 private: 48 friend class base::DeleteHelper<LocationRequest>; 49 friend struct content::BrowserThread::DeleteOnThread< 50 content::BrowserThread::IO>; 51 52 virtual ~LocationRequest(); 53 54 void AddObserverOnIOThread(); 55 56 void OnLocationUpdate(const content::Geoposition& position); 57 58 // Request name. 59 const std::string request_name_; 60 61 // Id of the owner extension. 62 const std::string extension_id_; 63 64 // Owning location manager. 65 const base::WeakPtr<LocationManager> location_manager_; 66 67 content::GeolocationProvider::LocationUpdateCallback callback_; 68 69 DISALLOW_COPY_AND_ASSIGN(LocationRequest); 70}; 71 72LocationRequest::LocationRequest( 73 const base::WeakPtr<LocationManager>& location_manager, 74 const std::string& extension_id, 75 const std::string& request_name) 76 : request_name_(request_name), 77 extension_id_(extension_id), 78 location_manager_(location_manager) { 79 // TODO(vadimt): use request_info. 80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 81} 82 83void LocationRequest::Initialize() { 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 85 callback_ = base::Bind(&LocationRequest::OnLocationUpdate, 86 base::Unretained(this)); 87 88 BrowserThread::PostTask( 89 BrowserThread::IO, 90 FROM_HERE, 91 base::Bind(&LocationRequest::AddObserverOnIOThread, 92 this)); 93} 94 95void LocationRequest::GrantPermission() { 96 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 97 content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices(); 98} 99 100LocationRequest::~LocationRequest() { 101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 102 content::GeolocationProvider::GetInstance()->RemoveLocationUpdateCallback( 103 callback_); 104} 105 106void LocationRequest::AddObserverOnIOThread() { 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 108 109 // TODO(vadimt): This can get a location cached by GeolocationProvider, 110 // contrary to the API definition which says that creating a location watch 111 // will get new location. 112 content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback( 113 callback_, true); 114} 115 116void LocationRequest::OnLocationUpdate(const content::Geoposition& position) { 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 118 119 BrowserThread::PostTask( 120 BrowserThread::UI, 121 FROM_HERE, 122 base::Bind(&LocationManager::SendLocationUpdate, 123 location_manager_, 124 extension_id_, 125 request_name_, 126 position)); 127} 128 129LocationManager::LocationManager(Profile* profile) 130 : profile_(profile) { 131 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 132 content::Source<Profile>(profile_)); 133 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 134 content::Source<Profile>(profile_)); 135} 136 137void LocationManager::AddLocationRequest(const std::string& extension_id, 138 const std::string& request_name) { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 // TODO(vadimt): Consider resuming requests after restarting the browser. 141 142 // Override any old request with the same name. 143 RemoveLocationRequest(extension_id, request_name); 144 145 LocationRequestPointer location_request = new LocationRequest(AsWeakPtr(), 146 extension_id, 147 request_name); 148 location_request->Initialize(); 149 location_requests_.insert( 150 LocationRequestMap::value_type(extension_id, location_request)); 151} 152 153void LocationManager::RemoveLocationRequest(const std::string& extension_id, 154 const std::string& name) { 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 156 157 std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator> 158 extension_range = location_requests_.equal_range(extension_id); 159 160 for (LocationRequestMap::iterator it = extension_range.first; 161 it != extension_range.second; 162 ++it) { 163 if (it->second->request_name() == name) { 164 location_requests_.erase(it); 165 return; 166 } 167 } 168} 169 170LocationManager::~LocationManager() { 171} 172 173void LocationManager::GeopositionToApiCoordinates( 174 const content::Geoposition& position, 175 location::Coordinates* coordinates) { 176 coordinates->latitude = position.latitude; 177 coordinates->longitude = position.longitude; 178 if (position.altitude > -10000.) 179 coordinates->altitude.reset(new double(position.altitude)); 180 coordinates->accuracy = position.accuracy; 181 if (position.altitude_accuracy >= 0.) { 182 coordinates->altitude_accuracy.reset( 183 new double(position.altitude_accuracy)); 184 } 185 if (position.heading >= 0. && position.heading <= 360.) 186 coordinates->heading.reset(new double(position.heading)); 187 if (position.speed >= 0.) 188 coordinates->speed.reset(new double(position.speed)); 189} 190 191void LocationManager::SendLocationUpdate( 192 const std::string& extension_id, 193 const std::string& request_name, 194 const content::Geoposition& position) { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 196 197 scoped_ptr<ListValue> args(new ListValue()); 198 std::string event_name; 199 200 if (position.Validate() && 201 position.error_code == content::Geoposition::ERROR_CODE_NONE) { 202 // Set data for onLocationUpdate event. 203 location::Location location; 204 location.name = request_name; 205 GeopositionToApiCoordinates(position, &location.coords); 206 location.timestamp = position.timestamp.ToJsTime(); 207 208 args->Append(location.ToValue().release()); 209 event_name = "location.onLocationUpdate"; 210 } else { 211 // Set data for onLocationError event. 212 // TODO(vadimt): Set name. 213 args->AppendString(position.error_message); 214 event_name = "location.onLocationError"; 215 } 216 217 scoped_ptr<Event> event(new Event(event_name, args.Pass())); 218 219 ExtensionSystem::Get(profile_)->event_router()-> 220 DispatchEventToExtension(extension_id, event.Pass()); 221} 222 223void LocationManager::Observe(int type, 224 const content::NotificationSource& source, 225 const content::NotificationDetails& details) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 228 switch (type) { 229 case chrome::NOTIFICATION_EXTENSION_LOADED: { 230 // Grants permission to use geolocation once an extension with "location" 231 // permission is loaded. 232 const Extension* extension = 233 content::Details<const Extension>(details).ptr(); 234 235 if (extension->HasAPIPermission(APIPermission::kLocation)) { 236 BrowserThread::PostTask( 237 BrowserThread::IO, 238 FROM_HERE, 239 base::Bind(&LocationRequest::GrantPermission)); 240 } 241 break; 242 } 243 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 244 // Delete all requests from the unloaded extension. 245 const Extension* extension = 246 content::Details<const UnloadedExtensionInfo>(details)->extension; 247 location_requests_.erase(extension->id()); 248 break; 249 } 250 default: 251 NOTREACHED(); 252 break; 253 } 254} 255 256static base::LazyInstance<ProfileKeyedAPIFactory<LocationManager> > 257g_factory = LAZY_INSTANCE_INITIALIZER; 258 259// static 260ProfileKeyedAPIFactory<LocationManager>* LocationManager::GetFactoryInstance() { 261 return &g_factory.Get(); 262} 263 264 // static 265LocationManager* LocationManager::Get(Profile* profile) { 266 return ProfileKeyedAPIFactory<LocationManager>::GetForProfile(profile); 267} 268 269} // namespace extensions 270