1 /*
2 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Torch Mobile, Inc.
4 * Copyright 2010, The Android Open Source Project
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29 #include "Geolocation.h"
30
31 #if ENABLE(GEOLOCATION)
32
33 #include "Chrome.h"
34 #include "Document.h"
35 #include "Frame.h"
36 #include "Page.h"
37 #include <wtf/CurrentTime.h>
38
39 #if ENABLE(CLIENT_BASED_GEOLOCATION)
40 #include "Coordinates.h"
41 #include "GeolocationController.h"
42 #include "GeolocationError.h"
43 #include "GeolocationPosition.h"
44 #include "PositionError.h"
45 #endif
46
47 namespace WebCore {
48
49 static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
50 static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service";
51 static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents";
52
53 static const int firstAvailableWatchId = 1;
54
55 #if ENABLE(CLIENT_BASED_GEOLOCATION)
56
createGeoposition(GeolocationPosition * position)57 static PassRefPtr<Geoposition> createGeoposition(GeolocationPosition* position)
58 {
59 if (!position)
60 return 0;
61
62 RefPtr<Coordinates> coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(),
63 position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(),
64 position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed());
65 return Geoposition::create(coordinates.release(), convertSecondsToDOMTimeStamp(position->timestamp()));
66 }
67
createPositionError(GeolocationError * error)68 static PassRefPtr<PositionError> createPositionError(GeolocationError* error)
69 {
70 PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
71 switch (error->code()) {
72 case GeolocationError::PermissionDenied:
73 code = PositionError::PERMISSION_DENIED;
74 break;
75 case GeolocationError::PositionUnavailable:
76 code = PositionError::POSITION_UNAVAILABLE;
77 break;
78 }
79
80 return PositionError::create(code, error->message());
81 }
82 #endif
83
GeoNotifier(Geolocation * geolocation,PassRefPtr<PositionCallback> successCallback,PassRefPtr<PositionErrorCallback> errorCallback,PassRefPtr<PositionOptions> options)84 Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
85 : m_geolocation(geolocation)
86 , m_successCallback(successCallback)
87 , m_errorCallback(errorCallback)
88 , m_options(options)
89 , m_timer(this, &Geolocation::GeoNotifier::timerFired)
90 , m_useCachedPosition(false)
91 {
92 ASSERT(m_geolocation);
93 ASSERT(m_successCallback);
94 // If no options were supplied from JS, we should have created a default set
95 // of options in JSGeolocationCustom.cpp.
96 ASSERT(m_options);
97 }
98
setFatalError(PassRefPtr<PositionError> error)99 void Geolocation::GeoNotifier::setFatalError(PassRefPtr<PositionError> error)
100 {
101 // If a fatal error has already been set, stick with it. This makes sure that
102 // when permission is denied, this is the error reported, as required by the
103 // spec.
104 if (m_fatalError)
105 return;
106
107 m_fatalError = error;
108 // An existing timer may not have a zero timeout.
109 m_timer.stop();
110 m_timer.startOneShot(0);
111 }
112
setUseCachedPosition()113 void Geolocation::GeoNotifier::setUseCachedPosition()
114 {
115 m_useCachedPosition = true;
116 m_timer.startOneShot(0);
117 }
118
hasZeroTimeout() const119 bool Geolocation::GeoNotifier::hasZeroTimeout() const
120 {
121 return m_options->hasTimeout() && m_options->timeout() == 0;
122 }
123
runSuccessCallback(Geoposition * position)124 void Geolocation::GeoNotifier::runSuccessCallback(Geoposition* position)
125 {
126 m_successCallback->handleEvent(position);
127 }
128
startTimerIfNeeded()129 void Geolocation::GeoNotifier::startTimerIfNeeded()
130 {
131 if (m_options->hasTimeout())
132 m_timer.startOneShot(m_options->timeout() / 1000.0);
133 }
134
timerFired(Timer<GeoNotifier> *)135 void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*)
136 {
137 m_timer.stop();
138
139 // Protect this GeoNotifier object, since it
140 // could be deleted by a call to clearWatch in a callback.
141 RefPtr<GeoNotifier> protect(this);
142
143 // Test for fatal error first. This is required for the case where the Frame is
144 // disconnected and requests are cancelled.
145 if (m_fatalError) {
146 if (m_errorCallback)
147 m_errorCallback->handleEvent(m_fatalError.get());
148 // This will cause this notifier to be deleted.
149 m_geolocation->fatalErrorOccurred(this);
150 return;
151 }
152
153 if (m_useCachedPosition) {
154 // Clear the cached position flag in case this is a watch request, which
155 // will continue to run.
156 m_useCachedPosition = false;
157 m_geolocation->requestUsesCachedPosition(this);
158 return;
159 }
160
161 if (m_errorCallback) {
162 RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, "Timeout expired");
163 m_errorCallback->handleEvent(error.get());
164 }
165 m_geolocation->requestTimedOut(this);
166 }
167
set(int id,PassRefPtr<GeoNotifier> prpNotifier)168 void Geolocation::Watchers::set(int id, PassRefPtr<GeoNotifier> prpNotifier)
169 {
170 ASSERT(id > 0);
171 RefPtr<GeoNotifier> notifier = prpNotifier;
172
173 m_idToNotifierMap.set(id, notifier.get());
174 m_notifierToIdMap.set(notifier.release(), id);
175 }
176
remove(int id)177 void Geolocation::Watchers::remove(int id)
178 {
179 ASSERT(id > 0);
180 IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
181 if (iter == m_idToNotifierMap.end())
182 return;
183 m_notifierToIdMap.remove(iter->second);
184 m_idToNotifierMap.remove(iter);
185 }
186
remove(GeoNotifier * notifier)187 void Geolocation::Watchers::remove(GeoNotifier* notifier)
188 {
189 NotifierToIdMap::iterator iter = m_notifierToIdMap.find(notifier);
190 if (iter == m_notifierToIdMap.end())
191 return;
192 m_idToNotifierMap.remove(iter->second);
193 m_notifierToIdMap.remove(iter);
194 }
195
contains(GeoNotifier * notifier) const196 bool Geolocation::Watchers::contains(GeoNotifier* notifier) const
197 {
198 return m_notifierToIdMap.contains(notifier);
199 }
200
clear()201 void Geolocation::Watchers::clear()
202 {
203 m_idToNotifierMap.clear();
204 m_notifierToIdMap.clear();
205 }
206
isEmpty() const207 bool Geolocation::Watchers::isEmpty() const
208 {
209 return m_idToNotifierMap.isEmpty();
210 }
211
getNotifiersVector(GeoNotifierVector & copy) const212 void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const
213 {
214 copyValuesToVector(m_idToNotifierMap, copy);
215 }
216
Geolocation(Frame * frame)217 Geolocation::Geolocation(Frame* frame)
218 : m_frame(frame)
219 #if !ENABLE(CLIENT_BASED_GEOLOCATION)
220 , m_service(GeolocationService::create(this))
221 #endif
222 , m_allowGeolocation(Unknown)
223 {
224 if (!m_frame)
225 return;
226 ASSERT(m_frame->document());
227 m_frame->document()->setUsingGeolocation(true);
228 }
229
~Geolocation()230 Geolocation::~Geolocation()
231 {
232 ASSERT(m_allowGeolocation != InProgress);
233 ASSERT(!m_frame);
234 }
235
page() const236 Page* Geolocation::page() const
237 {
238 return m_frame ? m_frame->page() : 0;
239 }
240
reset()241 void Geolocation::reset()
242 {
243 Page* page = this->page();
244 if (page && m_allowGeolocation == InProgress) {
245 #if ENABLE(CLIENT_BASED_GEOLOCATION)
246 page->geolocationController()->cancelPermissionRequest(this);
247 #else
248 page->chrome()->cancelGeolocationPermissionRequestForFrame(m_frame, this);
249 #endif
250 }
251 // The frame may be moving to a new page and we want to get the permissions from the new page's client.
252 m_allowGeolocation = Unknown;
253 cancelAllRequests();
254 stopUpdating();
255 }
256
disconnectFrame()257 void Geolocation::disconnectFrame()
258 {
259 // Once we are disconnected from the Frame, it is no longer possible to perform any operations.
260 reset();
261 if (m_frame && m_frame->document())
262 m_frame->document()->setUsingGeolocation(false);
263 m_frame = 0;
264 }
265
lastPosition()266 Geoposition* Geolocation::lastPosition()
267 {
268 #if ENABLE(CLIENT_BASED_GEOLOCATION)
269 Page* page = this->page();
270 if (!page)
271 return 0;
272
273 m_lastPosition = createGeoposition(page->geolocationController()->lastPosition());
274 #else
275 m_lastPosition = m_service->lastPosition();
276 #endif
277
278 return m_lastPosition.get();
279 }
280
getCurrentPosition(PassRefPtr<PositionCallback> successCallback,PassRefPtr<PositionErrorCallback> errorCallback,PassRefPtr<PositionOptions> options)281 void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
282 {
283 if (!m_frame)
284 return;
285
286 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options);
287 ASSERT(notifier);
288
289 m_oneShots.add(notifier);
290 }
291
watchPosition(PassRefPtr<PositionCallback> successCallback,PassRefPtr<PositionErrorCallback> errorCallback,PassRefPtr<PositionOptions> options)292 int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
293 {
294 if (!m_frame)
295 return 0;
296
297 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options);
298 ASSERT(notifier);
299
300 static int nextAvailableWatchId = firstAvailableWatchId;
301 // In case of overflow, make sure the ID remains positive, but reuse the ID values.
302 if (nextAvailableWatchId < 1)
303 nextAvailableWatchId = 1;
304 m_watchers.set(nextAvailableWatchId, notifier.release());
305 return nextAvailableWatchId++;
306 }
307
startRequest(PassRefPtr<PositionCallback> successCallback,PassRefPtr<PositionErrorCallback> errorCallback,PassRefPtr<PositionOptions> options)308 PassRefPtr<Geolocation::GeoNotifier> Geolocation::startRequest(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
309 {
310 RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
311
312 // Check whether permissions have already been denied. Note that if this is the case,
313 // the permission state can not change again in the lifetime of this page.
314 if (isDenied())
315 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
316 else if (haveSuitableCachedPosition(notifier->m_options.get()))
317 notifier->setUseCachedPosition();
318 else if (notifier->hasZeroTimeout())
319 notifier->startTimerIfNeeded();
320 #if USE(PREEMPT_GEOLOCATION_PERMISSION)
321 else if (!isAllowed()) {
322 // if we don't yet have permission, request for permission before calling startUpdating()
323 m_pendingForPermissionNotifiers.add(notifier);
324 requestPermission();
325 }
326 #endif
327 else if (startUpdating(notifier.get()))
328 notifier->startTimerIfNeeded();
329 else
330 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
331
332 return notifier.release();
333 }
334
fatalErrorOccurred(Geolocation::GeoNotifier * notifier)335 void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier)
336 {
337 // This request has failed fatally. Remove it from our lists.
338 m_oneShots.remove(notifier);
339 m_watchers.remove(notifier);
340
341 if (!hasListeners())
342 stopUpdating();
343 }
344
requestUsesCachedPosition(GeoNotifier * notifier)345 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
346 {
347 // This is called asynchronously, so the permissions could have been denied
348 // since we last checked in startRequest.
349 if (isDenied()) {
350 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
351 return;
352 }
353
354 m_requestsAwaitingCachedPosition.add(notifier);
355
356 // If permissions are allowed, make the callback
357 if (isAllowed()) {
358 makeCachedPositionCallbacks();
359 return;
360 }
361
362 // Request permissions, which may be synchronous or asynchronous.
363 requestPermission();
364 }
365
makeCachedPositionCallbacks()366 void Geolocation::makeCachedPositionCallbacks()
367 {
368 // All modifications to m_requestsAwaitingCachedPosition are done
369 // asynchronously, so we don't need to worry about it being modified from
370 // the callbacks.
371 GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
372 for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
373 GeoNotifier* notifier = iter->get();
374 notifier->runSuccessCallback(m_positionCache.cachedPosition());
375
376 // If this is a one-shot request, stop it. Otherwise, if the watch still
377 // exists, start the service to get updates.
378 if (m_oneShots.contains(notifier))
379 m_oneShots.remove(notifier);
380 else if (m_watchers.contains(notifier)) {
381 if (notifier->hasZeroTimeout() || startUpdating(notifier))
382 notifier->startTimerIfNeeded();
383 else
384 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
385 }
386 }
387
388 m_requestsAwaitingCachedPosition.clear();
389
390 if (!hasListeners())
391 stopUpdating();
392 }
393
requestTimedOut(GeoNotifier * notifier)394 void Geolocation::requestTimedOut(GeoNotifier* notifier)
395 {
396 // If this is a one-shot request, stop it.
397 m_oneShots.remove(notifier);
398
399 if (!hasListeners())
400 stopUpdating();
401 }
402
haveSuitableCachedPosition(PositionOptions * options)403 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
404 {
405 if (!m_positionCache.cachedPosition())
406 return false;
407 if (!options->hasMaximumAge())
408 return true;
409 if (!options->maximumAge())
410 return false;
411 DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
412 return m_positionCache.cachedPosition()->timestamp() > currentTimeMillis - options->maximumAge();
413 }
414
clearWatch(int watchId)415 void Geolocation::clearWatch(int watchId)
416 {
417 if (watchId < firstAvailableWatchId)
418 return;
419
420 m_watchers.remove(watchId);
421
422 if (!hasListeners())
423 stopUpdating();
424 }
425
suspend()426 void Geolocation::suspend()
427 {
428 #if !ENABLE(CLIENT_BASED_GEOLOCATION)
429 if (hasListeners())
430 m_service->suspend();
431 #endif
432 }
433
resume()434 void Geolocation::resume()
435 {
436 #if !ENABLE(CLIENT_BASED_GEOLOCATION)
437 if (hasListeners())
438 m_service->resume();
439 #endif
440 }
441
setIsAllowed(bool allowed)442 void Geolocation::setIsAllowed(bool allowed)
443 {
444 // Protect the Geolocation object from garbage collection during a callback.
445 RefPtr<Geolocation> protect(this);
446
447 // This may be due to either a new position from the service, or a cached
448 // position.
449 m_allowGeolocation = allowed ? Yes : No;
450
451 #if USE(PREEMPT_GEOLOCATION_PERMISSION)
452 // Permission request was made during the startRequest process
453 if (!m_pendingForPermissionNotifiers.isEmpty()) {
454 handlePendingPermissionNotifiers();
455 m_pendingForPermissionNotifiers.clear();
456 return;
457 }
458 #endif
459
460 if (!isAllowed()) {
461 RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
462 error->setIsFatal(true);
463 handleError(error.get());
464 m_requestsAwaitingCachedPosition.clear();
465 return;
466 }
467
468 // If the service has a last position, use it to call back for all requests.
469 // If any of the requests are waiting for permission for a cached position,
470 // the position from the service will be at least as fresh.
471 if (lastPosition())
472 makeSuccessCallbacks();
473 else
474 makeCachedPositionCallbacks();
475 }
476
sendError(GeoNotifierVector & notifiers,PositionError * error)477 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
478 {
479 GeoNotifierVector::const_iterator end = notifiers.end();
480 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
481 RefPtr<GeoNotifier> notifier = *it;
482
483 if (notifier->m_errorCallback)
484 notifier->m_errorCallback->handleEvent(error);
485 }
486 }
487
sendPosition(GeoNotifierVector & notifiers,Geoposition * position)488 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
489 {
490 GeoNotifierVector::const_iterator end = notifiers.end();
491 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
492 RefPtr<GeoNotifier> notifier = *it;
493 ASSERT(notifier->m_successCallback);
494
495 notifier->m_successCallback->handleEvent(position);
496 }
497 }
498
stopTimer(GeoNotifierVector & notifiers)499 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
500 {
501 GeoNotifierVector::const_iterator end = notifiers.end();
502 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
503 RefPtr<GeoNotifier> notifier = *it;
504 notifier->m_timer.stop();
505 }
506 }
507
stopTimersForOneShots()508 void Geolocation::stopTimersForOneShots()
509 {
510 GeoNotifierVector copy;
511 copyToVector(m_oneShots, copy);
512
513 stopTimer(copy);
514 }
515
stopTimersForWatchers()516 void Geolocation::stopTimersForWatchers()
517 {
518 GeoNotifierVector copy;
519 m_watchers.getNotifiersVector(copy);
520
521 stopTimer(copy);
522 }
523
stopTimers()524 void Geolocation::stopTimers()
525 {
526 stopTimersForOneShots();
527 stopTimersForWatchers();
528 }
529
cancelRequests(GeoNotifierVector & notifiers)530 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
531 {
532 GeoNotifierVector::const_iterator end = notifiers.end();
533 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
534 (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
535 }
536
cancelAllRequests()537 void Geolocation::cancelAllRequests()
538 {
539 GeoNotifierVector copy;
540 copyToVector(m_oneShots, copy);
541 cancelRequests(copy);
542 m_watchers.getNotifiersVector(copy);
543 cancelRequests(copy);
544 }
545
extractNotifiersWithCachedPosition(GeoNotifierVector & notifiers,GeoNotifierVector * cached)546 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
547 {
548 GeoNotifierVector nonCached;
549 GeoNotifierVector::iterator end = notifiers.end();
550 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
551 GeoNotifier* notifier = it->get();
552 if (notifier->m_useCachedPosition) {
553 if (cached)
554 cached->append(notifier);
555 } else
556 nonCached.append(notifier);
557 }
558 notifiers.swap(nonCached);
559 }
560
copyToSet(const GeoNotifierVector & src,GeoNotifierSet & dest)561 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
562 {
563 GeoNotifierVector::const_iterator end = src.end();
564 for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
565 GeoNotifier* notifier = it->get();
566 dest.add(notifier);
567 }
568 }
569
handleError(PositionError * error)570 void Geolocation::handleError(PositionError* error)
571 {
572 ASSERT(error);
573
574 GeoNotifierVector oneShotsCopy;
575 copyToVector(m_oneShots, oneShotsCopy);
576
577 GeoNotifierVector watchersCopy;
578 m_watchers.getNotifiersVector(watchersCopy);
579
580 // Clear the lists before we make the callbacks, to avoid clearing notifiers
581 // added by calls to Geolocation methods from the callbacks, and to prevent
582 // further callbacks to these notifiers.
583 GeoNotifierVector oneShotsWithCachedPosition;
584 m_oneShots.clear();
585 if (error->isFatal())
586 m_watchers.clear();
587 else {
588 // Don't send non-fatal errors to notifiers due to receive a cached position.
589 extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
590 extractNotifiersWithCachedPosition(watchersCopy, 0);
591 }
592
593 sendError(oneShotsCopy, error);
594 sendError(watchersCopy, error);
595
596 // hasListeners() doesn't distinguish between notifiers due to receive a
597 // cached position and those requiring a fresh position. Perform the check
598 // before restoring the notifiers below.
599 if (!hasListeners())
600 stopUpdating();
601
602 // Maintain a reference to the cached notifiers until their timer fires.
603 copyToSet(oneShotsWithCachedPosition, m_oneShots);
604 }
605
requestPermission()606 void Geolocation::requestPermission()
607 {
608 if (m_allowGeolocation > Unknown)
609 return;
610
611 Page* page = this->page();
612 if (!page)
613 return;
614
615 m_allowGeolocation = InProgress;
616
617 // Ask the embedder: it maintains the geolocation challenge policy itself.
618 #if ENABLE(CLIENT_BASED_GEOLOCATION)
619 page->geolocationController()->requestPermission(this);
620 #else
621 page->chrome()->requestGeolocationPermissionForFrame(m_frame, this);
622 #endif
623 }
624
positionChangedInternal()625 void Geolocation::positionChangedInternal()
626 {
627 m_positionCache.setCachedPosition(lastPosition());
628
629 // Stop all currently running timers.
630 stopTimers();
631
632 if (!isAllowed()) {
633 // requestPermission() will ask the chrome for permission. This may be
634 // implemented synchronously or asynchronously. In both cases,
635 // makeSuccessCallbacks() will be called if permission is granted, so
636 // there's nothing more to do here.
637 requestPermission();
638 return;
639 }
640
641 makeSuccessCallbacks();
642 }
643
makeSuccessCallbacks()644 void Geolocation::makeSuccessCallbacks()
645 {
646 ASSERT(lastPosition());
647 ASSERT(isAllowed());
648
649 GeoNotifierVector oneShotsCopy;
650 copyToVector(m_oneShots, oneShotsCopy);
651
652 GeoNotifierVector watchersCopy;
653 m_watchers.getNotifiersVector(watchersCopy);
654
655 // Clear the lists before we make the callbacks, to avoid clearing notifiers
656 // added by calls to Geolocation methods from the callbacks, and to prevent
657 // further callbacks to these notifiers.
658 m_oneShots.clear();
659
660 sendPosition(oneShotsCopy, lastPosition());
661 sendPosition(watchersCopy, lastPosition());
662
663 if (!hasListeners())
664 stopUpdating();
665 }
666
667 #if ENABLE(CLIENT_BASED_GEOLOCATION)
668
positionChanged()669 void Geolocation::positionChanged()
670 {
671 positionChangedInternal();
672 }
673
setError(GeolocationError * error)674 void Geolocation::setError(GeolocationError* error)
675 {
676 RefPtr<PositionError> positionError = createPositionError(error);
677 handleError(positionError.get());
678 }
679
680 #else
681
geolocationServicePositionChanged(GeolocationService * service)682 void Geolocation::geolocationServicePositionChanged(GeolocationService* service)
683 {
684 ASSERT_UNUSED(service, service == m_service);
685 ASSERT(m_service->lastPosition());
686
687 positionChangedInternal();
688 }
689
geolocationServiceErrorOccurred(GeolocationService * service)690 void Geolocation::geolocationServiceErrorOccurred(GeolocationService* service)
691 {
692 ASSERT(service->lastError());
693
694 // Note that we do not stop timers here. For one-shots, the request is
695 // cleared in handleError. For watchers, the spec requires that the timer is
696 // not cleared.
697 handleError(service->lastError());
698 }
699
700 #endif
701
startUpdating(GeoNotifier * notifier)702 bool Geolocation::startUpdating(GeoNotifier* notifier)
703 {
704 #if ENABLE(CLIENT_BASED_GEOLOCATION)
705 Page* page = this->page();
706 if (!page)
707 return false;
708
709 page->geolocationController()->addObserver(this, notifier->m_options->enableHighAccuracy());
710 return true;
711 #else
712 return m_service->startUpdating(notifier->m_options.get());
713 #endif
714 }
715
stopUpdating()716 void Geolocation::stopUpdating()
717 {
718 #if ENABLE(CLIENT_BASED_GEOLOCATION)
719 Page* page = this->page();
720 if (!page)
721 return;
722
723 page->geolocationController()->removeObserver(this);
724 #else
725 m_service->stopUpdating();
726 #endif
727
728 }
729
730 #if USE(PREEMPT_GEOLOCATION_PERMISSION)
handlePendingPermissionNotifiers()731 void Geolocation::handlePendingPermissionNotifiers()
732 {
733 // While we iterate through the list, we need not worry about list being modified as the permission
734 // is already set to Yes/No and no new listeners will be added to the pending list
735 GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
736 for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
737 GeoNotifier* notifier = iter->get();
738
739 if (isAllowed()) {
740 // start all pending notification requests as permission granted.
741 // The notifier is always ref'ed by m_oneShots or m_watchers.
742 if (startUpdating(notifier))
743 notifier->startTimerIfNeeded();
744 else
745 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
746 } else
747 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
748 }
749 }
750 #endif
751
752 } // namespace WebCore
753
754 #else
755
756 namespace WebCore {
757
clearWatch(int)758 void Geolocation::clearWatch(int) {}
759
reset()760 void Geolocation::reset() {}
761
disconnectFrame()762 void Geolocation::disconnectFrame() {}
763
Geolocation(Frame *)764 Geolocation::Geolocation(Frame*) {}
765
~Geolocation()766 Geolocation::~Geolocation() {}
767
setIsAllowed(bool)768 void Geolocation::setIsAllowed(bool) {}
769
770 }
771
772 #endif // ENABLE(GEOLOCATION)
773