Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <cmath> | 5 #include "content/browser/device_orientation/provider_impl.h" |
| 6 | |
| 6 #include <set> | 7 #include <set> |
| 7 #include <vector> | 8 #include <vector> |
| 8 | 9 |
| 9 #include "base/bind.h" | 10 #include "base/bind.h" |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
| 12 #include "base/threading/thread.h" | 13 #include "base/threading/thread.h" |
| 13 #include "base/threading/worker_pool.h" | 14 #include "base/threading/worker_pool.h" |
| 14 #include "content/browser/device_orientation/orientation.h" | |
| 15 #include "content/browser/device_orientation/provider_impl.h" | |
| 16 | 15 |
| 17 namespace { | 16 namespace { |
| 18 | 17 |
| 19 bool IsElementSignificantlyDifferent(bool can_provide_element1, | |
| 20 bool can_provide_element2, | |
| 21 double element1, | |
| 22 double element2) { | |
| 23 const double kThreshold = 0.1; | |
| 24 | |
| 25 if (can_provide_element1 != can_provide_element2) | |
| 26 return true; | |
| 27 if (can_provide_element1 && | |
| 28 std::fabs(element1 - element2) >= kThreshold) | |
| 29 return true; | |
| 30 return false; | |
| 31 } | |
| 32 | |
| 33 void DeleteThread(base::Thread* thread) { | 18 void DeleteThread(base::Thread* thread) { |
| 34 thread->Stop(); | 19 thread->Stop(); |
| 35 delete thread; | 20 delete thread; |
| 36 } | 21 } |
| 37 | 22 |
| 38 } | 23 } |
| 39 | 24 |
| 40 namespace device_orientation { | 25 namespace device_orientation { |
| 41 | 26 |
| 42 class ProviderImpl::PollingThread : public base::Thread { | 27 class ProviderImpl::PollingThread : public base::Thread { |
| 43 public: | 28 public: |
| 44 PollingThread(const char* name, | 29 PollingThread(const char* name, |
| 45 base::WeakPtr<ProviderImpl> provider, | 30 base::WeakPtr<ProviderImpl> provider, |
| 46 MessageLoop* creator_loop); | 31 MessageLoop* creator_loop); |
| 47 virtual ~PollingThread(); | 32 virtual ~PollingThread(); |
| 48 | 33 |
| 49 // Method for finding a suitable DataFetcher and starting the polling. | 34 // Method for creating a DataFetcher and starting the polling, if the fetcher |
| 50 void Initialize(DataFetcherFactory factory); | 35 // can provide this type of data. |
| 36 void Initialize(DataFetcherFactory factory, DeviceData::Type type); | |
| 37 | |
| 38 // Method for adding a type of data to poll for. | |
| 39 void DoAddPollingDataType(DeviceData::Type type); | |
| 51 | 40 |
| 52 private: | 41 private: |
| 53 // Method for polling a DataFetcher. | 42 // Method for polling a DataFetcher. |
| 54 void DoPoll(); | 43 void DoPoll(); |
| 55 void ScheduleDoPoll(); | 44 void ScheduleDoPoll(); |
| 56 | 45 |
| 57 // Schedule a notification to the |provider_| which lives on a different | 46 // Schedule a notification to the |provider_| which lives on a different |
| 58 // thread (|creator_loop_| is its message loop). | 47 // thread (|creator_loop_| is its message loop). |
| 59 void ScheduleDoNotify(const Orientation& orientation); | 48 void ScheduleDoNotify(const DeviceData* device_data, |
| 60 | 49 DeviceData::Type device_data_type); |
| 61 static bool SignificantlyDifferent(const Orientation& orientation1, | |
| 62 const Orientation& orientation2); | |
| 63 | 50 |
| 64 enum { kDesiredSamplingIntervalMs = 100 }; | 51 enum { kDesiredSamplingIntervalMs = 100 }; |
| 65 base::TimeDelta SamplingInterval() const; | 52 base::TimeDelta SamplingInterval() const; |
| 66 | 53 |
| 67 // The Message Loop on which this object was created. | 54 // The Message Loop on which this object was created. |
| 68 // Typically the I/O loop, but may be something else during testing. | 55 // Typically the I/O loop, but may be something else during testing. |
| 69 MessageLoop* creator_loop_; | 56 MessageLoop* creator_loop_; |
| 70 | 57 |
| 71 scoped_ptr<DataFetcher> data_fetcher_; | 58 scoped_ptr<DataFetcher> data_fetcher_; |
| 72 Orientation last_orientation_; | 59 std::map<DeviceData::Type, scoped_refptr<const DeviceData> > |
| 60 last_device_data_map_; | |
| 61 std::set<DeviceData::Type> polling_data_types_; | |
| 73 | 62 |
| 74 base::WeakPtr<ProviderImpl> provider_; | 63 base::WeakPtr<ProviderImpl> provider_; |
| 75 }; | 64 }; |
| 76 | 65 |
| 77 ProviderImpl::PollingThread::PollingThread( | 66 ProviderImpl::PollingThread::PollingThread( |
| 78 const char* name, | 67 const char* name, |
| 79 base::WeakPtr<ProviderImpl> provider, | 68 base::WeakPtr<ProviderImpl> provider, |
| 80 MessageLoop* creator_loop) | 69 MessageLoop* creator_loop) |
| 81 : base::Thread(name), | 70 : base::Thread(name), |
| 82 creator_loop_(creator_loop), | 71 creator_loop_(creator_loop), |
| 83 provider_(provider) { | 72 provider_(provider) { |
| 84 } | 73 } |
| 85 | 74 |
| 86 ProviderImpl::PollingThread::~PollingThread() { | 75 ProviderImpl::PollingThread::~PollingThread() { |
| 87 } | 76 } |
| 88 | 77 |
| 89 void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory) { | 78 void ProviderImpl::PollingThread::DoAddPollingDataType(DeviceData::Type type) { |
| 79 DCHECK(MessageLoop::current() == message_loop()); | |
| 80 | |
| 81 polling_data_types_.insert(type); | |
| 82 } | |
| 83 | |
| 84 void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory, | |
| 85 DeviceData::Type type) { | |
| 90 DCHECK(MessageLoop::current() == message_loop()); | 86 DCHECK(MessageLoop::current() == message_loop()); |
| 91 | 87 |
| 92 if (factory != NULL) { | 88 if (factory != NULL) { |
| 93 // Try to use factory to create a fetcher that can provide orientation data. | 89 // Try to use factory to create a fetcher that can provide this type of |
| 90 // data. If factory creates a fetcher that provides this type of data, | |
| 91 // start polling. | |
| 94 scoped_ptr<DataFetcher> fetcher(factory()); | 92 scoped_ptr<DataFetcher> fetcher(factory()); |
| 95 Orientation orientation; | |
| 96 | 93 |
| 97 if (fetcher.get() && fetcher->GetOrientation(&orientation)) { | 94 if (fetcher.get()) { |
| 98 // Pass ownership of fetcher to provider_. | 95 scoped_refptr<const DeviceData> device_data(fetcher->GetDeviceData(type)); |
| 99 data_fetcher_.swap(fetcher); | 96 if (device_data != NULL) { |
| 100 last_orientation_ = orientation; | 97 // Pass ownership of fetcher to provider_. |
| 98 data_fetcher_.swap(fetcher); | |
| 99 last_device_data_map_[type] = device_data; | |
| 101 | 100 |
| 102 // Notify observers. | 101 // Notify observers. |
| 103 if (!orientation.is_empty()) | 102 ScheduleDoNotify(device_data, type); |
| 104 ScheduleDoNotify(orientation); | |
| 105 | 103 |
| 106 // Start polling. | 104 // Start polling. |
| 107 ScheduleDoPoll(); | 105 ScheduleDoPoll(); |
| 108 return; | 106 return; |
| 107 } | |
| 109 } | 108 } |
| 110 } | 109 } |
| 111 | 110 |
| 112 // When no orientation data can be provided. | 111 // When no device data can be provided. |
| 113 ScheduleDoNotify(Orientation::Empty()); | 112 ScheduleDoNotify(NULL, type); |
| 114 } | 113 } |
| 115 | 114 |
| 116 void ProviderImpl::PollingThread::ScheduleDoNotify( | 115 void ProviderImpl::PollingThread::ScheduleDoNotify( |
| 117 const Orientation& orientation) { | 116 const DeviceData* device_data, DeviceData::Type device_data_type) { |
| 118 DCHECK(MessageLoop::current() == message_loop()); | 117 DCHECK(MessageLoop::current() == message_loop()); |
| 119 | 118 |
| 120 creator_loop_->PostTask( | 119 creator_loop_->PostTask(FROM_HERE, |
| 121 FROM_HERE, base::Bind(&ProviderImpl::DoNotify, provider_, orientation)); | 120 base::Bind(&ProviderImpl::DoNotify, provider_, |
| 121 device_data, device_data_type)); | |
| 122 } | 122 } |
| 123 | 123 |
| 124 void ProviderImpl::PollingThread::DoPoll() { | 124 void ProviderImpl::PollingThread::DoPoll() { |
| 125 DCHECK(MessageLoop::current() == message_loop()); | 125 DCHECK(MessageLoop::current() == message_loop()); |
| 126 | 126 |
| 127 Orientation orientation; | 127 // Poll the fetcher for each type of data. |
| 128 if (!data_fetcher_->GetOrientation(&orientation)) { | 128 typedef std::set<DeviceData::Type>::const_iterator SetIterator; |
| 129 LOG(ERROR) << "Failed to poll device orientation data fetcher."; | 129 for (SetIterator i = polling_data_types_.begin(); |
| 130 i != polling_data_types_.end(); ++i) { | |
| 131 DeviceData::Type device_data_type = *i; | |
| 132 scoped_refptr<const DeviceData> device_data(data_fetcher_->GetDeviceData( | |
| 133 device_data_type)); | |
| 130 | 134 |
| 131 ScheduleDoNotify(Orientation::Empty()); | 135 if (device_data == NULL) { |
| 132 return; | 136 LOG(ERROR) << "Failed to poll device data fetcher."; |
| 133 } | 137 ScheduleDoNotify(NULL, device_data_type); |
| 138 continue; | |
| 139 } | |
| 134 | 140 |
| 135 if (!orientation.is_empty() && | 141 const DeviceData* old_data = last_device_data_map_[device_data_type]; |
| 136 SignificantlyDifferent(orientation, last_orientation_)) { | 142 if (old_data != NULL && !device_data->ShouldFireEvent(old_data)) |
| 137 last_orientation_ = orientation; | 143 continue; |
| 138 ScheduleDoNotify(orientation); | 144 |
| 145 // Update the last device data of this type and notify observers. | |
| 146 last_device_data_map_[device_data_type] = device_data; | |
| 147 ScheduleDoNotify(device_data, device_data_type); | |
| 139 } | 148 } |
| 140 | 149 |
| 141 ScheduleDoPoll(); | 150 ScheduleDoPoll(); |
| 142 } | 151 } |
| 143 | 152 |
| 144 void ProviderImpl::PollingThread::ScheduleDoPoll() { | 153 void ProviderImpl::PollingThread::ScheduleDoPoll() { |
| 145 DCHECK(MessageLoop::current() == message_loop()); | 154 DCHECK(MessageLoop::current() == message_loop()); |
| 146 | 155 |
| 147 message_loop()->PostDelayedTask( | 156 message_loop()->PostDelayedTask( |
| 148 FROM_HERE, | 157 FROM_HERE, |
| 149 base::Bind(&PollingThread::DoPoll, base::Unretained(this)), | 158 base::Bind(&PollingThread::DoPoll, base::Unretained(this)), |
| 150 SamplingInterval()); | 159 SamplingInterval()); |
| 151 } | 160 } |
| 152 | 161 |
| 153 // Returns true if two orientations are considered different enough that | |
| 154 // observers should be notified of the new orientation. | |
| 155 bool ProviderImpl::PollingThread::SignificantlyDifferent( | |
| 156 const Orientation& o1, | |
| 157 const Orientation& o2) { | |
| 158 return IsElementSignificantlyDifferent(o1.can_provide_alpha(), | |
| 159 o2.can_provide_alpha(), | |
| 160 o1.alpha(), | |
| 161 o2.alpha()) || | |
| 162 IsElementSignificantlyDifferent(o1.can_provide_beta(), | |
| 163 o2.can_provide_beta(), | |
| 164 o1.beta(), | |
| 165 o2.beta()) || | |
| 166 IsElementSignificantlyDifferent(o1.can_provide_gamma(), | |
| 167 o2.can_provide_gamma(), | |
| 168 o1.gamma(), | |
| 169 o2.gamma()) || | |
| 170 (o1.can_provide_absolute() != o2.can_provide_absolute() || | |
| 171 o1.absolute() != o2.absolute()); | |
| 172 } | |
| 173 | |
| 174 base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const { | 162 base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const { |
| 175 DCHECK(MessageLoop::current() == message_loop()); | 163 DCHECK(MessageLoop::current() == message_loop()); |
| 176 DCHECK(data_fetcher_.get()); | 164 DCHECK(data_fetcher_.get()); |
| 177 | 165 |
| 178 // TODO(erg): There used to be unused code here, that called a default | 166 // TODO(erg): There used to be unused code here, that called a default |
| 179 // implementation on the DataFetcherInterface that was never defined. I'm | 167 // implementation on the DataFetcherInterface that was never defined. I'm |
| 180 // removing unused methods from headers. | 168 // removing unused methods from headers. |
| 181 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); | 169 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); |
| 182 } | 170 } |
| 183 | 171 |
| 184 ProviderImpl::ProviderImpl(DataFetcherFactory factory) | 172 ProviderImpl::ProviderImpl(DataFetcherFactory factory) |
| 185 : creator_loop_(MessageLoop::current()), | 173 : creator_loop_(MessageLoop::current()), |
| 186 factory_(factory), | 174 factory_(factory), |
| 187 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 175 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 188 polling_thread_(NULL) { | 176 polling_thread_(NULL) { |
| 189 } | 177 } |
| 190 | 178 |
| 191 ProviderImpl::~ProviderImpl() { | 179 ProviderImpl::~ProviderImpl() { |
| 192 Stop(); | 180 Stop(); |
| 193 } | 181 } |
| 194 | 182 |
| 183 void ProviderImpl::ScheduleDoAddPollingDataType(DeviceData::Type type) { | |
| 184 DCHECK(MessageLoop::current() == creator_loop_); | |
| 185 | |
| 186 MessageLoop* polling_loop = polling_thread_->message_loop(); | |
| 187 polling_loop->PostTask(FROM_HERE, | |
| 188 base::Bind(&PollingThread::DoAddPollingDataType, | |
| 189 base::Unretained(polling_thread_), | |
| 190 type)); | |
| 191 } | |
| 192 | |
| 195 void ProviderImpl::AddObserver(Observer* observer) { | 193 void ProviderImpl::AddObserver(Observer* observer) { |
| 196 DCHECK(MessageLoop::current() == creator_loop_); | 194 DCHECK(MessageLoop::current() == creator_loop_); |
| 197 | 195 |
| 196 DeviceData::Type type = observer->device_data_type(); | |
| 197 | |
| 198 observers_.insert(observer); | 198 observers_.insert(observer); |
| 199 if (observers_.size() == 1) | 199 if (observers_.size() == 1) |
| 200 Start(); | 200 Start(type); |
| 201 else | 201 else { |
| 202 observer->OnOrientationUpdate(last_notification_); | 202 // Notify observer of most recent notification if one exists. |
| 203 const DeviceData *last_notification = last_notifications_map_[type]; | |
| 204 if (last_notification != NULL) | |
| 205 observer->OnDeviceDataUpdate(last_notification, type); | |
| 206 } | |
| 207 | |
| 208 ScheduleDoAddPollingDataType(type); | |
| 203 } | 209 } |
| 204 | 210 |
| 205 void ProviderImpl::RemoveObserver(Observer* observer) { | 211 void ProviderImpl::RemoveObserver(Observer* observer) { |
| 206 DCHECK(MessageLoop::current() == creator_loop_); | 212 DCHECK(MessageLoop::current() == creator_loop_); |
| 207 | 213 |
| 208 observers_.erase(observer); | 214 observers_.erase(observer); |
| 209 if (observers_.empty()) | 215 if (observers_.empty()) |
| 210 Stop(); | 216 Stop(); |
| 211 } | 217 } |
| 212 | 218 |
| 213 void ProviderImpl::Start() { | 219 void ProviderImpl::Start(DeviceData::Type type) { |
| 214 DCHECK(MessageLoop::current() == creator_loop_); | 220 DCHECK(MessageLoop::current() == creator_loop_); |
| 215 DCHECK(!polling_thread_); | 221 DCHECK(!polling_thread_); |
| 216 | 222 |
| 217 polling_thread_ = new PollingThread("Device orientation polling thread", | 223 polling_thread_ = new PollingThread("Device data polling thread", |
| 218 weak_factory_.GetWeakPtr(), | 224 weak_factory_.GetWeakPtr(), |
| 219 creator_loop_); | 225 creator_loop_); |
| 220 if (!polling_thread_->Start()) { | 226 if (!polling_thread_->Start()) { |
| 221 LOG(ERROR) << "Failed to start device orientation polling thread"; | 227 LOG(ERROR) << "Failed to start device data polling thread"; |
| 222 delete polling_thread_; | 228 delete polling_thread_; |
| 223 polling_thread_ = NULL; | 229 polling_thread_ = NULL; |
| 224 return; | 230 return; |
| 225 } | 231 } |
| 226 ScheduleInitializePollingThread(); | 232 ScheduleInitializePollingThread(type); |
| 227 } | 233 } |
| 228 | 234 |
| 229 void ProviderImpl::Stop() { | 235 void ProviderImpl::Stop() { |
| 230 DCHECK(MessageLoop::current() == creator_loop_); | 236 DCHECK(MessageLoop::current() == creator_loop_); |
| 231 | 237 |
| 232 weak_factory_.InvalidateWeakPtrs(); | 238 weak_factory_.InvalidateWeakPtrs(); |
| 233 if (polling_thread_) { | 239 if (polling_thread_) { |
| 234 polling_thread_->StopSoon(); | 240 polling_thread_->StopSoon(); |
| 235 bool posted = base::WorkerPool::PostTask( | 241 bool posted = base::WorkerPool::PostTask( |
| 236 FROM_HERE, | 242 FROM_HERE, |
| 237 base::Bind(&DeleteThread, base::Unretained(polling_thread_)), | 243 base::Bind(&DeleteThread, base::Unretained(polling_thread_)), |
| 238 true /* task is slow */); | 244 true /* task is slow */); |
| 239 DCHECK(posted); | 245 DCHECK(posted); |
| 240 polling_thread_ = NULL; | 246 polling_thread_ = NULL; |
| 241 } | 247 } |
| 242 } | 248 } |
| 243 | 249 |
| 244 void ProviderImpl::ScheduleInitializePollingThread() { | 250 void ProviderImpl::ScheduleInitializePollingThread( |
| 251 DeviceData::Type device_data_type) { | |
| 245 DCHECK(MessageLoop::current() == creator_loop_); | 252 DCHECK(MessageLoop::current() == creator_loop_); |
| 246 | 253 |
| 247 MessageLoop* polling_loop = polling_thread_->message_loop(); | 254 MessageLoop* polling_loop = polling_thread_->message_loop(); |
| 248 polling_loop->PostTask(FROM_HERE, | 255 polling_loop->PostTask(FROM_HERE, |
| 249 base::Bind(&PollingThread::Initialize, | 256 base::Bind(&PollingThread::Initialize, |
| 250 base::Unretained(polling_thread_), | 257 base::Unretained(polling_thread_), |
| 251 factory_)); | 258 factory_, |
| 259 device_data_type)); | |
| 252 } | 260 } |
| 253 | 261 |
| 254 void ProviderImpl::DoNotify(const Orientation& orientation) { | 262 void ProviderImpl::DoNotify(const DeviceData* device_data, |
| 263 DeviceData::Type device_data_type) { | |
| 255 DCHECK(MessageLoop::current() == creator_loop_); | 264 DCHECK(MessageLoop::current() == creator_loop_); |
| 256 | 265 |
| 257 last_notification_ = orientation; | 266 scoped_refptr<const DeviceData> data(device_data); |
| 258 | 267 |
| 259 typedef std::set<Observer*>::const_iterator Iterator; | 268 // Update last notification of this type. |
| 260 for (Iterator i = observers_.begin(); i != observers_.end(); ++i) | 269 last_notifications_map_[device_data_type] = data; |
| 261 (*i)->OnOrientationUpdate(orientation); | |
| 262 | 270 |
| 263 if (orientation.is_empty()) { | 271 // Notify observers of this type of the new data. |
| 264 // Notify observers about failure to provide data exactly once. | 272 typedef std::set<Observer*>::const_iterator ConstIterator; |
| 265 observers_.clear(); | 273 for (ConstIterator i = observers_.begin(); i != observers_.end(); ++i) { |
| 266 Stop(); | 274 if ((*i)->device_data_type() == device_data_type) |
| 275 (*i)->OnDeviceDataUpdate(data.get(), device_data_type); | |
| 276 } | |
| 277 | |
| 278 if (data == NULL) { | |
| 279 // Notify observers exactly once about failure to provide data. | |
| 280 typedef std::set<Observer*>::iterator Iterator; | |
| 281 Iterator i = observers_.begin(); | |
| 282 while (i != observers_.end()) { | |
| 283 Iterator current = i++; | |
| 284 if ((*current)->device_data_type() == device_data_type) | |
| 285 observers_.erase(current); | |
| 286 } | |
| 287 | |
| 288 if (observers_.empty()) | |
| 289 Stop(); | |
| 267 } | 290 } |
| 268 } | 291 } |
| 269 | 292 |
| 293 | |
| 270 } // namespace device_orientation | 294 } // namespace device_orientation |
| 295 | |
|
hans
2012/08/03 13:05:41
ultra nit: a blank line too much here
aousterh
2012/08/03 13:42:18
Done.
| |
| OLD | NEW |