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 if (!m_frame) 275 return; 276 277 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options); 278 ASSERT(notifier); 279 280 m_oneShots.add(notifier); 281 } 282 283 int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) 284 { 285 if (!m_frame) 286 return 0; 287 288 RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options); 289 ASSERT(notifier); 290 291 static int nextAvailableWatchId = 1; 292 // In case of overflow, make sure the ID remains positive, but reuse the ID values. 293 if (nextAvailableWatchId < 1) 294 nextAvailableWatchId = 1; 295 m_watchers.set(nextAvailableWatchId, notifier.release()); 296 return nextAvailableWatchId++; 297 } 298 299 PassRefPtr<Geolocation::GeoNotifier> Geolocation::startRequest(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) 300 { 301 RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options); 302 303 // Check whether permissions have already been denied. Note that if this is the case, 304 // the permission state can not change again in the lifetime of this page. 305 if (isDenied()) 306 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); 307 else if (haveSuitableCachedPosition(notifier->m_options.get())) 308 notifier->setUseCachedPosition(); 309 else if (notifier->hasZeroTimeout() || startUpdating(notifier.get())) { 310 #if ENABLE(CLIENT_BASED_GEOLOCATION) 311 // Only start timer if we're not waiting for user permission. 312 if (!m_startRequestPermissionNotifier) 313 #endif 314 notifier->startTimerIfNeeded(); 315 } else 316 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); 317 318 return notifier.release(); 319 } 320 321 void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier) 322 { 323 // This request has failed fatally. Remove it from our lists. 324 m_oneShots.remove(notifier); 325 m_watchers.remove(notifier); 326 327 if (!hasListeners()) 328 stopUpdating(); 329 } 330 331 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier) 332 { 333 // This is called asynchronously, so the permissions could have been denied 334 // since we last checked in startRequest. 335 if (isDenied()) { 336 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); 337 return; 338 } 339 340 m_requestsAwaitingCachedPosition.add(notifier); 341 342 // If permissions are allowed, make the callback 343 if (isAllowed()) { 344 makeCachedPositionCallbacks(); 345 return; 346 } 347 348 // Request permissions, which may be synchronous or asynchronous. 349 requestPermission(); 350 } 351 352 void Geolocation::makeCachedPositionCallbacks() 353 { 354 // All modifications to m_requestsAwaitingCachedPosition are done 355 // asynchronously, so we don't need to worry about it being modified from 356 // the callbacks. 357 GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end(); 358 for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) { 359 GeoNotifier* notifier = iter->get(); 360 notifier->runSuccessCallback(m_positionCache->cachedPosition()); 361 362 // If this is a one-shot request, stop it. Otherwise, if the watch still 363 // exists, start the service to get updates. 364 if (m_oneShots.contains(notifier)) 365 m_oneShots.remove(notifier); 366 else if (m_watchers.contains(notifier)) { 367 if (notifier->hasZeroTimeout() || startUpdating(notifier)) 368 notifier->startTimerIfNeeded(); 369 else 370 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); 371 } 372 } 373 374 m_requestsAwaitingCachedPosition.clear(); 375 376 if (!hasListeners()) 377 stopUpdating(); 378 } 379 380 void Geolocation::requestTimedOut(GeoNotifier* notifier) 381 { 382 // If this is a one-shot request, stop it. 383 m_oneShots.remove(notifier); 384 385 if (!hasListeners()) 386 stopUpdating(); 387 } 388 389 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options) 390 { 391 if (!m_positionCache->cachedPosition()) 392 return false; 393 if (!options->hasMaximumAge()) 394 return true; 395 if (!options->maximumAge()) 396 return false; 397 DOMTimeStamp currentTimeMillis = currentTime() * 1000.0; 398 return m_positionCache->cachedPosition()->timestamp() > currentTimeMillis - options->maximumAge(); 399 } 400 401 void Geolocation::clearWatch(int watchId) 402 { 403 m_watchers.remove(watchId); 404 405 if (!hasListeners()) 406 stopUpdating(); 407 } 408 409 void Geolocation::suspend() 410 { 411 #if !ENABLE(CLIENT_BASED_GEOLOCATION) 412 if (hasListeners()) 413 m_service->suspend(); 414 #endif 415 } 416 417 void Geolocation::resume() 418 { 419 #if !ENABLE(CLIENT_BASED_GEOLOCATION) 420 if (hasListeners()) 421 m_service->resume(); 422 #endif 423 } 424 425 void Geolocation::setIsAllowed(bool allowed) 426 { 427 // This may be due to either a new position from the service, or a cached 428 // position. 429 m_allowGeolocation = allowed ? Yes : No; 430 431 #if ENABLE(CLIENT_BASED_GEOLOCATION) 432 if (m_startRequestPermissionNotifier) { 433 if (isAllowed()) { 434 // Permission request was made during the startUpdating process 435 m_startRequestPermissionNotifier->startTimerIfNeeded(); 436 m_startRequestPermissionNotifier = 0; 437 if (!m_frame) 438 return; 439 Page* page = m_frame->page(); 440 if (!page) 441 return; 442 page->geolocationController()->addObserver(this); 443 } else { 444 m_startRequestPermissionNotifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); 445 m_oneShots.add(m_startRequestPermissionNotifier); 446 m_startRequestPermissionNotifier = 0; 447 } 448 return; 449 } 450 #endif 451 452 if (!isAllowed()) { 453 RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); 454 error->setIsFatal(true); 455 handleError(error.get()); 456 m_requestsAwaitingCachedPosition.clear(); 457 return; 458 } 459 460 // If the service has a last position, use it to call back for all requests. 461 // If any of the requests are waiting for permission for a cached position, 462 // the position from the service will be at least as fresh. 463 if (lastPosition()) 464 makeSuccessCallbacks(); 465 else 466 makeCachedPositionCallbacks(); 467 } 468 469 void Geolocation::sendError(Vector<RefPtr<GeoNotifier> >& notifiers, PositionError* error) 470 { 471 Vector<RefPtr<GeoNotifier> >::const_iterator end = notifiers.end(); 472 for (Vector<RefPtr<GeoNotifier> >::const_iterator it = notifiers.begin(); it != end; ++it) { 473 RefPtr<GeoNotifier> notifier = *it; 474 475 if (notifier->m_errorCallback) 476 notifier->m_errorCallback->handleEvent(error); 477 } 478 } 479 480 void Geolocation::sendPosition(Vector<RefPtr<GeoNotifier> >& notifiers, Geoposition* position) 481 { 482 Vector<RefPtr<GeoNotifier> >::const_iterator end = notifiers.end(); 483 for (Vector<RefPtr<GeoNotifier> >::const_iterator it = notifiers.begin(); it != end; ++it) { 484 RefPtr<GeoNotifier> notifier = *it; 485 ASSERT(notifier->m_successCallback); 486 487 notifier->m_successCallback->handleEvent(position); 488 } 489 } 490 491 void Geolocation::stopTimer(Vector<RefPtr<GeoNotifier> >& notifiers) 492 { 493 Vector<RefPtr<GeoNotifier> >::const_iterator end = notifiers.end(); 494 for (Vector<RefPtr<GeoNotifier> >::const_iterator it = notifiers.begin(); it != end; ++it) { 495 RefPtr<GeoNotifier> notifier = *it; 496 notifier->m_timer.stop(); 497 } 498 } 499 500 void Geolocation::stopTimersForOneShots() 501 { 502 Vector<RefPtr<GeoNotifier> > copy; 503 copyToVector(m_oneShots, copy); 504 505 stopTimer(copy); 506 } 507 508 void Geolocation::stopTimersForWatchers() 509 { 510 Vector<RefPtr<GeoNotifier> > copy; 511 m_watchers.getNotifiersVector(copy); 512 513 stopTimer(copy); 514 } 515 516 void Geolocation::stopTimers() 517 { 518 stopTimersForOneShots(); 519 stopTimersForWatchers(); 520 } 521 522 void Geolocation::handleError(PositionError* error) 523 { 524 ASSERT(error); 525 526 Vector<RefPtr<GeoNotifier> > oneShotsCopy; 527 copyToVector(m_oneShots, oneShotsCopy); 528 529 Vector<RefPtr<GeoNotifier> > watchersCopy; 530 m_watchers.getNotifiersVector(watchersCopy); 531 532 // Clear the lists before we make the callbacks, to avoid clearing notifiers 533 // added by calls to Geolocation methods from the callbacks, and to prevent 534 // further callbacks to these notifiers. 535 m_oneShots.clear(); 536 if (error->isFatal()) 537 m_watchers.clear(); 538 539 sendError(oneShotsCopy, error); 540 sendError(watchersCopy, error); 541 542 if (!hasListeners()) 543 stopUpdating(); 544 } 545 546 void Geolocation::requestPermission() 547 { 548 if (m_allowGeolocation > Unknown) 549 return; 550 551 if (!m_frame) 552 return; 553 554 Page* page = m_frame->page(); 555 if (!page) 556 return; 557 558 m_allowGeolocation = InProgress; 559 560 // Ask the chrome: it maintains the geolocation challenge policy itself. 561 page->chrome()->requestGeolocationPermissionForFrame(m_frame, this); 562 } 563 564 void Geolocation::positionChanged(PassRefPtr<Geoposition> newPosition) 565 { 566 m_currentPosition = newPosition; 567 568 m_positionCache->setCachedPosition(m_currentPosition.get()); 569 570 // Stop all currently running timers. 571 stopTimers(); 572 573 if (!isAllowed()) { 574 // requestPermission() will ask the chrome for permission. This may be 575 // implemented synchronously or asynchronously. In both cases, 576 // makeSuccessCallbacks() will be called if permission is granted, so 577 // there's nothing more to do here. 578 requestPermission(); 579 return; 580 } 581 582 makeSuccessCallbacks(); 583 } 584 585 void Geolocation::makeSuccessCallbacks() 586 { 587 ASSERT(m_currentPosition); 588 ASSERT(isAllowed()); 589 590 Vector<RefPtr<GeoNotifier> > oneShotsCopy; 591 copyToVector(m_oneShots, oneShotsCopy); 592 593 Vector<RefPtr<GeoNotifier> > watchersCopy; 594 m_watchers.getNotifiersVector(watchersCopy); 595 596 // Clear the lists before we make the callbacks, to avoid clearing notifiers 597 // added by calls to Geolocation methods from the callbacks, and to prevent 598 // further callbacks to these notifiers. 599 m_oneShots.clear(); 600 601 sendPosition(oneShotsCopy, m_currentPosition.get()); 602 sendPosition(watchersCopy, m_currentPosition.get()); 603 604 if (!hasListeners()) 605 stopUpdating(); 606 } 607 608 #if ENABLE(CLIENT_BASED_GEOLOCATION) 609 610 void Geolocation::setPosition(GeolocationPosition* position) 611 { 612 positionChanged(createGeoposition(position)); 613 } 614 615 void Geolocation::setError(GeolocationError* error) 616 { 617 RefPtr<PositionError> positionError = createPositionError(error); 618 handleError(positionError.get()); 619 } 620 621 #else 622 623 void Geolocation::geolocationServicePositionChanged(GeolocationService* service) 624 { 625 ASSERT_UNUSED(service, service == m_service); 626 ASSERT(m_service->lastPosition()); 627 628 positionChanged(m_service->lastPosition()); 629 } 630 631 void Geolocation::geolocationServiceErrorOccurred(GeolocationService* service) 632 { 633 ASSERT(service->lastError()); 634 635 // Note that we do not stop timers here. For one-shots, the request is 636 // cleared in handleError. For watchers, the spec requires that the timer is 637 // not cleared. 638 handleError(service->lastError()); 639 } 640 641 #endif 642 643 bool Geolocation::startUpdating(GeoNotifier* notifier) 644 { 645 #if ENABLE(CLIENT_BASED_GEOLOCATION) 646 // FIXME: Pass options to client. 647 648 if (!isAllowed()) { 649 m_startRequestPermissionNotifier = notifier; 650 requestPermission(); 651 return true; 652 } 653 654 if (!m_frame) 655 return false; 656 657 Page* page = m_frame->page(); 658 if (!page) 659 return false; 660 661 page->geolocationController()->addObserver(this); 662 return true; 663 #else 664 #if PLATFORM(ANDROID) 665 // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082 666 // Note that the correct fix is to use a 'paused' flag in WebCore, rather 667 // than calling into PlatformBridge. 668 if (!m_frame) 669 return false; 670 FrameView* view = m_frame->view(); 671 if (!view) 672 return false; 673 return m_service->startUpdating(notifier->m_options.get(), PlatformBridge::isWebViewPaused(view)); 674 #else 675 return m_service->startUpdating(notifier->m_options.get()); 676 #endif 677 #endif 678 } 679 680 void Geolocation::stopUpdating() 681 { 682 #if ENABLE(CLIENT_BASED_GEOLOCATION) 683 if (!m_frame) 684 return; 685 686 Page* page = m_frame->page(); 687 if (!page) 688 return; 689 690 page->geolocationController()->removeObserver(this); 691 #else 692 m_service->stopUpdating(); 693 #endif 694 695 } 696 697 // ANDROID 698 bool Geolocation::operator==(const EventListener& listener) 699 { 700 if (listener.type() != GeolocationEventListenerType) 701 return false; 702 const Geolocation* geolocation = static_cast<const Geolocation*>(&listener); 703 return m_frame == geolocation->m_frame; 704 } 705 706 void Geolocation::handleEvent(ScriptExecutionContext*, Event* event) 707 { 708 ASSERT_UNUSED(event, event->type() == eventNames().unloadEvent); 709 // Cancel any ongoing requests on page unload. This is required to release 710 // references to JS callbacks in the page, to allow the frame to be cleaned up 711 // by WebKit. 712 m_oneShots.clear(); 713 m_watchers.clear(); 714 } 715 // END ANDROID 716 717 } // namespace WebCore 718