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