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