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