1// Copyright 2018 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.h" 6 7#import <CoreLocation/CoreLocation.h> 8 9#include <memory> 10 11#include "base/mac/foundation_util.h" 12#include "base/metrics/histogram_macros.h" 13#include "base/strings/sys_string_conversions.h" 14#include "ios/chrome/browser/browser_state/chrome_browser_state.h" 15#import "ios/chrome/browser/main/browser.h" 16#import "ios/chrome/browser/ui/commands/command_dispatcher.h" 17#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h" 18#import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h" 19#import "ios/chrome/browser/ui/fullscreen/fullscreen_ui_updater.h" 20#import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h" 21#import "ios/chrome/browser/ui/ntp/ntp_util.h" 22#import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h" 23#import "ios/chrome/browser/ui/orchestrator/omnibox_focus_orchestrator.h" 24#import "ios/chrome/browser/ui/toolbar/adaptive_toolbar_coordinator+subclassing.h" 25#import "ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.h" 26#import "ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller_delegate.h" 27#import "ios/chrome/browser/ui/toolbar/toolbar_coordinator_delegate.h" 28#import "ios/chrome/browser/ui/util/ui_util.h" 29#import "ios/chrome/browser/ui/util/uikit_ui_util.h" 30#import "ios/chrome/browser/web_state_list/web_state_list.h" 31#include "ios/web/public/navigation/referrer.h" 32 33#if !defined(__has_feature) || !__has_feature(objc_arc) 34#error "This file requires ARC support." 35#endif 36 37@interface PrimaryToolbarCoordinator () <PrimaryToolbarViewControllerDelegate> { 38 // Observer that updates |toolbarViewController| for fullscreen events. 39 std::unique_ptr<FullscreenUIUpdater> _fullscreenUIUpdater; 40} 41 42// Whether the coordinator is started. 43@property(nonatomic, assign, getter=isStarted) BOOL started; 44// Redefined as PrimaryToolbarViewController. 45@property(nonatomic, strong) PrimaryToolbarViewController* viewController; 46// The coordinator for the location bar in the toolbar. 47@property(nonatomic, strong) LocationBarCoordinator* locationBarCoordinator; 48// Orchestrator for the expansion animation. 49@property(nonatomic, strong) OmniboxFocusOrchestrator* orchestrator; 50// Whether the omnibox focusing should happen with animation. 51@property(nonatomic, assign) BOOL enableAnimationsForOmniboxFocus; 52 53@end 54 55@implementation PrimaryToolbarCoordinator 56 57@dynamic viewController; 58@synthesize popupPresenterDelegate = _popupPresenterDelegate; 59@synthesize delegate = _delegate; 60 61#pragma mark - ChromeCoordinator 62 63- (void)start { 64 DCHECK(self.browser); 65 if (self.started) 66 return; 67 68 self.enableAnimationsForOmniboxFocus = YES; 69 70 [self.browser->GetCommandDispatcher() 71 startDispatchingToTarget:self 72 forProtocol:@protocol(FakeboxFocuser)]; 73 74 self.viewController = [[PrimaryToolbarViewController alloc] init]; 75 self.viewController.shouldHideOmniboxOnNTP = 76 !self.browser->GetBrowserState()->IsOffTheRecord(); 77 self.viewController.buttonFactory = [self buttonFactoryWithType:PRIMARY]; 78 // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol 79 // clean up. 80 self.viewController.dispatcher = 81 static_cast<id<ApplicationCommands, BrowserCommands, OmniboxCommands>>( 82 self.browser->GetCommandDispatcher()); 83 self.viewController.delegate = self; 84 85 self.orchestrator = [[OmniboxFocusOrchestrator alloc] init]; 86 self.orchestrator.toolbarAnimatee = self.viewController; 87 88 [self setUpLocationBar]; 89 self.viewController.locationBarViewController = 90 self.locationBarCoordinator.locationBarViewController; 91 self.orchestrator.locationBarAnimatee = 92 [self.locationBarCoordinator locationBarAnimatee]; 93 94 self.orchestrator.editViewAnimatee = 95 [self.locationBarCoordinator editViewAnimatee]; 96 97 if (fullscreen::features::ShouldScopeFullscreenControllerToBrowser()) { 98 _fullscreenUIUpdater = std::make_unique<FullscreenUIUpdater>( 99 FullscreenController::FromBrowser(self.browser), self.viewController); 100 } else { 101 _fullscreenUIUpdater = std::make_unique<FullscreenUIUpdater>( 102 FullscreenController::FromBrowserState(self.browser->GetBrowserState()), 103 self.viewController); 104 } 105 106 [super start]; 107 self.started = YES; 108} 109 110- (void)stop { 111 if (!self.started) 112 return; 113 [super stop]; 114 [self.browser->GetCommandDispatcher() stopDispatchingToTarget:self]; 115 [self.locationBarCoordinator stop]; 116 _fullscreenUIUpdater = nullptr; 117 self.started = NO; 118} 119 120#pragma mark - Public 121 122- (id<ActivityServicePositioner>)activityServicePositioner { 123 return self.viewController; 124} 125 126- (void)showPrerenderingAnimation { 127 [self.viewController showPrerenderingAnimation]; 128} 129 130- (BOOL)isOmniboxFirstResponder { 131 return [self.locationBarCoordinator isOmniboxFirstResponder]; 132} 133 134- (BOOL)showingOmniboxPopup { 135 return [self.locationBarCoordinator showingOmniboxPopup]; 136} 137 138- (void)transitionToLocationBarFocusedState:(BOOL)focused { 139 if (self.viewController.traitCollection.verticalSizeClass == 140 UIUserInterfaceSizeClassUnspecified) { 141 return; 142 } 143 144 [self.orchestrator 145 transitionToStateOmniboxFocused:focused 146 toolbarExpanded:focused && !IsRegularXRegularSizeClass( 147 self.viewController) 148 animated:self.enableAnimationsForOmniboxFocus]; 149} 150 151- (id<ViewRevealingAnimatee>)animatee { 152 return self.viewController; 153} 154 155- (void)setPanGestureHandler: 156 (ViewRevealingVerticalPanHandler*)panGestureHandler { 157 self.viewController.panGestureHandler = panGestureHandler; 158} 159 160#pragma mark - PrimaryToolbarViewControllerDelegate 161 162- (void)viewControllerTraitCollectionDidChange: 163 (UITraitCollection*)previousTraitCollection { 164 BOOL omniboxFocused = self.isOmniboxFirstResponder || 165 [self.locationBarCoordinator showingOmniboxPopup]; 166 [self.orchestrator 167 transitionToStateOmniboxFocused:omniboxFocused 168 toolbarExpanded:omniboxFocused && 169 !IsRegularXRegularSizeClass( 170 self.viewController) 171 animated:NO]; 172} 173 174- (void)exitFullscreen { 175 if (fullscreen::features::ShouldScopeFullscreenControllerToBrowser()) { 176 FullscreenController::FromBrowser(self.browser)->ExitFullscreen(); 177 } else { 178 FullscreenController::FromBrowserState(self.browser->GetBrowserState()) 179 ->ExitFullscreen(); 180 } 181} 182 183#pragma mark - NewTabPageControllerDelegate 184 185- (UIResponder<UITextInput>*)fakeboxScribbleForwardingTarget { 186 return self.locationBarCoordinator.omniboxScribbleForwardingTarget; 187} 188 189#pragma mark - FakeboxFocuser 190 191- (void)focusOmniboxNoAnimation { 192 self.enableAnimationsForOmniboxFocus = NO; 193 [self fakeboxFocused]; 194 self.enableAnimationsForOmniboxFocus = YES; 195 // If the pasteboard is containing a URL, the omnibox popup suggestions are 196 // displayed as soon as the omnibox is focused. 197 // If the fake omnibox animation is triggered at the same time, it is possible 198 // to see the NTP going up where the real omnibox should be displayed. 199 if ([self.locationBarCoordinator omniboxPopupHasAutocompleteResults]) 200 [self onFakeboxAnimationComplete]; 201} 202 203- (void)fakeboxFocused { 204 [self.locationBarCoordinator focusOmniboxFromFakebox]; 205} 206 207- (void)onFakeboxBlur { 208 // Hide the toolbar if the NTP is currently displayed. 209 web::WebState* webState = 210 self.browser->GetWebStateList()->GetActiveWebState(); 211 if (webState && IsVisibleURLNewTabPage(webState)) { 212 self.viewController.view.hidden = IsSplitToolbarMode(self.viewController); 213 } 214} 215 216- (void)onFakeboxAnimationComplete { 217 self.viewController.view.hidden = NO; 218} 219 220#pragma mark - Protected override 221 222- (void)updateToolbarForSideSwipeSnapshot:(web::WebState*)webState { 223 [super updateToolbarForSideSwipeSnapshot:webState]; 224 225 BOOL isNTP = IsVisibleURLNewTabPage(webState); 226 227 // Don't do anything for a live non-ntp tab. 228 if (webState == self.browser->GetWebStateList()->GetActiveWebState() && 229 !isNTP) { 230 [self.locationBarCoordinator.locationBarViewController.view setHidden:NO]; 231 } else { 232 self.viewController.view.hidden = NO; 233 [self.locationBarCoordinator.locationBarViewController.view setHidden:YES]; 234 } 235} 236 237- (void)resetToolbarAfterSideSwipeSnapshot { 238 [super resetToolbarAfterSideSwipeSnapshot]; 239 [self.locationBarCoordinator.locationBarViewController.view setHidden:NO]; 240} 241 242#pragma mark - Private 243 244// Sets the location bar up. 245- (void)setUpLocationBar { 246 self.locationBarCoordinator = 247 [[LocationBarCoordinator alloc] initWithBaseViewController:nil 248 browser:self.browser]; 249 self.locationBarCoordinator.delegate = self.delegate; 250 self.locationBarCoordinator.popupPresenterDelegate = 251 self.popupPresenterDelegate; 252 [self.locationBarCoordinator start]; 253} 254 255@end 256