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 "Frame.h" 35 #include "Page.h" 36 #if PLATFORM(ANDROID) 37 #include "PlatformBridge.h" 38 #endif 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 void Geolocation::Watchers::remove(int id) 180 { 181 ASSERT(id > 0); 182 IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id); 183 if (iter == m_idToNotifierMap.end()) 184 return; 185 m_notifierToIdMap.remove(iter->second); 186 m_idToNotifierMap.remove(iter); 187 } 188 189 void Geolocation::Watchers::remove(GeoNotifier* notifier) 190 { 191 NotifierToIdMap::iterator iter = m_notifierToIdMap.find(notifier); 192 if (iter == m_notifierToIdMap.end()) 193 return; 194 m_idToNotifierMap.remove(iter->second); 195 m_notifierToIdMap.remove(iter); 196 } 197 198 bool Geolocation::Watchers::contains(GeoNotifier* notifier) const 199 { 200 return m_notifierToIdMap.contains(notifier); 201 } 202 203 void Geolocation::Watchers::clear() 204 { 205 m_idToNotifierMap.clear(); 206 m_notifierToIdMap.clear(); 207 } 208 209 bool Geolocation::Watchers::isEmpty() const 210 { 211 return m_idToNotifierMap.isEmpty(); 212 } 213 214 void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const 215 { 216 copyValuesToVector(m_idToNotifierMap, copy); 217 } 218 219 Geolocation::Geolocation(Frame* frame) 220 : m_frame(frame) 221 #if !ENABLE(CLIENT_BASED_GEOLOCATION) 222 , m_service(GeolocationService::create(this)) 223 #endif 224 , m_allowGeolocation(Unknown) 225 { 226 if (!m_frame) 227 return; 228 ASSERT(m_frame->document()); 229 m_frame->document()->setUsingGeolocation(true); 230 } 231 232 Geolocation::~Geolocation() 233 { 234 ASSERT(m_allowGeolocation != InProgress); 235 ASSERT(!m_frame); 236 } 237 238 Page* Geolocation::page() const 239 { 240 return m_frame ? m_frame->page() : 0; 241 } 242 243 void Geolocation::reset() 244 { 245 Page* page = this->page(); 246 if (page && m_allowGeolocation == InProgress) { 247 #if ENABLE(CLIENT_BASED_GEOLOCATION) 248 page->geolocationController()->cancelPermissionRequest(this); 249 #else 250 page->chrome()->cancelGeolocationPermissionRequestForFrame(m_frame, this); 251 #endif 252 } 253 // The frame may be moving to a new page and we want to get the permissions from the new page's client. 254 m_allowGeolocation = Unknown; 255 cancelAllRequests(); 256 stopUpdating(); 257 } 258 259 void Geolocation::disconnectFrame() 260 { 261 // Once we are disconnected from the Frame, it is no longer possible to perform any operations. 262 reset(); 263 if (m_frame && m_frame->document()) 264 m_frame->document()->setUsingGeolocation(false); 265 m_frame = 0; 266 } 267 268 Geoposition* Geolocation::lastPosition() 269 { 270 #if ENABLE(CLIENT_BASED_GEOLOCATION) 271 Page* page = this->page(); 272 if (!page) 273 return 0; 274 275 m_lastPosition = createGeoposition(page->geolocationController()->lastPosition()); 276 #else 277 m_lastPosition = m_service->lastPosition(); 278 #endif 279 280 return m_lastPosition.get(); 281 } 282 283 void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) 284 { 285 if (!m_frame) 286 return; 287 288 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options); 289 ASSERT(notifier); 290 291 m_oneShots.add(notifier); 292 } 293 294 int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) 295 { 296 if (!m_frame) 297 return 0; 298 299 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options); 300 ASSERT(notifier); 301 302 static int nextAvailableWatchId = firstAvailableWatchId; 303 // In case of overflow, make sure the ID remains positive, but reuse the ID values. 304 if (nextAvailableWatchId < 1) 305 nextAvailableWatchId = 1; 306 m_watchers.set(nextAvailableWatchId, notifier.release()); 307 return nextAvailableWatchId++; 308 } 309 310 PassRefPtr<Geolocation::GeoNotifier> Geolocation::startRequest(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) 311 { 312 RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options); 313 314 // Check whether permissions have already been denied. Note that if this is the case, 315 // the permission state can not change again in the lifetime of this page. 316 if (isDenied()) 317 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); 318 else if (haveSuitableCachedPosition(notifier->m_options.get())) 319 notifier->setUseCachedPosition(); 320 else if (notifier->hasZeroTimeout()) 321 notifier->startTimerIfNeeded(); 322 #if USE(PREEMPT_GEOLOCATION_PERMISSION) 323 else if (!isAllowed()) { 324 // if we don't yet have permission, request for permission before calling startUpdating() 325 m_pendingForPermissionNotifiers.add(notifier); 326 requestPermission(); 327 } 328 #endif 329 else if (startUpdating(notifier.get())) 330 notifier->startTimerIfNeeded(); 331 else 332 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); 333 334 return notifier.release(); 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_positionCache.cachedPosition()); 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_positionCache.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_positionCache.cachedPosition()->timestamp() > currentTimeMillis - options->maximumAge(); 415 } 416 417 void Geolocation::clearWatch(int watchId) 418 { 419 if (watchId < firstAvailableWatchId) 420 return; 421 422 m_watchers.remove(watchId); 423 424 if (!hasListeners()) 425 stopUpdating(); 426 } 427 428 void Geolocation::suspend() 429 { 430 #if !ENABLE(CLIENT_BASED_GEOLOCATION) 431 if (hasListeners()) 432 m_service->suspend(); 433 #endif 434 } 435 436 void Geolocation::resume() 437 { 438 #if !ENABLE(CLIENT_BASED_GEOLOCATION) 439 if (hasListeners()) 440 m_service->resume(); 441 #endif 442 } 443 444 void Geolocation::setIsAllowed(bool allowed) 445 { 446 // This may be due to either a new position from the service, or a cached 447 // position. 448 m_allowGeolocation = allowed ? Yes : No; 449 450 #if USE(PREEMPT_GEOLOCATION_PERMISSION) 451 // Permission request was made during the startRequest process 452 if (!m_pendingForPermissionNotifiers.isEmpty()) { 453 handlePendingPermissionNotifiers(); 454 m_pendingForPermissionNotifiers.clear(); 455 return; 456 } 457 #endif 458 459 if (!isAllowed()) { 460 RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); 461 error->setIsFatal(true); 462 handleError(error.get()); 463 m_requestsAwaitingCachedPosition.clear(); 464 return; 465 } 466 467 // If the service has a last position, use it to call back for all requests. 468 // If any of the requests are waiting for permission for a cached position, 469 // the position from the service will be at least as fresh. 470 if (lastPosition()) 471 makeSuccessCallbacks(); 472 else 473 makeCachedPositionCallbacks(); 474 } 475 476 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error) 477 { 478 GeoNotifierVector::const_iterator end = notifiers.end(); 479 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { 480 RefPtr<GeoNotifier> notifier = *it; 481 482 if (notifier->m_errorCallback) 483 notifier->m_errorCallback->handleEvent(error); 484 } 485 } 486 487 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position) 488 { 489 GeoNotifierVector::const_iterator end = notifiers.end(); 490 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { 491 RefPtr<GeoNotifier> notifier = *it; 492 ASSERT(notifier->m_successCallback); 493 494 notifier->m_successCallback->handleEvent(position); 495 } 496 } 497 498 void Geolocation::stopTimer(GeoNotifierVector& notifiers) 499 { 500 GeoNotifierVector::const_iterator end = notifiers.end(); 501 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { 502 RefPtr<GeoNotifier> notifier = *it; 503 notifier->m_timer.stop(); 504 } 505 } 506 507 void Geolocation::stopTimersForOneShots() 508 { 509 GeoNotifierVector copy; 510 copyToVector(m_oneShots, copy); 511 512 stopTimer(copy); 513 } 514 515 void Geolocation::stopTimersForWatchers() 516 { 517 GeoNotifierVector copy; 518 m_watchers.getNotifiersVector(copy); 519 520 stopTimer(copy); 521 } 522 523 void Geolocation::stopTimers() 524 { 525 stopTimersForOneShots(); 526 stopTimersForWatchers(); 527 } 528 529 void Geolocation::cancelRequests(GeoNotifierVector& notifiers) 530 { 531 GeoNotifierVector::const_iterator end = notifiers.end(); 532 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) 533 (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage)); 534 } 535 536 void Geolocation::cancelAllRequests() 537 { 538 GeoNotifierVector copy; 539 copyToVector(m_oneShots, copy); 540 cancelRequests(copy); 541 m_watchers.getNotifiersVector(copy); 542 cancelRequests(copy); 543 } 544 545 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached) 546 { 547 GeoNotifierVector nonCached; 548 GeoNotifierVector::iterator end = notifiers.end(); 549 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) { 550 GeoNotifier* notifier = it->get(); 551 if (notifier->m_useCachedPosition) { 552 if (cached) 553 cached->append(notifier); 554 } else 555 nonCached.append(notifier); 556 } 557 notifiers.swap(nonCached); 558 } 559 560 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest) 561 { 562 GeoNotifierVector::const_iterator end = src.end(); 563 for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) { 564 GeoNotifier* notifier = it->get(); 565 dest.add(notifier); 566 } 567 } 568 569 void Geolocation::handleError(PositionError* error) 570 { 571 ASSERT(error); 572 573 GeoNotifierVector oneShotsCopy; 574 copyToVector(m_oneShots, oneShotsCopy); 575 576 GeoNotifierVector watchersCopy; 577 m_watchers.getNotifiersVector(watchersCopy); 578 579 // Clear the lists before we make the callbacks, to avoid clearing notifiers 580 // added by calls to Geolocation methods from the callbacks, and to prevent 581 // further callbacks to these notifiers. 582 GeoNotifierVector oneShotsWithCachedPosition; 583 m_oneShots.clear(); 584 if (error->isFatal()) 585 m_watchers.clear(); 586 else { 587 // Don't send non-fatal errors to notifiers due to receive a cached position. 588 extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition); 589 extractNotifiersWithCachedPosition(watchersCopy, 0); 590 } 591 592 sendError(oneShotsCopy, error); 593 sendError(watchersCopy, error); 594 595 // hasListeners() doesn't distinguish between notifiers due to receive a 596 // cached position and those requiring a fresh position. Perform the check 597 // before restoring the notifiers below. 598 if (!hasListeners()) 599 stopUpdating(); 600 601 // Maintain a reference to the cached notifiers until their timer fires. 602 copyToSet(oneShotsWithCachedPosition, m_oneShots); 603 } 604 605 void Geolocation::requestPermission() 606 { 607 if (m_allowGeolocation > Unknown) 608 return; 609 610 Page* page = this->page(); 611 if (!page) 612 return; 613 614 m_allowGeolocation = InProgress; 615 616 // Ask the embedder: it maintains the geolocation challenge policy itself. 617 #if ENABLE(CLIENT_BASED_GEOLOCATION) 618 page->geolocationController()->requestPermission(this); 619 #else 620 page->chrome()->requestGeolocationPermissionForFrame(m_frame, this); 621 #endif 622 } 623 624 void Geolocation::positionChangedInternal() 625 { 626 m_positionCache.setCachedPosition(lastPosition()); 627 628 // Stop all currently running timers. 629 stopTimers(); 630 631 if (!isAllowed()) { 632 // requestPermission() will ask the chrome for permission. This may be 633 // implemented synchronously or asynchronously. In both cases, 634 // makeSuccessCallbacks() will be called if permission is granted, so 635 // there's nothing more to do here. 636 requestPermission(); 637 return; 638 } 639 640 makeSuccessCallbacks(); 641 } 642 643 void Geolocation::makeSuccessCallbacks() 644 { 645 ASSERT(lastPosition()); 646 ASSERT(isAllowed()); 647 648 GeoNotifierVector oneShotsCopy; 649 copyToVector(m_oneShots, oneShotsCopy); 650 651 GeoNotifierVector watchersCopy; 652 m_watchers.getNotifiersVector(watchersCopy); 653 654 // Clear the lists before we make the callbacks, to avoid clearing notifiers 655 // added by calls to Geolocation methods from the callbacks, and to prevent 656 // further callbacks to these notifiers. 657 m_oneShots.clear(); 658 659 sendPosition(oneShotsCopy, lastPosition()); 660 sendPosition(watchersCopy, lastPosition()); 661 662 if (!hasListeners()) 663 stopUpdating(); 664 } 665 666 #if ENABLE(CLIENT_BASED_GEOLOCATION) 667 668 void Geolocation::positionChanged() 669 { 670 positionChangedInternal(); 671 } 672 673 void Geolocation::setError(GeolocationError* error) 674 { 675 RefPtr<PositionError> positionError = createPositionError(error); 676 handleError(positionError.get()); 677 } 678 679 #else 680 681 void Geolocation::geolocationServicePositionChanged(GeolocationService* service) 682 { 683 ASSERT_UNUSED(service, service == m_service); 684 ASSERT(m_service->lastPosition()); 685 686 positionChangedInternal(); 687 } 688 689 void Geolocation::geolocationServiceErrorOccurred(GeolocationService* service) 690 { 691 ASSERT(service->lastError()); 692 693 // Note that we do not stop timers here. For one-shots, the request is 694 // cleared in handleError. For watchers, the spec requires that the timer is 695 // not cleared. 696 handleError(service->lastError()); 697 } 698 699 #endif 700 701 bool Geolocation::startUpdating(GeoNotifier* notifier) 702 { 703 #if ENABLE(CLIENT_BASED_GEOLOCATION) 704 Page* page = this->page(); 705 if (!page) 706 return false; 707 708 page->geolocationController()->addObserver(this, notifier->m_options->enableHighAccuracy()); 709 return true; 710 #else 711 #if PLATFORM(ANDROID) 712 // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082 713 // Note that the correct fix is to use a 'paused' flag in WebCore, rather 714 // than calling into PlatformBridge. 715 if (!m_frame) 716 return false; 717 FrameView* view = m_frame->view(); 718 if (!view) 719 return false; 720 return m_service->startUpdating(notifier->m_options.get(), PlatformBridge::isWebViewPaused(view)); 721 #else 722 return m_service->startUpdating(notifier->m_options.get()); 723 #endif 724 #endif 725 } 726 727 void Geolocation::stopUpdating() 728 { 729 #if ENABLE(CLIENT_BASED_GEOLOCATION) 730 Page* page = this->page(); 731 if (!page) 732 return; 733 734 page->geolocationController()->removeObserver(this); 735 #else 736 m_service->stopUpdating(); 737 #endif 738 739 } 740 741 #if USE(PREEMPT_GEOLOCATION_PERMISSION) 742 void Geolocation::handlePendingPermissionNotifiers() 743 { 744 // While we iterate through the list, we need not worry about list being modified as the permission 745 // is already set to Yes/No and no new listeners will be added to the pending list 746 GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end(); 747 for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) { 748 GeoNotifier* notifier = iter->get(); 749 750 if (isAllowed()) { 751 // start all pending notification requests as permission granted. 752 // The notifier is always ref'ed by m_oneShots or m_watchers. 753 if (startUpdating(notifier)) 754 notifier->startTimerIfNeeded(); 755 else 756 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); 757 } else 758 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); 759 } 760 } 761 #endif 762 763 } // namespace WebCore 764 765 #else 766 767 namespace WebCore { 768 769 void Geolocation::clearWatch(int) {} 770 771 void Geolocation::reset() {} 772 773 void Geolocation::disconnectFrame() {} 774 775 Geolocation::Geolocation(Frame*) {} 776 777 Geolocation::~Geolocation() {} 778 779 void Geolocation::setIsAllowed(bool) {} 780 781 } 782 783 #endif // ENABLE(GEOLOCATION) 784