| Index: ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
|
| diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b0a42beb2934208331dcd6a64cea9da8568726bd
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
|
| @@ -0,0 +1,586 @@
|
| +// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
|
| +
|
| +#import <CoreLocation/CoreLocation.h>
|
| +#import <UIKit/UIKit.h>
|
| +
|
| +#include <string>
|
| +
|
| +#import "base/ios/weak_nsobject.h"
|
| +#include "base/logging.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/metrics/histogram.h"
|
| +#include "base/version.h"
|
| +#include "components/google/core/browser/google_util.h"
|
| +#include "components/version_info/version_info.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#import "ios/chrome/browser/geolocation/CLLocation+OmniboxGeolocation.h"
|
| +#import "ios/chrome/browser/geolocation/CLLocation+XGeoHeader.h"
|
| +#import "ios/chrome/browser/geolocation/location_manager.h"
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_authorization_alert.h"
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller+Testing.h"
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_local_state.h"
|
| +#import "ios/chrome/browser/tabs/tab.h"
|
| +#include "ios/web/public/navigation_item.h"
|
| +#import "ios/web/public/navigation_manager.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace {
|
| +
|
| +// Values for the histogram that records whether we sent the X-Geo header for
|
| +// an Omnibox query or why we did not do so. These match the definition of
|
| +// GeolocationHeaderSentOrNot in Chromium
|
| +// src-internal/tools/histograms/histograms.xml.
|
| +typedef enum {
|
| + // The user disabled location for Google.com (not used by Chrome iOS).
|
| + kHeaderStateNotSentAuthorizationGoogleDenied = 0,
|
| + // The user has not yet determined Chrome's access to the current device
|
| + // location or Chrome's use of geolocation for Omnibox queries.
|
| + kHeaderStateNotSentAuthorizationNotDetermined,
|
| + // The current device location is not available.
|
| + kHeaderStateNotSentLocationNotAvailable,
|
| + // The current device location is stale.
|
| + kHeaderStateNotSentLocationStale,
|
| + // The X-Geo header was sent.
|
| + kHeaderStateSent,
|
| + // The user denied Chrome from accessing the current device location.
|
| + kHeaderStateNotSentAuthorizationChromeDenied,
|
| + // The user denied Chrome from using geolocation for Omnibox queries.
|
| + kHeaderStateNotSentAuthorizationOmniboxDenied,
|
| + // The user's Google search domain is not whitelisted.
|
| + kHeaderStateNotSentDomainNotWhitelisted,
|
| + // The number of possible of HeaderState values to report.
|
| + kHeaderStateCount,
|
| +} HeaderState;
|
| +
|
| +// Values for the histograms that record the user's action when prompted to
|
| +// authorize the use of location by Chrome. These match the definition of
|
| +// GeolocationAuthorizationAction in Chromium
|
| +// src-internal/tools/histograms/histograms.xml.
|
| +typedef enum {
|
| + // The user authorized use of location.
|
| + kAuthorizationActionAuthorized = 0,
|
| + // The user permanently denied use of location (Don't Allow).
|
| + kAuthorizationActionPermanentlyDenied,
|
| + // The user denied use of location at this prompt (Not Now).
|
| + kAuthorizationActionDenied,
|
| + // The number of possible AuthorizationAction values to report.
|
| + kAuthorizationActionCount,
|
| +} AuthorizationAction;
|
| +
|
| +// Name of the histogram recording HeaderState.
|
| +const char* const kGeolocationHeaderSentOrNotHistogram =
|
| + "Geolocation.HeaderSentOrNot";
|
| +
|
| +// Name of the histogram recording location acquisition time.
|
| +const char* const kOmniboxQueryGeolocationAcquisitionTimeHistogram =
|
| + "Omnibox.QueryGeolocationAcquisitionTime";
|
| +
|
| +// Name of the histogram recording estimated location accuracy.
|
| +const char* const kOmniboxQueryGeolocationHorizontalAccuracyHistogram =
|
| + "Omnibox.QueryGeolocationHorizontalAccuracy";
|
| +
|
| +// Name of the histogram recording AuthorizationAction for an existing user.
|
| +const char* const kGeolocationAuthorizationActionExistingUser =
|
| + "Geolocation.AuthorizationActionExistingUser";
|
| +
|
| +// Name of the histogram recording AuthorizationAction for a new user.
|
| +const char* const kGeolocationAuthorizationActionNewUser =
|
| + "Geolocation.AuthorizationActionNewUser";
|
| +
|
| +} // anonymous namespace
|
| +
|
| +@interface OmniboxGeolocationController ()<
|
| + LocationManagerDelegate,
|
| + OmniboxGeolocationAuthorizationAlertDelegate> {
|
| + base::scoped_nsobject<OmniboxGeolocationLocalState> localState_;
|
| + base::scoped_nsobject<LocationManager> locationManager_;
|
| + base::scoped_nsobject<OmniboxGeolocationAuthorizationAlert>
|
| + authorizationAlert_;
|
| + base::WeakNSObject<Tab> weakTabToReload_;
|
| +
|
| + // Records whether we have deliberately presented the system prompt, so that
|
| + // we can record the user's action in
|
| + // locationManagerDidChangeAuthorizationStatus:.
|
| + BOOL systemPrompt_;
|
| +
|
| + // Records whether we are prompting for a new user, so that we can record the
|
| + // user's action to the right histogram (either
|
| + // kGeolocationAuthorizationActionExistingUser or
|
| + // kGeolocationAuthorizationActionNewUser).
|
| + BOOL newUser_;
|
| +}
|
| +
|
| +// Boolean value indicating whether geolocation is enabled for Omnibox queries.
|
| +@property(nonatomic, readonly) BOOL enabled;
|
| +
|
| +// Convenience property lazily initializes |localState_|.
|
| +@property(nonatomic, readonly) OmniboxGeolocationLocalState* localState;
|
| +
|
| +// Convenience property lazily initializes |locationManager_|.
|
| +@property(nonatomic, readonly) LocationManager* locationManager;
|
| +
|
| +// Returns YES if and only if |url| and |transition| specify an Omnibox query
|
| +// that is eligible for geolocation.
|
| +- (BOOL)URLIsEligibleQueryURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition;
|
| +
|
| +// Returns YES if and only if |url| and |transition| specify an Omnibox query.
|
| +//
|
| +// Note: URLIsQueryURL:transition: is more liberal than
|
| +// URLIsEligibleQueryURL:transition:. Use URLIsEligibleQueryURL:transition: and
|
| +// not URLIsQueryURL:transition: to test Omnibox query URLs with respect to
|
| +// sending location to Google.
|
| +- (BOOL)URLIsQueryURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition;
|
| +
|
| +// Returns YES if and only if |url| specifies a page for which we will prompt
|
| +// the user to authorize the use of geolocation for Omnibox queries.
|
| +- (BOOL)URLIsAuthorizationPromptingURL:(const GURL&)url;
|
| +
|
| +// Starts updating device location if needed.
|
| +- (void)startUpdatingLocation;
|
| +// Stops updating device location.
|
| +- (void)stopUpdatingLocation;
|
| +// If the current location is not stale, then adds the current location to the
|
| +// current session entry for |tab| and reloads |tab|. If the current location
|
| +// is stale, then does nothing.
|
| +- (void)addLocationAndReloadTab:(Tab*)tab;
|
| +// Returns YES if and only if we should show an alert that prompts the user to
|
| +// authorize using geolocation for Omnibox queries.
|
| +- (BOOL)shouldShowAuthorizationAlert;
|
| +// Shows an alert that prompts the user to authorize using geolocation for
|
| +// Omnibox queries. Sets |weakTabToReload_| from |tab|, so that we can reload
|
| +// |tab| if the user authorizes using geolocation.
|
| +- (void)showAuthorizationAlertForTab:(Tab*)tab;
|
| +// Records |headerState| for the |kGeolocationHeaderSentOrNotHistogram|
|
| +// histogram.
|
| +- (void)recordHeaderState:(HeaderState)headerState;
|
| +// Records |authorizationAction|.
|
| +- (void)recordAuthorizationAction:(AuthorizationAction)authorizationAction;
|
| +
|
| +@end
|
| +
|
| +@implementation OmniboxGeolocationController
|
| +
|
| ++ (OmniboxGeolocationController*)sharedInstance {
|
| + static OmniboxGeolocationController* instance =
|
| + [[OmniboxGeolocationController alloc] init];
|
| + return instance;
|
| +}
|
| +
|
| +- (void)triggerSystemPromptForNewUser:(BOOL)newUser {
|
| + if (self.locationManager.locationServicesEnabled &&
|
| + self.locationManager.authorizationStatus ==
|
| + kCLAuthorizationStatusNotDetermined) {
|
| + // Set |systemPrompt_|, so that
|
| + // locationManagerDidChangeAuthorizationStatus: will know to handle any
|
| + // CLAuthorizationStatus changes.
|
| + //
|
| + // TODO(crbug.com/661996): Remove the now useless
|
| + // kAuthorizationStateNotDeterminedSystemPrompt from
|
| + // omnibox_geolocation_local_state.h.
|
| + systemPrompt_ = YES;
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateNotDeterminedSystemPrompt;
|
| +
|
| + // Turn on location updates, so that iOS will prompt the user.
|
| + [self startUpdatingLocation];
|
| +
|
| + weakTabToReload_.reset();
|
| + newUser_ = newUser;
|
| + }
|
| +}
|
| +
|
| +- (void)locationBarDidBecomeFirstResponder:
|
| + (ios::ChromeBrowserState*)browserState {
|
| + if (self.enabled && browserState && !browserState->IsOffTheRecord()) {
|
| + [self startUpdatingLocation];
|
| + }
|
| +}
|
| +
|
| +- (void)locationBarDidResignFirstResponder:
|
| + (ios::ChromeBrowserState*)browserState {
|
| + // It's always okay to stop updating location.
|
| + [self stopUpdatingLocation];
|
| +}
|
| +
|
| +- (void)locationBarDidSubmitURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + // Stop updating the location when the user submits a query from the Omnibox.
|
| + // We're not interested in further updates until the next time the user puts
|
| + // the focus on the Omnbox.
|
| + [self stopUpdatingLocation];
|
| +}
|
| +
|
| +- (BOOL)addLocationToNavigationItem:(web::NavigationItem*)item
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + // If this is incognito mode or is not an Omnibox query, then do nothing.
|
| + //
|
| + // Check the URL with URLIsQueryURL:transition: here and not
|
| + // URLIsEligibleQueryURL:transition:, because we want to log the cases where
|
| + // we did not send the X-Geo header due to the Google search domain not being
|
| + // whitelisted.
|
| + DCHECK(item);
|
| + const GURL& url = item->GetURL();
|
| + if (!browserState || browserState->IsOffTheRecord() ||
|
| + ![self URLIsQueryURL:url transition:item->GetTransitionType()]) {
|
| + return NO;
|
| + }
|
| +
|
| + if (![[OmniboxGeolocationConfig sharedInstance] URLHasEligibleDomain:url]) {
|
| + [self recordHeaderState:kHeaderStateNotSentDomainNotWhitelisted];
|
| + return NO;
|
| + }
|
| +
|
| + // At this point, we should only have Omnibox query URLs that are eligible
|
| + // for geolocation.
|
| + DCHECK([self URLIsEligibleQueryURL:url transition:item->GetTransitionType()]);
|
| +
|
| + HeaderState headerState;
|
| + if (!self.locationManager.locationServicesEnabled) {
|
| + headerState = kHeaderStateNotSentAuthorizationChromeDenied;
|
| + } else {
|
| + switch (self.localState.authorizationState) {
|
| + case geolocation::kAuthorizationStateNotDeterminedWaiting:
|
| + case geolocation::kAuthorizationStateNotDeterminedSystemPrompt:
|
| + if (self.locationManager.authorizationStatus ==
|
| + kCLAuthorizationStatusNotDetermined ||
|
| + [self shouldShowAuthorizationAlert]) {
|
| + headerState = kHeaderStateNotSentAuthorizationNotDetermined;
|
| + } else {
|
| + DCHECK(self.locationManager.authorizationStatus ==
|
| + kCLAuthorizationStatusAuthorizedAlways ||
|
| + self.locationManager.authorizationStatus ==
|
| + kCLAuthorizationStatusAuthorizedWhenInUse);
|
| + headerState = kHeaderStateNotSentAuthorizationOmniboxDenied;
|
| + }
|
| + break;
|
| +
|
| + case geolocation::kAuthorizationStateDenied:
|
| + switch (self.locationManager.authorizationStatus) {
|
| + case kCLAuthorizationStatusNotDetermined:
|
| + NOTREACHED();
|
| + // To keep the compiler quiet about headerState not being
|
| + // initialized in this switch case.
|
| + headerState = kHeaderStateNotSentAuthorizationChromeDenied;
|
| + break;
|
| + case kCLAuthorizationStatusRestricted:
|
| + case kCLAuthorizationStatusDenied:
|
| + headerState = kHeaderStateNotSentAuthorizationChromeDenied;
|
| + break;
|
| + case kCLAuthorizationStatusAuthorizedAlways:
|
| + case kCLAuthorizationStatusAuthorizedWhenInUse:
|
| + headerState = kHeaderStateNotSentAuthorizationOmniboxDenied;
|
| + break;
|
| + }
|
| + break;
|
| +
|
| + case geolocation::kAuthorizationStateAuthorized: {
|
| + DCHECK(self.enabled);
|
| + CLLocation* currentLocation = [self.locationManager currentLocation];
|
| + if (!currentLocation) {
|
| + headerState = kHeaderStateNotSentLocationNotAvailable;
|
| + } else if (![currentLocation cr_isFreshEnough]) {
|
| + headerState = kHeaderStateNotSentLocationStale;
|
| + } else {
|
| + NSDictionary* locationHTTPHeaders =
|
| + @{ @"X-Geo" : [currentLocation cr_xGeoString] };
|
| + item->AddHttpRequestHeaders(locationHTTPHeaders);
|
| + headerState = kHeaderStateSent;
|
| +
|
| + NSTimeInterval acquisitionInterval =
|
| + currentLocation.cr_acquisitionInterval;
|
| + base::TimeDelta acquisitionTime = base::TimeDelta::FromMilliseconds(
|
| + acquisitionInterval * base::Time::kMillisecondsPerSecond);
|
| + UMA_HISTOGRAM_TIMES(kOmniboxQueryGeolocationAcquisitionTimeHistogram,
|
| + acquisitionTime);
|
| +
|
| + double horizontalAccuracy = currentLocation.horizontalAccuracy;
|
| + UMA_HISTOGRAM_COUNTS_10000(
|
| + kOmniboxQueryGeolocationHorizontalAccuracyHistogram,
|
| + horizontalAccuracy);
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + [self recordHeaderState:headerState];
|
| + return headerState == kHeaderStateSent;
|
| +}
|
| +
|
| +- (void)finishPageLoadForTab:(Tab*)tab loadSuccess:(BOOL)loadSuccess {
|
| + if (tab.isPrerenderTab || !loadSuccess || !tab.browserState ||
|
| + tab.browserState->IsOffTheRecord()) {
|
| + return;
|
| + }
|
| +
|
| + DCHECK(tab.webState->GetNavigationManager());
|
| + web::NavigationItem* item =
|
| + tab.webState->GetNavigationManager()->GetVisibleItem();
|
| + if (![self URLIsAuthorizationPromptingURL:item->GetURL()] ||
|
| + !self.locationManager.locationServicesEnabled) {
|
| + return;
|
| + }
|
| +
|
| + switch (self.locationManager.authorizationStatus) {
|
| + case kCLAuthorizationStatusNotDetermined:
|
| + // Prompt the user with the iOS system location authorization alert.
|
| + //
|
| + // Set |systemPrompt_|, so that
|
| + // locationManagerDidChangeAuthorizationStatus: will know that any
|
| + // CLAuthorizationStatus changes are coming from this specific prompt.
|
| + systemPrompt_ = YES;
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateNotDeterminedSystemPrompt;
|
| + [self startUpdatingLocation];
|
| +
|
| + // Save this tab in case we're able to transition to
|
| + // kAuthorizationStateAuthorized.
|
| + weakTabToReload_.reset(tab);
|
| + break;
|
| +
|
| + case kCLAuthorizationStatusRestricted:
|
| + case kCLAuthorizationStatusDenied:
|
| + break;
|
| +
|
| + case kCLAuthorizationStatusAuthorizedAlways:
|
| + case kCLAuthorizationStatusAuthorizedWhenInUse:
|
| + // We might be in state kAuthorizationStateNotDeterminedSystemPrompt here
|
| + // if we presented the iOS system location alert when
|
| + // [CLLocationManager authorizationStatus] was
|
| + // kCLAuthorizationStatusNotDetermined but the user managed to authorize
|
| + // the app through some other flow; this might happen if the user
|
| + // backgrounded the app or the app crashed. If so, then reset the state.
|
| + if (self.localState.authorizationState ==
|
| + geolocation::kAuthorizationStateNotDeterminedSystemPrompt) {
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateNotDeterminedWaiting;
|
| + }
|
| + // If the user has authorized the app to use location but not yet
|
| + // explicitly authorized or denied using geolocation for Omnibox queries,
|
| + // then present an alert.
|
| + if (self.localState.authorizationState ==
|
| + geolocation::kAuthorizationStateNotDeterminedWaiting &&
|
| + [self shouldShowAuthorizationAlert]) {
|
| + [self showAuthorizationAlertForTab:tab];
|
| + }
|
| + break;
|
| + }
|
| +}
|
| +
|
| +#pragma mark - Private
|
| +
|
| +- (BOOL)enabled {
|
| + return self.locationManager.locationServicesEnabled &&
|
| + self.localState.authorizationState ==
|
| + geolocation::kAuthorizationStateAuthorized;
|
| +}
|
| +
|
| +- (OmniboxGeolocationLocalState*)localState {
|
| + if (!localState_) {
|
| + localState_.reset([[OmniboxGeolocationLocalState alloc]
|
| + initWithLocationManager:self.locationManager]);
|
| + }
|
| + return localState_;
|
| +}
|
| +
|
| +- (LocationManager*)locationManager {
|
| + if (!locationManager_) {
|
| + locationManager_.reset([[LocationManager alloc] init]);
|
| + [locationManager_ setDelegate:self];
|
| + }
|
| + return locationManager_;
|
| +}
|
| +
|
| +- (BOOL)URLIsEligibleQueryURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition {
|
| + return [self URLIsQueryURL:url transition:transition] &&
|
| + [[OmniboxGeolocationConfig sharedInstance] URLHasEligibleDomain:url];
|
| +}
|
| +
|
| +- (BOOL)URLIsQueryURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition {
|
| + if (google_util::IsGoogleSearchUrl(url) &&
|
| + (transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0) {
|
| + ui::PageTransition coreTransition = static_cast<ui::PageTransition>(
|
| + transition & ui::PAGE_TRANSITION_CORE_MASK);
|
| + if (PageTransitionCoreTypeIs(coreTransition,
|
| + ui::PAGE_TRANSITION_GENERATED) ||
|
| + PageTransitionCoreTypeIs(coreTransition, ui::PAGE_TRANSITION_RELOAD)) {
|
| + return YES;
|
| + }
|
| + }
|
| + return NO;
|
| +}
|
| +
|
| +- (BOOL)URLIsAuthorizationPromptingURL:(const GURL&)url {
|
| + // Per PRD: "Show a modal dialog upon reaching google.com or a search results
|
| + // page..." However, we only want to do this for domains where we will send
|
| + // location.
|
| + return (google_util::IsGoogleHomePageUrl(url) ||
|
| + google_util::IsGoogleSearchUrl(url)) &&
|
| + [[OmniboxGeolocationConfig sharedInstance] URLHasEligibleDomain:url];
|
| +}
|
| +
|
| +- (void)startUpdatingLocation {
|
| + // Note that GeolocationUpdater will stop itself automatically after 5
|
| + // seconds.
|
| + [self.locationManager startUpdatingLocation];
|
| +}
|
| +
|
| +- (void)stopUpdatingLocation {
|
| + // Note that we don't need to initialize |locationManager_| here. If it's
|
| + // nil, then it's not running.
|
| + [locationManager_ stopUpdatingLocation];
|
| +}
|
| +
|
| +- (void)addLocationAndReloadTab:(Tab*)tab {
|
| + if (self.enabled && [tab navigationManager]) {
|
| + // Make sure that GeolocationUpdater is running the first time we request
|
| + // the current location.
|
| + //
|
| + // If GeolocationUpdater is not running, then it returns nil for the
|
| + // current location. That's normally okay, because we cache the most recent
|
| + // location in LocationManager. However, we arrive here when the user first
|
| + // authorizes us to use location, so we may not have ever started
|
| + // GeolocationUpdater.
|
| + [self startUpdatingLocation];
|
| +
|
| + web::NavigationItem* item =
|
| + tab.webState->GetNavigationManager()->GetVisibleItem();
|
| + if ([self addLocationToNavigationItem:item browserState:tab.browserState]) {
|
| + [tab reload];
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (BOOL)shouldShowAuthorizationAlert {
|
| + base::Version previousVersion(self.localState.lastAuthorizationAlertVersion);
|
| + if (!previousVersion.IsValid())
|
| + return YES;
|
| +
|
| + base::Version currentVersion(version_info::GetVersionNumber());
|
| + DCHECK(currentVersion.IsValid());
|
| + return currentVersion.components()[0] != previousVersion.components()[0];
|
| +}
|
| +
|
| +- (void)showAuthorizationAlertForTab:(Tab*)tab {
|
| + // Save this tab in case we're able to transition to
|
| + // kAuthorizationStateAuthorized.
|
| + weakTabToReload_.reset(tab);
|
| +
|
| + authorizationAlert_.reset(
|
| + [[OmniboxGeolocationAuthorizationAlert alloc] initWithDelegate:self]);
|
| + [authorizationAlert_ showAuthorizationAlert];
|
| +
|
| + self.localState.lastAuthorizationAlertVersion =
|
| + version_info::GetVersionNumber();
|
| +}
|
| +
|
| +- (void)recordHeaderState:(HeaderState)headerState {
|
| + UMA_HISTOGRAM_ENUMERATION(kGeolocationHeaderSentOrNotHistogram, headerState,
|
| + kHeaderStateCount);
|
| +}
|
| +
|
| +- (void)recordAuthorizationAction:(AuthorizationAction)authorizationAction {
|
| + if (newUser_) {
|
| + newUser_ = NO;
|
| +
|
| + UMA_HISTOGRAM_ENUMERATION(kGeolocationAuthorizationActionNewUser,
|
| + authorizationAction, kAuthorizationActionCount);
|
| + } else {
|
| + UMA_HISTOGRAM_ENUMERATION(kGeolocationAuthorizationActionExistingUser,
|
| + authorizationAction, kAuthorizationActionCount);
|
| + }
|
| +}
|
| +
|
| +#pragma mark - LocationManagerDelegate
|
| +
|
| +- (void)locationManagerDidChangeAuthorizationStatus:
|
| + (LocationManager*)locationManager {
|
| + if (systemPrompt_) {
|
| + switch (self.locationManager.authorizationStatus) {
|
| + case kCLAuthorizationStatusNotDetermined:
|
| + // We may get a spurious notification about a transition to
|
| + // |kCLAuthorizationStatusNotDetermined| when we first start location
|
| + // services. Ignore it and don't reset |systemPrompt_| until we get a
|
| + // real change.
|
| + break;
|
| +
|
| + case kCLAuthorizationStatusRestricted:
|
| + case kCLAuthorizationStatusDenied:
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateDenied;
|
| + systemPrompt_ = NO;
|
| +
|
| + [self recordAuthorizationAction:kAuthorizationActionPermanentlyDenied];
|
| + break;
|
| +
|
| + case kCLAuthorizationStatusAuthorizedAlways:
|
| + case kCLAuthorizationStatusAuthorizedWhenInUse:
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateAuthorized;
|
| + systemPrompt_ = NO;
|
| +
|
| + base::scoped_nsobject<Tab> tab([weakTabToReload_ retain]);
|
| + [self addLocationAndReloadTab:tab];
|
| + weakTabToReload_.reset();
|
| +
|
| + [self recordAuthorizationAction:kAuthorizationActionAuthorized];
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +#pragma mark - OmniboxGeolocationAuthorizationAlertDelegate
|
| +
|
| +- (void)authorizationAlertDidAuthorize:
|
| + (OmniboxGeolocationAuthorizationAlert*)authorizationAlert {
|
| + self.localState.authorizationState =
|
| + geolocation::kAuthorizationStateAuthorized;
|
| +
|
| + base::scoped_nsobject<Tab> tab([weakTabToReload_ retain]);
|
| + [self addLocationAndReloadTab:tab];
|
| +
|
| + // Just resetting |authorizationAlert_| leads to a user-after-free crash
|
| + // presumably due to a UIKit bug. Making authorizationAlert_ autorelease
|
| + // will keep it alive long enough to avoid the crash. See crbug.com/381235
|
| + authorizationAlert_.autorelease();
|
| + weakTabToReload_.reset();
|
| +
|
| + [self recordAuthorizationAction:kAuthorizationActionAuthorized];
|
| +}
|
| +
|
| +- (void)authorizationAlertDidCancel:
|
| + (OmniboxGeolocationAuthorizationAlert*)authorizationAlert {
|
| + // Leave authorization state as undetermined (not kAuthorizationStateDenied).
|
| + // We won't use location, but we'll still be able to prompt at the next
|
| + // application update.
|
| +
|
| + // Just resetting |authorizationAlert_| leads to a user-after-free crash
|
| + // presumably due to a UIKit bug. Making authorizationAlert_ autorelease
|
| + // will keep it alive long enough to avoid the crash. See crbug.com/381235
|
| + authorizationAlert_.autorelease();
|
| + weakTabToReload_.reset();
|
| +
|
| + [self recordAuthorizationAction:kAuthorizationActionDenied];
|
| +}
|
| +
|
| +#pragma mark - OmniboxGeolocationController+Testing
|
| +
|
| +- (void)setLocalState:(OmniboxGeolocationLocalState*)localState {
|
| + localState_.reset([localState retain]);
|
| +}
|
| +
|
| +- (void)setLocationManager:(LocationManager*)locationManager {
|
| + locationManager_.reset([locationManager retain]);
|
| +}
|
| +
|
| +@end
|
|
|