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