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