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