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 |