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/browser_view/browser_coordinator.h"
6
7#include <memory>
8
9#include "base/scoped_observer.h"
10#import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
11#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
12#import "ios/chrome/browser/autofill/autofill_tab_helper.h"
13#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
14#include "ios/chrome/browser/chrome_url_constants.h"
15#include "ios/chrome/browser/download/download_directory_util.h"
16#import "ios/chrome/browser/download/external_app_util.h"
17#import "ios/chrome/browser/download/pass_kit_tab_helper.h"
18#import "ios/chrome/browser/find_in_page/find_tab_helper.h"
19#import "ios/chrome/browser/main/browser.h"
20#import "ios/chrome/browser/store_kit/store_kit_coordinator.h"
21#import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
22#import "ios/chrome/browser/tabs/tab_title_util.h"
23#import "ios/chrome/browser/ui/activity_services/activity_params.h"
24#import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
25#import "ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.h"
26#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.h"
27#import "ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.h"
28#import "ios/chrome/browser/ui/browser_container/browser_container_coordinator.h"
29#import "ios/chrome/browser/ui/browser_view/browser_view_controller+private.h"
30#import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
31#import "ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h"
32#import "ios/chrome/browser/ui/commands/activity_service_commands.h"
33#import "ios/chrome/browser/ui/commands/application_commands.h"
34#import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
35#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
36#import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
37#import "ios/chrome/browser/ui/commands/infobar_commands.h"
38#import "ios/chrome/browser/ui/commands/page_info_commands.h"
39#import "ios/chrome/browser/ui/commands/password_breach_commands.h"
40#import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
41#import "ios/chrome/browser/ui/commands/share_highlight_command.h"
42#import "ios/chrome/browser/ui/commands/text_zoom_commands.h"
43#import "ios/chrome/browser/ui/commands/whats_new_commands.h"
44#import "ios/chrome/browser/ui/download/ar_quick_look_coordinator.h"
45#import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
46#import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
47#import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
48#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
49#import "ios/chrome/browser/ui/open_in/open_in_mediator.h"
50#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
51#import "ios/chrome/browser/ui/page_info/features.h"
52#import "ios/chrome/browser/ui/page_info/page_info_coordinator.h"
53#import "ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.h"
54#import "ios/chrome/browser/ui/passwords/password_breach_coordinator.h"
55#import "ios/chrome/browser/ui/print/print_controller.h"
56#import "ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h"
57#import "ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.h"
58#import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
59#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
60#import "ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_coordinator.h"
61#import "ios/chrome/browser/ui/sharing/sharing_coordinator.h"
62#import "ios/chrome/browser/ui/snackbar/snackbar_coordinator.h"
63#import "ios/chrome/browser/ui/text_zoom/text_zoom_coordinator.h"
64#import "ios/chrome/browser/ui/toolbar/accessory/toolbar_accessory_coordinator_delegate.h"
65#import "ios/chrome/browser/ui/toolbar/accessory/toolbar_accessory_presenter.h"
66#import "ios/chrome/browser/ui/translate/legacy_translate_infobar_coordinator.h"
67#include "ios/chrome/browser/ui/ui_feature_flags.h"
68#import "ios/chrome/browser/ui/whats_new/default_browser_promo_coordinator.h"
69#import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
70#import "ios/chrome/browser/url_loading/url_loading_params.h"
71#import "ios/chrome/browser/web/features.h"
72#import "ios/chrome/browser/web/font_size_tab_helper.h"
73#import "ios/chrome/browser/web/print_tab_helper.h"
74#import "ios/chrome/browser/web/repost_form_tab_helper.h"
75#import "ios/chrome/browser/web/repost_form_tab_helper_delegate.h"
76#include "ios/chrome/browser/web_state_list/web_state_list.h"
77#import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
78#include "ui/base/l10n/l10n_util_mac.h"
79
80#if !defined(__has_feature) || !__has_feature(objc_arc)
81#error "This file requires ARC support."
82#endif
83
84@interface BrowserCoordinator () <ActivityServiceCommands,
85                                  BrowserCoordinatorCommands,
86                                  DefaultBrowserPromoCommands,
87                                  FormInputAccessoryCoordinatorNavigator,
88                                  PageInfoCommands,
89                                  PasswordBreachCommands,
90                                  RepostFormTabHelperDelegate,
91                                  ToolbarAccessoryCoordinatorDelegate,
92                                  URLLoadingDelegate,
93                                  WebStateListObserving>
94
95// Whether the coordinator is started.
96@property(nonatomic, assign, getter=isStarted) BOOL started;
97
98// Handles command dispatching, provided by the Browser instance.
99@property(nonatomic, weak) CommandDispatcher* dispatcher;
100
101// The coordinator managing the container view controller.
102@property(nonatomic, strong)
103    BrowserContainerCoordinator* browserContainerCoordinator;
104
105// Mediator between OpenIn TabHelper and OpenIn UI.
106@property(nonatomic, strong) OpenInMediator* openInMediator;
107
108// =================================================
109// Child Coordinators, listed in alphabetical order.
110// =================================================
111
112// Presents a QLPreviewController in order to display USDZ format 3D models.
113@property(nonatomic, strong) ARQuickLookCoordinator* ARQuickLookCoordinator;
114
115// Coordinator to add new credit card.
116@property(nonatomic, strong)
117    AutofillAddCreditCardCoordinator* addCreditCardCoordinator;
118
119// Coordinator for the badge popup menu.
120@property(nonatomic, strong)
121    BadgePopupMenuCoordinator* badgePopupMenuCoordinator;
122
123// Coordinator for the find bar.
124@property(nonatomic, strong) FindBarCoordinator* findBarCoordinator;
125
126// Coordinator in charge of the presenting autofill options above the
127// keyboard.
128@property(nonatomic, strong)
129    FormInputAccessoryCoordinator* formInputAccessoryCoordinator;
130
131// Weak reference for the next coordinator to be displayed over the toolbar.
132@property(nonatomic, weak) ChromeCoordinator* nextToolbarCoordinator;
133
134// Coordinator for Page Info UI.
135@property(nonatomic, strong) ChromeCoordinator* pageInfoCoordinator;
136
137// Coordinator for the PassKit UI presentation.
138@property(nonatomic, strong) PassKitCoordinator* passKitCoordinator;
139
140// Coordinator for the password breach UI presentation.
141@property(nonatomic, strong)
142    PasswordBreachCoordinator* passwordBreachCoordinator;
143
144// Used to display the Print UI. Nil if not visible.
145// TODO(crbug.com/910017): Convert to coordinator.
146@property(nonatomic, strong) PrintController* printController;
147
148// Coordinator for the QR scanner.
149@property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
150
151// Coordinator for displaying the Reading List.
152@property(nonatomic, strong) ReadingListCoordinator* readingListCoordinator;
153
154// Coordinator for Recent Tabs.
155@property(nonatomic, strong) RecentTabsCoordinator* recentTabsCoordinator;
156
157// Coordinator for displaying Repost Form dialog.
158@property(nonatomic, strong) RepostFormCoordinator* repostFormCoordinator;
159
160// Coordinator for sharing scenarios.
161@property(nonatomic, strong) SharingCoordinator* sharingCoordinator;
162
163// Coordinator for displaying snackbars.
164@property(nonatomic, strong) SnackbarCoordinator* snackbarCoordinator;
165
166// Coordinator for presenting SKStoreProductViewController.
167@property(nonatomic, strong) StoreKitCoordinator* storeKitCoordinator;
168
169// Coordinator for Text Zoom.
170@property(nonatomic, strong) TextZoomCoordinator* textZoomCoordinator;
171
172// Coordinator for the translate infobar's language selection and translate
173// option popup menus.
174@property(nonatomic, strong)
175    LegacyTranslateInfobarCoordinator* translateInfobarCoordinator;
176
177// Coordinator that manages the default browser promo modal.
178@property(nonatomic, strong)
179    DefaultBrowserPromoCoordinator* defaultBrowserPromoCoordinator;
180
181// The container coordinators for the infobar modalities.
182@property(nonatomic, strong)
183    OverlayContainerCoordinator* infobarBannerOverlayContainerCoordinator;
184@property(nonatomic, strong)
185    OverlayContainerCoordinator* infobarModalOverlayContainerCoordinator;
186
187@end
188
189@implementation BrowserCoordinator {
190  // Observers for WebStateList.
191  std::unique_ptr<WebStateListObserverBridge> _webStateListObserverBridge;
192  std::unique_ptr<ScopedObserver<WebStateList, WebStateListObserver>>
193      _scopedWebStateListObserver;
194}
195
196#pragma mark - ChromeCoordinator
197
198- (instancetype)initWithBaseViewController:(UIViewController*)viewController
199                                   browser:(Browser*)browser {
200  if (self = [super initWithBaseViewController:viewController
201                                       browser:browser]) {
202    _dispatcher = browser->GetCommandDispatcher();
203  }
204  return self;
205}
206- (void)start {
207  if (self.started)
208    return;
209
210  DCHECK(!self.viewController);
211
212  // Add commands protocols handled by this class in this array to let the
213  // dispatcher know where to dispatch such commands. This must be done before
214  // starting any child coordinator, otherwise they won't be able to resolve
215  // handlers.
216  NSArray<Protocol*>* protocols = @[
217    @protocol(ActivityServiceCommands), @protocol(BrowserCoordinatorCommands),
218    @protocol(FindInPageCommands), @protocol(PageInfoCommands),
219    @protocol(PasswordBreachCommands), @protocol(TextZoomCommands),
220    @protocol(WhatsNewCommands)
221  ];
222
223  for (Protocol* protocol in protocols) {
224    [self.dispatcher startDispatchingToTarget:self forProtocol:protocol];
225  }
226
227  [self startBrowserContainer];
228  [self createViewController];
229  [self startChildCoordinators];
230  [self installDelegatesForAllWebStates];
231  [self installDelegatesForBrowser];
232  [self addWebStateListObserver];
233  [super start];
234  self.started = YES;
235}
236
237- (void)stop {
238  if (!self.started)
239    return;
240  [super stop];
241  [self removeWebStateListObserver];
242  [self uninstallDelegatesForBrowser];
243  [self uninstallDelegatesForAllWebStates];
244  self.viewController.commandDispatcher = nil;
245  [self.dispatcher stopDispatchingToTarget:self];
246  [self stopChildCoordinators];
247  [self destroyViewController];
248  [self stopBrowserContainer];
249  self.dispatcher = nil;
250  self.started = NO;
251}
252
253#pragma mark - Public
254
255- (void)setActive:(BOOL)active {
256  DCHECK_EQ(_active, self.viewController.active);
257  if (_active == active) {
258    return;
259  }
260  _active = active;
261
262  self.viewController.active = active;
263}
264
265- (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
266                           dismissOmnibox:(BOOL)dismissOmnibox {
267  [self.passKitCoordinator stop];
268
269  [self.openInMediator disableAll];
270
271  [self.printController dismissAnimated:YES];
272
273  [self.readingListCoordinator stop];
274  self.readingListCoordinator = nil;
275
276  [self.sharingCoordinator stop];
277  self.sharingCoordinator = nil;
278
279  [self.passwordBreachCoordinator stop];
280  self.passwordBreachCoordinator = nil;
281
282  [self.pageInfoCoordinator stop];
283
284  [self.viewController clearPresentedStateWithCompletion:completion
285                                          dismissOmnibox:dismissOmnibox];
286}
287
288- (void)displayPopupMenuWithBadgeItems:(NSArray<id<BadgeItem>>*)badgeItems {
289  self.badgePopupMenuCoordinator = [[BadgePopupMenuCoordinator alloc]
290      initWithBaseViewController:self.viewController
291                         browser:self.browser];
292  [self.badgePopupMenuCoordinator setBadgeItemsToShow:badgeItems];
293  [self.badgePopupMenuCoordinator start];
294}
295
296#pragma mark - Private
297
298// Instantiates a BrowserViewController.
299- (void)createViewController {
300  DCHECK(self.browserContainerCoordinator.viewController);
301  BrowserViewControllerDependencyFactory* factory =
302      [[BrowserViewControllerDependencyFactory alloc]
303          initWithBrowser:self.browser];
304  _viewController = [[BrowserViewController alloc]
305                     initWithBrowser:self.browser
306                   dependencyFactory:factory
307      browserContainerViewController:self.browserContainerCoordinator
308                                         .viewController
309                          dispatcher:self.dispatcher];
310}
311
312// Shuts down the BrowserViewController.
313- (void)destroyViewController {
314  [self.viewController shutdown];
315  _viewController = nil;
316}
317
318// Starts the browser container.
319- (void)startBrowserContainer {
320  self.browserContainerCoordinator = [[BrowserContainerCoordinator alloc]
321      initWithBaseViewController:nil
322                         browser:self.browser];
323  [self.browserContainerCoordinator start];
324}
325
326// Stops the browser container.
327- (void)stopBrowserContainer {
328  [self.browserContainerCoordinator stop];
329  self.browserContainerCoordinator = nil;
330}
331
332// Starts child coordinators.
333- (void)startChildCoordinators {
334  // Dispatcher should be instantiated so that it can be passed to child
335  // coordinators.
336  DCHECK(self.dispatcher);
337
338  self.ARQuickLookCoordinator = [[ARQuickLookCoordinator alloc]
339      initWithBaseViewController:self.viewController
340                         browser:self.browser];
341  [self.ARQuickLookCoordinator start];
342
343  self.formInputAccessoryCoordinator = [[FormInputAccessoryCoordinator alloc]
344      initWithBaseViewController:self.viewController
345                         browser:self.browser];
346  self.formInputAccessoryCoordinator.navigator = self;
347  [self.formInputAccessoryCoordinator start];
348
349  self.snackbarCoordinator = [[SnackbarCoordinator alloc]
350      initWithBaseViewController:self.viewController
351                         browser:self.browser];
352  [self.snackbarCoordinator start];
353
354  self.translateInfobarCoordinator = [[LegacyTranslateInfobarCoordinator alloc]
355      initWithBaseViewController:self.viewController
356                         browser:self.browser];
357  [self.translateInfobarCoordinator start];
358
359  self.passKitCoordinator =
360      [[PassKitCoordinator alloc] initWithBaseViewController:self.viewController
361                                                     browser:self.browser];
362
363  self.printController = [[PrintController alloc] init];
364  self.printController.baseViewController = self.viewController;
365
366  self.qrScannerCoordinator = [[QRScannerLegacyCoordinator alloc]
367      initWithBaseViewController:self.viewController
368                         browser:self.browser];
369  [self.qrScannerCoordinator start];
370
371  /* passwordBreachCoordinator is created and started by a BrowserCommand */
372
373  /* ReadingListCoordinator is created and started by a BrowserCommand */
374
375  /* RecentTabsCoordinator is created and started by a BrowserCommand */
376
377  /* RepostFormCoordinator is created and started by a delegate method */
378
379  /* SharingCoordinator is created and started by an ActivityServiceCommand */
380
381  self.storeKitCoordinator = [[StoreKitCoordinator alloc]
382      initWithBaseViewController:self.viewController
383                         browser:self.browser];
384
385  self.addCreditCardCoordinator = [[AutofillAddCreditCardCoordinator alloc]
386      initWithBaseViewController:self.viewController
387                         browser:self.browser];
388
389  if (base::FeatureList::IsEnabled(kInfobarOverlayUI)) {
390    self.infobarBannerOverlayContainerCoordinator =
391        [[OverlayContainerCoordinator alloc]
392            initWithBaseViewController:self.viewController
393                               browser:self.browser
394                              modality:OverlayModality::kInfobarBanner];
395    [self.infobarBannerOverlayContainerCoordinator start];
396    self.viewController.infobarBannerOverlayContainerViewController =
397        self.infobarBannerOverlayContainerCoordinator.viewController;
398
399    self.infobarModalOverlayContainerCoordinator =
400        [[OverlayContainerCoordinator alloc]
401            initWithBaseViewController:self.viewController
402                               browser:self.browser
403                              modality:OverlayModality::kInfobarModal];
404    [self.infobarModalOverlayContainerCoordinator start];
405    self.viewController.infobarModalOverlayContainerViewController =
406        self.infobarModalOverlayContainerCoordinator.viewController;
407  }
408}
409
410// Stops child coordinators.
411- (void)stopChildCoordinators {
412  [self.ARQuickLookCoordinator stop];
413  self.ARQuickLookCoordinator = nil;
414
415  [self.findBarCoordinator stop];
416  self.findBarCoordinator = nil;
417
418  [self.formInputAccessoryCoordinator stop];
419  self.formInputAccessoryCoordinator = nil;
420
421  [self.pageInfoCoordinator stop];
422  self.pageInfoCoordinator = nil;
423
424  [self.passKitCoordinator stop];
425  self.passKitCoordinator = nil;
426
427  [self.passwordBreachCoordinator stop];
428  self.passwordBreachCoordinator = nil;
429
430  self.printController = nil;
431
432  [self.qrScannerCoordinator stop];
433  self.qrScannerCoordinator = nil;
434
435  [self.readingListCoordinator stop];
436  self.readingListCoordinator = nil;
437
438  [self.recentTabsCoordinator stop];
439  self.recentTabsCoordinator = nil;
440
441  [self.repostFormCoordinator stop];
442  self.repostFormCoordinator = nil;
443
444  [self.sharingCoordinator stop];
445  self.sharingCoordinator = nil;
446
447  [self.snackbarCoordinator stop];
448  self.snackbarCoordinator = nil;
449
450  [self.storeKitCoordinator stop];
451  self.storeKitCoordinator = nil;
452
453  [self.textZoomCoordinator stop];
454  self.textZoomCoordinator = nil;
455
456  [self.translateInfobarCoordinator stop];
457  self.translateInfobarCoordinator = nil;
458
459  [self.addCreditCardCoordinator stop];
460  self.addCreditCardCoordinator = nil;
461
462  [self.infobarBannerOverlayContainerCoordinator stop];
463  self.infobarBannerOverlayContainerCoordinator = nil;
464
465  [self.infobarModalOverlayContainerCoordinator stop];
466  self.infobarModalOverlayContainerCoordinator = nil;
467
468  [self.defaultBrowserPromoCoordinator stop];
469  self.defaultBrowserPromoCoordinator = nil;
470}
471
472#pragma mark - ActivityServiceCommands
473
474- (void)sharePage {
475  ActivityParams* params = [[ActivityParams alloc]
476      initWithScenario:ActivityScenario::TabShareButton];
477
478  self.sharingCoordinator = [[SharingCoordinator alloc]
479      initWithBaseViewController:self.viewController
480                         browser:self.browser
481                          params:params
482                      originView:self.viewController.activityServicePositioner
483                                     .sourceView
484                      originRect:self.viewController.activityServicePositioner
485                                     .sourceRect];
486  [self.sharingCoordinator start];
487}
488
489- (void)shareHighlight:(ShareHighlightCommand*)command {
490  ActivityParams* params =
491      [[ActivityParams alloc] initWithURL:command.URL
492                                    title:command.title
493                           additionalText:command.selectedText
494                                 scenario:ActivityScenario::SharedHighlight];
495
496  self.sharingCoordinator = [[SharingCoordinator alloc]
497      initWithBaseViewController:self.viewController
498                         browser:self.browser
499                          params:params
500                      originView:command.sourceView
501                      originRect:command.sourceRect];
502  [self.sharingCoordinator start];
503}
504
505#pragma mark - BrowserCoordinatorCommands
506
507- (void)printTab {
508  DCHECK(self.printController);
509  web::WebState* webState =
510      self.browser->GetWebStateList()->GetActiveWebState();
511  [self.printController printWebState:webState];
512}
513
514- (void)showReadingList {
515  self.readingListCoordinator = [[ReadingListCoordinator alloc]
516      initWithBaseViewController:self.viewController
517                         browser:self.browser];
518  [self.readingListCoordinator start];
519}
520
521- (void)showDownloadsFolder {
522  NSURL* URL = GetFilesAppUrl();
523  if (!URL)
524    return;
525
526  [[UIApplication sharedApplication] openURL:URL
527                                     options:@{}
528                           completionHandler:nil];
529}
530
531- (void)showRecentTabs {
532  // TODO(crbug.com/825431): If BVC's clearPresentedState is ever called (such
533  // as in tearDown after a failed egtest), then this coordinator is left in a
534  // started state even though its corresponding VC is no longer on screen.
535  // That causes issues when the coordinator is started again and we destroy the
536  // old mediator without disconnecting it first.  Temporarily work around these
537  // issues by not having a long lived coordinator.  A longer-term solution will
538  // require finding a way to stop this coordinator so that the mediator is
539  // properly disconnected and destroyed and does not live longer than its
540  // associated VC.
541  if (self.recentTabsCoordinator) {
542    [self.recentTabsCoordinator stop];
543    self.recentTabsCoordinator = nil;
544  }
545
546  self.recentTabsCoordinator = [[RecentTabsCoordinator alloc]
547      initWithBaseViewController:self.viewController
548                         browser:self.browser];
549  self.recentTabsCoordinator.loadStrategy = UrlLoadStrategy::NORMAL;
550  [self.recentTabsCoordinator start];
551}
552
553- (void)showAddCreditCard {
554  [self.formInputAccessoryCoordinator reset];
555  [self.addCreditCardCoordinator start];
556}
557
558#pragma mark - WhatsNewCommands
559
560- (void)showDefaultBrowserFullscreenPromo {
561  if (!self.defaultBrowserPromoCoordinator) {
562    self.defaultBrowserPromoCoordinator =
563        [[DefaultBrowserPromoCoordinator alloc]
564            initWithBaseViewController:self.viewController
565                               browser:self.browser];
566    self.defaultBrowserPromoCoordinator.handler = self;
567  }
568  [self.defaultBrowserPromoCoordinator start];
569}
570
571#pragma mark - DefaultBrowserPromoCommands
572
573- (void)hidePromo {
574  [self.defaultBrowserPromoCoordinator stop];
575  self.defaultBrowserPromoCoordinator = nil;
576}
577
578#pragma mark - FindInPageCommands
579
580- (void)openFindInPage {
581  if (!self.canShowFindBar)
582    return;
583
584  self.findBarCoordinator =
585      [[FindBarCoordinator alloc] initWithBaseViewController:self.viewController
586                                                     browser:self.browser];
587  self.findBarCoordinator.presenter =
588      self.viewController.toolbarAccessoryPresenter;
589  self.findBarCoordinator.delegate = self;
590  self.findBarCoordinator.presentationDelegate = self.viewController;
591
592  if (self.viewController.toolbarAccessoryPresenter.isPresenting) {
593    self.nextToolbarCoordinator = self.findBarCoordinator;
594    [self closeTextZoom];
595    return;
596  }
597
598  [self.findBarCoordinator start];
599}
600
601- (void)closeFindInPage {
602  web::WebState* currentWebState =
603      self.browser->GetWebStateList()->GetActiveWebState();
604
605  if (currentWebState) {
606    FindTabHelper* findTabHelper = FindTabHelper::FromWebState(currentWebState);
607    if (findTabHelper->IsFindUIActive()) {
608      findTabHelper->StopFinding();
609    } else {
610      [self.findBarCoordinator stop];
611    }
612  }
613}
614
615- (void)showFindUIIfActive {
616  web::WebState* currentWebState =
617      self.browser->GetWebStateList()->GetActiveWebState();
618  auto* findHelper = FindTabHelper::FromWebState(currentWebState);
619  if (findHelper && findHelper->IsFindUIActive() &&
620      !self.findBarCoordinator.presenter.isPresenting) {
621    [self.findBarCoordinator start];
622  }
623}
624
625- (void)hideFindUI {
626  [self.findBarCoordinator stop];
627}
628
629- (void)defocusFindInPage {
630  [self.findBarCoordinator defocusFindBar];
631}
632
633- (void)searchFindInPage {
634  web::WebState* currentWebState =
635      self.browser->GetWebStateList()->GetActiveWebState();
636  DCHECK(currentWebState);
637  FindTabHelper* helper = FindTabHelper::FromWebState(currentWebState);
638  helper->StartFinding([self.findBarCoordinator.findBarController searchTerm]);
639
640  if (!self.browser->GetBrowserState()->IsOffTheRecord())
641    helper->PersistSearchTerm();
642}
643
644- (void)findNextStringInPage {
645  web::WebState* currentWebState =
646      self.browser->GetWebStateList()->GetActiveWebState();
647  DCHECK(currentWebState);
648  // TODO(crbug.com/603524): Reshow find bar if necessary.
649  FindTabHelper::FromWebState(currentWebState)
650      ->ContinueFinding(FindTabHelper::FORWARD);
651}
652
653- (void)findPreviousStringInPage {
654  web::WebState* currentWebState =
655      self.browser->GetWebStateList()->GetActiveWebState();
656  DCHECK(currentWebState);
657  // TODO(crbug.com/603524): Reshow find bar if necessary.
658  FindTabHelper::FromWebState(currentWebState)
659      ->ContinueFinding(FindTabHelper::REVERSE);
660}
661
662#pragma mark - FindInPageCommands Helpers
663
664- (BOOL)canShowFindBar {
665  web::WebState* currentWebState =
666      self.browser->GetWebStateList()->GetActiveWebState();
667  if (!currentWebState) {
668    return NO;
669  }
670
671  auto* helper = FindTabHelper::FromWebState(currentWebState);
672  return (helper && helper->CurrentPageSupportsFindInPage() &&
673          !helper->IsFindUIActive());
674}
675
676#pragma mark - PageInfoCommands
677
678- (void)legacyShowPageInfoForOriginPoint:(CGPoint)originPoint {
679  PageInfoLegacyCoordinator* pageInfoCoordinator =
680      [[PageInfoLegacyCoordinator alloc]
681          initWithBaseViewController:self.viewController
682                             browser:self.browser];
683  pageInfoCoordinator.presentationProvider = self.viewController;
684  pageInfoCoordinator.originPoint = originPoint;
685  self.pageInfoCoordinator = pageInfoCoordinator;
686  [self.pageInfoCoordinator start];
687}
688
689- (void)showPageInfo {
690  DCHECK(base::FeatureList::IsEnabled(kPageInfoRefactoring));
691  PageInfoCoordinator* pageInfoCoordinator = [[PageInfoCoordinator alloc]
692      initWithBaseViewController:self.viewController
693                         browser:self.browser];
694  pageInfoCoordinator.presentationProvider = self.viewController;
695  self.pageInfoCoordinator = pageInfoCoordinator;
696  [self.pageInfoCoordinator start];
697}
698
699- (void)hidePageInfo {
700  [self.pageInfoCoordinator stop];
701  self.pageInfoCoordinator = nil;
702}
703
704- (void)showSecurityHelpPage {
705  UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kPageInfoHelpCenterURL));
706  params.in_incognito = self.browser->GetBrowserState()->IsOffTheRecord();
707  UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
708  [self hidePageInfo];
709}
710
711#pragma mark - FormInputAccessoryCoordinatorNavigator
712
713- (void)openPasswordSettings {
714  [HandlerForProtocol(self.dispatcher, ApplicationCommands)
715      showSavedPasswordsSettingsFromViewController:self.viewController];
716}
717
718- (void)openAddressSettings {
719  [HandlerForProtocol(self.dispatcher, ApplicationCommands)
720      showProfileSettingsFromViewController:self.viewController];
721}
722
723- (void)openCreditCardSettings {
724  [HandlerForProtocol(self.dispatcher, ApplicationCommands)
725      showCreditCardSettingsFromViewController:self.viewController];
726}
727
728#pragma mark - RepostFormTabHelperDelegate
729
730- (void)repostFormTabHelper:(RepostFormTabHelper*)helper
731    presentRepostFormDialogForWebState:(web::WebState*)webState
732                         dialogAtPoint:(CGPoint)location
733                     completionHandler:(void (^)(BOOL))completion {
734  self.repostFormCoordinator = [[RepostFormCoordinator alloc]
735      initWithBaseViewController:self.viewController
736                         browser:self.browser
737                  dialogLocation:location
738                        webState:webState
739               completionHandler:completion];
740  [self.repostFormCoordinator start];
741}
742
743- (void)repostFormTabHelperDismissRepostFormDialog:
744    (RepostFormTabHelper*)helper {
745  [self.repostFormCoordinator stop];
746  self.repostFormCoordinator = nil;
747}
748
749#pragma mark - ToolbarAccessoryCoordinatorDelegate
750
751- (void)toolbarAccessoryCoordinatorDidDismissUI:
752    (ChromeCoordinator*)coordinator {
753  if (!self.nextToolbarCoordinator) {
754    return;
755  }
756  if (self.nextToolbarCoordinator == self.findBarCoordinator) {
757    [self openFindInPage];
758    self.nextToolbarCoordinator = nil;
759  } else if (self.nextToolbarCoordinator == self.textZoomCoordinator) {
760    [self openTextZoom];
761    self.nextToolbarCoordinator = nil;
762  }
763}
764
765#pragma mark - TextZoomCommands
766
767- (void)openTextZoom {
768  self.textZoomCoordinator = [[TextZoomCoordinator alloc]
769      initWithBaseViewController:self.viewController
770                         browser:self.browser];
771  self.textZoomCoordinator.presenter =
772      self.viewController.toolbarAccessoryPresenter;
773  self.textZoomCoordinator.delegate = self;
774
775  if (self.viewController.toolbarAccessoryPresenter.isPresenting) {
776    self.nextToolbarCoordinator = self.textZoomCoordinator;
777    [self closeFindInPage];
778    return;
779  }
780
781  [self.textZoomCoordinator start];
782}
783
784- (void)closeTextZoom {
785  if (!base::FeatureList::IsEnabled(web::kWebPageTextAccessibility)) {
786    return;
787  }
788
789  web::WebState* currentWebState =
790      self.browser->GetWebStateList()->GetActiveWebState();
791  if (currentWebState) {
792    FontSizeTabHelper* fontSizeTabHelper =
793        FontSizeTabHelper::FromWebState(currentWebState);
794    fontSizeTabHelper->SetTextZoomUIActive(false);
795  }
796  [self.textZoomCoordinator stop];
797}
798
799- (void)showTextZoomUIIfActive {
800  web::WebState* currentWebState =
801      self.browser->GetWebStateList()->GetActiveWebState();
802  if (!currentWebState) {
803    return;
804  }
805
806  FontSizeTabHelper* fontSizeTabHelper =
807      FontSizeTabHelper::FromWebState(currentWebState);
808  if (fontSizeTabHelper && fontSizeTabHelper->IsTextZoomUIActive() &&
809      !self.textZoomCoordinator.presenter.isPresenting) {
810    [self.textZoomCoordinator start];
811  }
812}
813
814- (void)hideTextZoomUI {
815  [self.textZoomCoordinator stop];
816}
817
818#pragma mark - URLLoadingServiceDelegate
819
820- (void)animateOpenBackgroundTabFromParams:(const UrlLoadParams&)params
821                                completion:(void (^)())completion {
822  [self.viewController
823      animateOpenBackgroundTabFromOriginPoint:params.origin_point
824                                   completion:completion];
825}
826
827// TODO(crbug.com/906525) : Move WebStateListObserving out of
828// BrowserCoordinator.
829#pragma mark - WebStateListObserving
830
831- (void)webStateList:(WebStateList*)webStateList
832    didInsertWebState:(web::WebState*)webState
833              atIndex:(int)index
834           activating:(BOOL)activating {
835  [self installDelegatesForWebState:webState];
836}
837
838- (void)webStateList:(WebStateList*)webStateList
839    didReplaceWebState:(web::WebState*)oldWebState
840          withWebState:(web::WebState*)newWebState
841               atIndex:(int)index {
842  [self uninstallDelegatesForWebState:oldWebState];
843  [self installDelegatesForWebState:newWebState];
844}
845
846- (void)webStateList:(WebStateList*)webStateList
847    didDetachWebState:(web::WebState*)webState
848              atIndex:(int)index {
849  [self uninstallDelegatesForWebState:webState];
850}
851
852// TODO(crbug.com/906525) : Move out of BrowserCoordinator along with
853// WebStateListObserving.
854#pragma mark - Private WebState management methods
855
856// Adds observer for WebStateList.
857- (void)addWebStateListObserver {
858  _webStateListObserverBridge =
859      std::make_unique<WebStateListObserverBridge>(self);
860  _scopedWebStateListObserver =
861      std::make_unique<ScopedObserver<WebStateList, WebStateListObserver>>(
862          _webStateListObserverBridge.get());
863  _scopedWebStateListObserver->Add(self.browser->GetWebStateList());
864}
865
866// Removes observer for WebStateList.
867- (void)removeWebStateListObserver {
868  _scopedWebStateListObserver.reset();
869  _webStateListObserverBridge.reset();
870}
871
872// Installs delegates for each WebState in WebStateList.
873- (void)installDelegatesForAllWebStates {
874  self.openInMediator = [[OpenInMediator alloc] initWithBrowser:self.browser];
875
876  for (int i = 0; i < self.browser->GetWebStateList()->count(); i++) {
877    web::WebState* webState = self.browser->GetWebStateList()->GetWebStateAt(i);
878    [self installDelegatesForWebState:webState];
879  }
880}
881
882// Installs delegates for self.browser.
883- (void)installDelegatesForBrowser {
884  UrlLoadingBrowserAgent* loadingAgent =
885      UrlLoadingBrowserAgent::FromBrowser(self.browser);
886  if (loadingAgent) {
887    loadingAgent->SetDelegate(self);
888  }
889}
890
891// Uninstalls delegates for self.browser.
892- (void)uninstallDelegatesForBrowser {
893  UrlLoadingBrowserAgent* loadingAgent =
894      UrlLoadingBrowserAgent::FromBrowser(self.browser);
895  if (loadingAgent) {
896    loadingAgent->SetDelegate(nil);
897  }
898}
899
900// Uninstalls delegates for each WebState in WebStateList.
901- (void)uninstallDelegatesForAllWebStates {
902  // OpenInMediator is controlled directly monitors the webStateList and should
903  // be deleted.
904  self.openInMediator = nil;
905  for (int i = 0; i < self.browser->GetWebStateList()->count(); i++) {
906    web::WebState* webState = self.browser->GetWebStateList()->GetWebStateAt(i);
907    [self uninstallDelegatesForWebState:webState];
908  }
909}
910
911// Install delegates for |webState|.
912- (void)installDelegatesForWebState:(web::WebState*)webState {
913  if (AutofillTabHelper::FromWebState(webState)) {
914    AutofillTabHelper::FromWebState(webState)->SetBaseViewController(
915        self.viewController);
916  }
917
918  PassKitTabHelper::CreateForWebState(webState, self.passKitCoordinator);
919
920  if (PrintTabHelper::FromWebState(webState)) {
921    PrintTabHelper::FromWebState(webState)->set_printer(self.printController);
922  }
923
924  RepostFormTabHelper::CreateForWebState(webState, self);
925
926  if (StoreKitTabHelper::FromWebState(webState)) {
927    StoreKitTabHelper::FromWebState(webState)->SetLauncher(
928        self.storeKitCoordinator);
929  }
930}
931
932// Uninstalls delegates for |webState|.
933- (void)uninstallDelegatesForWebState:(web::WebState*)webState {
934  if (PrintTabHelper::FromWebState(webState)) {
935    PrintTabHelper::FromWebState(webState)->set_printer(nil);
936  }
937
938  if (StoreKitTabHelper::FromWebState(webState)) {
939    StoreKitTabHelper::FromWebState(webState)->SetLauncher(nil);
940  }
941}
942
943#pragma mark - PasswordBreachCommands
944
945- (void)showPasswordBreachForLeakType:(CredentialLeakType)leakType
946                                  URL:(const GURL&)URL {
947  self.passwordBreachCoordinator = [[PasswordBreachCoordinator alloc]
948      initWithBaseViewController:self.viewController
949                         browser:self.browser
950                        leakType:leakType
951                             URL:URL];
952  [self.passwordBreachCoordinator start];
953}
954
955@end
956