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