// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.h"

#import "base/functional/bind.h"
#import "base/ios/ios_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/sys_string_conversions.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/lens/lens_overlay_metrics.h"
#import "components/omnibox/browser/omnibox_field_trial.h"
#import "components/omnibox/browser/omnibox_pref_names.h"
#import "components/omnibox/common/omnibox_features.h"
#import "components/open_from_clipboard/clipboard_recent_content.h"
#import "components/prefs/pref_service.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/cobrowse/model/cobrowse_context.h"
#import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_animator.h"
#import "ios/chrome/browser/intelligence/bwg/metrics/gemini_metrics.h"
#import "ios/chrome/browser/intelligence/bwg/utils/gemini_constants.h"
#import "ios/chrome/browser/intelligence/features/features.h"
#import "ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_entrypoint_view.h"
#import "ios/chrome/browser/lens/ui_bundled/lens_entrypoint.h"
#import "ios/chrome/browser/lens_overlay/model/lens_overlay_presentation_type.h"
#import "ios/chrome/browser/lens_overlay/public/lens_overlay_availability.h"
#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.h"
#import "ios/chrome/browser/location_bar/ui_bundled/badges_container_view.h"
#import "ios/chrome/browser/location_bar/ui_bundled/fakebox_buttons_snapshot_provider.h"
#import "ios/chrome/browser/location_bar/ui_bundled/highlight_utils.h"
#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_constants.h"
#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_metrics.h"
#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_mutator.h"
#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_placeholder_type.h"
#import "ios/chrome/browser/location_bar/ui_bundled/location_bar_steady_view.h"
#import "ios/chrome/browser/menu/ui_bundled/action_factory.h"
#import "ios/chrome/browser/menu/ui_bundled/menu_histograms.h"
#import "ios/chrome/browser/omnibox/public/omnibox_constants.h"
#import "ios/chrome/browser/omnibox/ui/text_field_view_containing.h"
#import "ios/chrome/browser/orchestrator/ui_bundled/location_bar_offset_provider.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/commands/activity_service_commands.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/custom_leading_view_type.h"
#import "ios/chrome/browser/shared/public/commands/gemini_commands.h"
#import "ios/chrome/browser/shared/public/commands/help_commands.h"
#import "ios/chrome/browser/shared/public/commands/lens_commands.h"
#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
#import "ios/chrome/browser/shared/public/commands/open_lens_input_selection_command.h"
#import "ios/chrome/browser/shared/public/commands/page_action_menu_commands.h"
#import "ios/chrome/browser/shared/public/commands/page_action_menu_entry_point_commands.h"
#import "ios/chrome/browser/shared/public/commands/scene_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/shared/ui/util/util_swift.h"
#import "ios/chrome/browser/sharing/ui_bundled/sharing_metrics.h"
#import "ios/chrome/browser/toolbar/legacy/ui_bundled/public/toolbar_type.h"
#import "ios/chrome/common/NSString+Chromium.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#import "ios/chrome/common/ui/util/pointer_interaction_util.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/public/provider/chrome/browser/lens/lens_api.h"
#import "ui/base/l10n/l10n_util.h"

using base::UserMetricsAction;

namespace {

typedef NS_ENUM(int, TrailingButtonState) {
  kNoButton = 0,
  kShareButton,
  kVoiceSearchButton,
};

// The scale factor of the steady view in fullscreen.
const CGFloat kFullscreenScaleFactor = 0.87;

// Spacing between the custom leading view (Gemini Live icon) and the URL label.
const CGFloat kGeminiLiveIconToLabelSpacing = 8.0;

// The size of the symbol image.
const CGFloat kSymbolImagePointSize = 18.;

// Identifier for the omnibox embedded in this location bar as a scribble
// element.
const NSString* kScribbleOmniboxElementId = @"omnibox";

// The padding to be added to the bottom of the system share icon to balance
// the white space on top.
const CGFloat kShareIconBalancingHeightPadding = 1;

// The minimum width of the plus button.
const CGFloat kPlusButtonMinimumWidth = 44.0;

// The point size of the Gemini Live symbol.
const CGFloat kGeminiLiveSymbolPointSize = 10.0;

// The size of the Gemini Live circle container.
const CGFloat kGeminiLiveCircleSize = 20.0;
}  // namespace

// Helper view to maintain a round background.
@interface RoundGeminiLiveView : UIView
@end

@implementation RoundGeminiLiveView
- (void)layoutSubviews {
  [super layoutSubviews];
  self.layer.cornerRadius = self.bounds.size.height / 2.0;
  self.clipsToBounds = YES;
}
@end

@interface LocationBarViewController () <UIContextMenuInteractionDelegate,
                                         UIIndirectScribbleInteractionDelegate>
// The injected edit view.
@property(nonatomic, strong) UIView<TextFieldViewContaining>* editView;

// The injected text field.
@property(nonatomic, weak) UIView* textField;

// The injected incognito badge view.
@property(nonatomic, strong) UIView* incognitoBadgeView;

// The injected badge view.
@property(nonatomic, strong) UIView* badgeView;

// The injected Contextual Panel entrypoint view;
@property(nonatomic, strong) UIView* contextualPanelEntrypointView;

// The injected reader mode chip view.
@property(nonatomic, strong) UIView* readerModeChipView;

// The injected placeholder view;
@property(nonatomic, strong) UIView* placeholderView;

// The view that displays current location when the omnibox is not focused.
@property(nonatomic, strong) LocationBarSteadyView* locationBarSteadyView;

@property(nonatomic, assign) TrailingButtonState trailingButtonState;

// When this flag is YES, the share button will not be displayed in situations
// when it normally is shown. Setting it triggers a refresh of the button
// visibility.
@property(nonatomic, assign) BOOL hideShareButtonWhileOnIncognitoNTP;

// Keeps the share button enabled status. This is necessary to preserve the
// state of the share button if it's temporarily replaced by the voice search
// icon (in iPad multitasking).
@property(nonatomic, assign) BOOL shareButtonEnabled;

// Whether the default search engine supports search-by-image. This controls the
// edit menu option to do an image search.
@property(nonatomic, assign) BOOL searchByImageEnabled;

// Whether the default search engine supports Lensing images. This controls the
// edit menu option to do an image search.
@property(nonatomic, assign) BOOL lensImageEnabled;

// Placeholder text.
@property(nonatomic, copy) NSString* placeholderText;

// Type of the current placeholder view.
@property(nonatomic, assign) LocationBarPlaceholderType placeholderType;

// The icon for the default search engine.
@property(nonatomic, strong) UIImage* placeholderDefaultSearchEngineIcon;

// Starts voice search, updating the layout guide to be constrained to the
// trailing button.
- (void)startVoiceSearch;

@end

@implementation LocationBarViewController {
  BOOL _isNTP;
  BOOL _active;
  BOOL _textOnly;
  // Stores a snapshot of the fakebox buttons that is overlaid on the Location
  // Bar and anchored to the trailing edge during focus transitions (when it is
  // faded out) and defocus transitions (when it is faded in).
  UIView* _fakeboxButtonsSnapshot;

  // The AIM plus button.
  ExtendedTouchTargetButton* _plusButton;

  // The location bar button to access Lens.
  LensOverlayEntrypointButton* _lensOverlayPlaceholderView;

  // The location bar button to access the page action menu.
  PageActionMenuEntrypointView* _pageActionMenuEntrypointView;

  // The placeholder view that holds the DSE icon.
  UIImageView* _defaultSearchEngineIconView;

  // The current fullscreen progress, ranging from 1.0 (toolbar fully visible)
  // to 0.0 (toolbar fully collapsed / fullscreen).
  CGFloat _fullscreenProgress;

  // The type of custom leading view to display.
  CustomLeadingViewType _customLeadingViewType;
}

#pragma mark - public

- (instancetype)initWithTextOnly:(BOOL)textOnly {
  self = [super initWithNibName:nil bundle:nil];
  if (self) {
    _textOnly = textOnly;
    _locationBarSteadyView =
        [[LocationBarSteadyView alloc] initWithTextOnly:textOnly];
    if (!_textOnly && IsGlassToolbarEnabled()) {
      _steadyViewLayoutGuide = [[UILayoutGuide alloc] init];
    }
    _fullscreenProgress = 1.0;
    _customLeadingViewType = CustomLeadingViewType::kNone;
  }
  return self;
}

- (instancetype)init {
  return [self initWithTextOnly:NO];
}

- (void)setEditView:(UIView<TextFieldViewContaining>*)editView {
  DCHECK(!self.editView);
  _editView = editView;
  _textField = editView.textFieldView;
}

- (void)setBadgeView:(UIView*)badgeView {
  DCHECK(!self.badgeView);
  _badgeView = badgeView;
}

- (void)setIncognitoBadgeView:(UIView*)incognitoBadgeView {
  CHECK(!self.incognitoBadgeView);
  _incognitoBadgeView = incognitoBadgeView;
}

- (void)setContextualPanelEntrypointView:
    (UIView*)contextualPanelEntrypointView {
  DCHECK(!self.contextualPanelEntrypointView);
  _contextualPanelEntrypointView = contextualPanelEntrypointView;
}

- (void)setReaderModeChipView:(UIView*)readerModeChipView {
  DCHECK(!self.readerModeChipView);
  _readerModeChipView = readerModeChipView;
}

- (void)setPlaceholderType:(LocationBarPlaceholderType)placeholderType {
  if (placeholderType == _placeholderType) {
    return;
  }
  _placeholderType = placeholderType;
  if (self.isViewLoaded) {
    [self updatePlaceholderView];
  }
}

- (void)switchToEditing:(BOOL)editing {
  self.editView.hidden = !editing;
  self.locationBarSteadyView.hidden = editing;
}

- (id<PageActionMenuEntryPointCommands>)pageActionMenuEntryPointHandler {
  return _pageActionMenuEntrypointView;
}

- (void)setIncognito:(BOOL)incognito {
  _incognito = incognito;
  self.locationBarSteadyView.incognito = incognito;
  self.locationBarSteadyView.colorScheme =
      [LocationBarSteadyViewColorScheme standardScheme];
}

- (void)setVoiceSearchEnabled:(BOOL)enabled {
  if (_voiceSearchEnabled == enabled) {
    return;
  }
  _voiceSearchEnabled = enabled;
  [self updateTrailingButtonState];
}

- (void)setHideShareButtonWhileOnIncognitoNTP:(BOOL)hide {
  if (_hideShareButtonWhileOnIncognitoNTP == hide) {
    return;
  }
  _hideShareButtonWhileOnIncognitoNTP = hide;
  [self updateTrailingButton];
}

- (void)updateTrailingButtonState {
  if (IsNextOldDesignEnabled()) {
    self.trailingButtonState = kShareButton;
    return;
  }

  BOOL shouldShowVoiceSearch =
      self.traitCollection.horizontalSizeClass ==
          UIUserInterfaceSizeClassRegular ||
      self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;

  self.trailingButtonState =
      shouldShowVoiceSearch ? kVoiceSearchButton : kShareButton;
}

- (id<ContextualPanelEntrypointVisibilityDelegate>)
    contextualEntrypointVisibilityDelegate {
  return self.locationBarSteadyView.contextualEntrypointVisibilityDelegate;
}

- (id<BadgeViewVisibilityDelegate>)badgeViewVisibilityDelegate {
  return self.locationBarSteadyView.badgeViewVisibilityDelegate;
}

- (id<ReaderModeChipVisibilityDelegate>)readerModeChipVisibilityDelegate {
  return self.locationBarSteadyView.readerModeChipVisibilityDelegate;
}

- (id<IncognitoBadgeViewVisibilityDelegate>)
    incognitoBadgeViewVisibilityDelegate {
  return self.locationBarSteadyView.incognitoBadgeViewVisibilityDelegate;
}

- (void)setHelpCommandsHandler:(id<HelpCommands>)helpCommandsHandler {
  _helpCommandsHandler = helpCommandsHandler;
}

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.view.clipsToBounds = YES;

  // TODO(crbug.com/328446957): Cleanup when fully launched, at which point
  // `contextualPanelEntrypointView` should be CHECK()'ed. Until fully launched,
  // the entrypoint view might be nil if the flag is disabled.
  if (self.contextualPanelEntrypointView) {
    [self.locationBarSteadyView
        setContextualPanelEntrypointView:self.contextualPanelEntrypointView];
  }

  if (self.incognitoBadgeView) {
    [self.locationBarSteadyView setIncognitoBadgeView:self.incognitoBadgeView];
  }

  DCHECK(self.badgeView) << "The badge view must be set at this point";
  [self.locationBarSteadyView setBadgeView:self.badgeView];
  if (self.readerModeChipView) {
    [self.locationBarSteadyView setReaderModeChipView:self.readerModeChipView];
  }

  if (IsPageActionMenuEnabled()) {
    _pageActionMenuEntrypointView = [[PageActionMenuEntrypointView alloc] init];
    [_pageActionMenuEntrypointView
               addTarget:self
                  action:@selector(handlePageActionMenuEntrypointTapped)
        forControlEvents:UIControlEventTouchUpInside];
  }

  if (IsLensOverlayAllowedByPolicy(_profilePrefs)) {
    _lensOverlayPlaceholderView = [[LensOverlayEntrypointButton alloc]
        initWithProfilePrefs:_profilePrefs];

    [_lensOverlayPlaceholderView
               addTarget:self
                  action:@selector(handleLensEntrypointPressed)
        forControlEvents:UIControlEventTouchUpInside];
  }

  [self createAIMPlusButton];

  [_locationBarSteadyView.locationButton
             addTarget:self
                action:@selector(locationBarSteadyViewTapped)
      forControlEvents:UIControlEventTouchUpInside];

  [_locationBarSteadyView
      addInteraction:[[UIContextMenuInteraction alloc] initWithDelegate:self]];

  UIIndirectScribbleInteraction* scribbleInteraction =
      [[UIIndirectScribbleInteraction alloc] initWithDelegate:self];
  [_locationBarSteadyView addInteraction:scribbleInteraction];

  if (!IsComposeboxIOSEnabled()) {
    DCHECK(self.editView) << "The edit view must be set at this point";

    [self.view addSubview:self.editView];
    self.editView.translatesAutoresizingMaskIntoConstraints = NO;
    AddSameConstraints(self.editView, self.view);
  }

  [self.view addSubview:self.locationBarSteadyView];
  self.locationBarSteadyView.translatesAutoresizingMaskIntoConstraints = NO;
  AddSameConstraints(self.locationBarSteadyView, self.view);

  if (self.steadyViewLayoutGuide) {
    [self.view addLayoutGuide:self.steadyViewLayoutGuide];
    AddSameConstraints(self.steadyViewLayoutGuide, self.locationBarSteadyView);
  }

  if (IsGeminiLiveEnabled()) {
    // Use the Gemini Live symbol.
#if BUILDFLAG(IOS_USE_BRANDED_ASSETS)
    UIImage* image =
        SymbolWithPointSize(SymbolGeminiLiveLogo, kGeminiLiveSymbolPointSize);
#else
    UIImage* image =
        SymbolWithPointSize(SymbolWaveform, kGeminiLiveSymbolPointSize);
#endif

    // Create the round container view.
    RoundGeminiLiveView* geminiBadgeContainer =
        [[RoundGeminiLiveView alloc] init];
    geminiBadgeContainer.translatesAutoresizingMaskIntoConstraints = NO;
    geminiBadgeContainer.hidden = YES;

    UIImageView* iconView = [[UIImageView alloc] initWithImage:image];
    iconView.translatesAutoresizingMaskIntoConstraints = NO;

    if (IsChromeNextIaEnabled()) {
      UIView* gradientView = CreateIPHGradientView();
      gradientView.translatesAutoresizingMaskIntoConstraints = NO;
      [geminiBadgeContainer addSubview:gradientView];
      AddSameConstraints(gradientView, geminiBadgeContainer);
      ConfigureIPHImageStyleForImageView(iconView);
    } else {
      geminiBadgeContainer.backgroundColor =
          [UIColor colorNamed:kStaticBlueColor];
      iconView.tintColor = [UIColor whiteColor];
    }

    [geminiBadgeContainer addSubview:iconView];

    // Force width to match height to keep it square/round.
    [NSLayoutConstraint activateConstraints:@[
      [geminiBadgeContainer.widthAnchor
          constraintEqualToConstant:kGeminiLiveCircleSize],
      [geminiBadgeContainer.heightAnchor
          constraintEqualToConstant:kGeminiLiveCircleSize],
      [iconView.centerXAnchor
          constraintEqualToAnchor:geminiBadgeContainer.centerXAnchor],
      [iconView.centerYAnchor
          constraintEqualToAnchor:geminiBadgeContainer.centerYAnchor],
    ]];

    // Inject it directly next to the location label.
    [self.locationBarSteadyView
        addCustomLeadingView:geminiBadgeContainer
                 targetWidth:kGeminiLiveCircleSize
                     spacing:kGeminiLiveIconToLabelSpacing];
  }

  [self updatePlaceholderView];
  [self updateTrailingButtonState];
  [self switchToEditing:NO];

  [self
      registerForTraitChanges:
          @[ UITraitHorizontalSizeClass.class, UITraitVerticalSizeClass.class ]
                   withAction:@selector(updateTrailingButtonState)];

  [self registerForTraitChanges:@[ UITraitHorizontalSizeClass.class ]
                     withAction:@selector(sizeClassDidChange)];

  _defaultSearchEngineIconView = [[UIImageView alloc] init];
  _defaultSearchEngineIconView.translatesAutoresizingMaskIntoConstraints = NO;
  _defaultSearchEngineIconView.contentMode = UIViewContentModeCenter;
  _defaultSearchEngineIconView.image = self.placeholderDefaultSearchEngineIcon;
  if (self.placeholderDefaultSearchEngineIcon) {
    _defaultSearchEngineIconView.accessibilityIdentifier = @"DSEIconNonEmpty";
  }
  AddSizeConstraints(
      _defaultSearchEngineIconView,
      CGSizeMake(kOmniboxLeadingImageSize + 12.0f, kOmniboxLeadingImageSize));

  if (IsProactiveSuggestionsFrameworkEnabled()) {
    _locationBarSteadyView.pageActionMenuHandler = self.pageActionMenuHandler;
  }

  [self updateLayoutGuides];
}

- (void)setActive:(BOOL)active {
  if (_active == active) {
    return;
  }
  _active = active;
  [self updateLayoutGuides];
}

#pragma mark - FullscreenUIElement

- (void)updateForFullscreenProgress:(CGFloat)progress {
  _fullscreenProgress = progress;
  CGFloat alphaValue = fmax((progress - 0.85) / 0.15, 0);
  self.locationBarSteadyView.trailingButton.alpha = alphaValue;
  self.locationBarSteadyView.badgesContainerView.placeholderView.alpha =
      alphaValue;
  if (IsProactiveSuggestionsFrameworkEnabled() &&
      (!self.incognito || IsChromeNextIaEnabled())) {
    self.locationBarSteadyView.badgesContainerView.alpha = alphaValue;
  }
  BOOL badgeViewShouldCollapse = progress <= kFullscreenProgressThreshold;
  [self.locationBarSteadyView
      setFullScreenCollapsedMode:badgeViewShouldCollapse];
  if (!IsGlassToolbarEnabled()) {
    CGFloat scaleValue =
        IsChromeNextIaEnabled()
            ? kFullscreenScaleFactor + (1 - kFullscreenScaleFactor) * progress
            : (0.79 + 0.21 * progress);
    self.locationBarSteadyView.transform =
        CGAffineTransformMakeScale(scaleValue, scaleValue);
  }
  [self updateCustomLeadingViewVisibilityAnimated:YES];
}

- (void)updateForFullscreenEnabled:(BOOL)enabled {
  if (!enabled) {
    [self updateForFullscreenProgress:1.0];
  }
}

- (void)animateFullscreenWithAnimator:(FullscreenAnimator*)animator {
  CGFloat finalProgress = animator.finalProgress;
  [animator addAnimations:^{
    [self updateForFullscreenProgress:finalProgress];
  }];
}

#pragma mark - LocationBarConsumer

- (void)defocusOmnibox {
  [self.dispatcher hideComposebox];
}

- (void)setPlaceholderText:(NSString*)placeholderText {
  if ([_placeholderText isEqualToString:placeholderText]) {
    return;
  }
  _placeholderText = [placeholderText copy];
  if (_isNTP) {
    [self updatePlaceholder];
  }
}

- (void)setPlaceholderDefaultSearchEngineIcon:(UIImage*)icon {
  _placeholderDefaultSearchEngineIcon = icon;
  if (_defaultSearchEngineIconView) {
    _defaultSearchEngineIconView.image = icon;
    if (icon) {
      _defaultSearchEngineIconView.accessibilityIdentifier = @"DSEIconNonEmpty";
    } else {
      _defaultSearchEngineIconView.accessibilityIdentifier = nil;
    }
  }
}

#pragma mark - LocationBarSteadyViewConsumer

- (void)updateLocationText:(NSString*)string clipTail:(BOOL)clipTail {
  [self.locationBarSteadyView setLocationLabelText:string clipTail:clipTail];
}

- (void)updateLocationIcon:(UIImage*)icon
        securityStatusText:(NSString*)statusText {
  [self.locationBarSteadyView setLocationImage:icon];
  self.locationBarSteadyView.securityLevelAccessibilityString = statusText;
}

// Updates display on the NTP. Note that this is only meaningful on iPad, where
// the location bar is visible after scrolling the fakebox off the page. On
// iPhone, the location bar is not shown on the NTP at all.
- (void)updateForNTP:(BOOL)isNTP {
  _isNTP = isNTP;
  if (isNTP) {
    [self updatePlaceholder];
  }
  [self.locationBarSteadyView setCentered:(!isNTP || self.incognito)];
  self.hideShareButtonWhileOnIncognitoNTP = isNTP;
}

- (void)setShareButtonEnabled:(BOOL)enabled {
  _shareButtonEnabled = enabled;
  if (self.trailingButtonState == kShareButton) {
    [self.locationBarSteadyView enableTrailingButton:enabled];

    if (_shareButtonEnabled) {
      [self.layoutGuideCenter
          referenceView:self.locationBarSteadyView.trailingButton
              underName:kShareButtonGuide];
    }
  }
}

- (BOOL)canShowLargeContextualPanelEntrypoint {
  // TODO(crbug.com/445786272): Add actual checks when implementing badge view
  // loud moment blocking (check might need to be in the actual view).
  return !self.locationBarSteadyView.hidden;
}

- (void)setLocationBarLabelCenteredBetweenContent:(BOOL)centered {
  [self.locationBarSteadyView
      setLocationBarLabelCenteredBetweenContent:centered];
}

- (void)attemptShowingLensOverlayIPH {
  if (IsLensOverlayAllowedByPolicy(_profilePrefs) &&
      !IsPageActionMenuEnabled() &&
      !self.locationBarSteadyView.badgesContainerView.placeholderView.hidden) {
    [self.helpCommandsHandler
        presentInProductHelpWithType:InProductHelpType::kLensOverlayEntrypoint];
  }
}

- (void)recordLensOverlayAvailability {
  // Record lens overlay placeholder available.
  if (_placeholderType == LocationBarPlaceholderType::kLensOverlay) {
    RecordLensEntrypointAvailable();
  }
}

- (void)focusSteadyViewForVoiceOver {
  UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
                                  self.locationBarSteadyView);
}

#pragma mark - LocationBarAnimatee

- (void)offsetTextFieldToMatchSteadyView {
  CGAffineTransform offsetTransform =
      CGAffineTransformMakeTranslation([self targetOffset], 0);
  self.textField.transform = offsetTransform;
}

- (void)resetTextFieldOffsetAndOffsetSteadyViewToMatch {
  self.locationBarSteadyView.transform =
      CGAffineTransformMakeTranslation(-self.textField.transform.tx, 0);
  self.textField.transform = CGAffineTransformIdentity;
}

- (void)offsetSteadyViewToMatchTextField {
  CGAffineTransform offsetTransform =
      CGAffineTransformMakeTranslation(-[self targetOffset], 0);
  self.locationBarSteadyView.transform = offsetTransform;
}

- (void)resetSteadyViewOffsetAndOffsetTextFieldToMatch {
  self.textField.transform = CGAffineTransformMakeTranslation(
      -self.locationBarSteadyView.transform.tx, 0);
  self.locationBarSteadyView.transform = CGAffineTransformIdentity;
}

- (void)setSteadyViewFaded:(BOOL)hidden {
  self.locationBarSteadyView.alpha = hidden ? 0 : 1;
}

- (void)hideSteadyViewBadgeAndEntrypointViews {
  self.locationBarSteadyView.badgesContainerView.hidden = YES;
}

- (void)showSteadyViewBadgeAndEntrypointViews {
  self.locationBarSteadyView.badgesContainerView.hidden = NO;
}

- (void)setEditViewFaded:(BOOL)hidden {
  self.editView.alpha = hidden ? 0 : 1;
}

- (void)setEditViewHidden:(BOOL)hidden {
  self.editView.hidden = hidden;
}
- (void)setSteadyViewHidden:(BOOL)hidden {
  self.locationBarSteadyView.hidden = hidden;
}

- (void)resetTransforms {
  // Focus/defocus animations only affect translations and not scale. So reset
  // translation and keep the scale.
  self.textField.transform = CGAffineTransformMake(
      self.textField.transform.a, self.textField.transform.b,
      self.textField.transform.c, self.textField.transform.d, 0, 0);
  self.locationBarSteadyView.transform =
      CGAffineTransformMake(self.locationBarSteadyView.transform.a,
                            self.locationBarSteadyView.transform.b,
                            self.locationBarSteadyView.transform.c,
                            self.locationBarSteadyView.transform.d, 0, 0);
}

- (void)addFakeboxButtonsSnapshot {
  _fakeboxButtonsSnapshot =
      [self.fakeboxButtonsSnapshotProvider fakeboxButtonsSnapshot];
  if (!_fakeboxButtonsSnapshot) {
    return;
  }

  _fakeboxButtonsSnapshot.translatesAutoresizingMaskIntoConstraints = NO;
  _fakeboxButtonsSnapshot.alpha = 0;
  [self.view addSubview:_fakeboxButtonsSnapshot];
  CGSize size = _fakeboxButtonsSnapshot.frame.size;
  [NSLayoutConstraint activateConstraints:@[
    [_fakeboxButtonsSnapshot.widthAnchor constraintEqualToConstant:size.width],
    [_fakeboxButtonsSnapshot.heightAnchor
        constraintEqualToConstant:size.height],
    [_fakeboxButtonsSnapshot.centerYAnchor
        constraintEqualToAnchor:self.view.centerYAnchor],
    [_fakeboxButtonsSnapshot.trailingAnchor
        constraintEqualToAnchor:self.view.trailingAnchor],
  ]];
  [self.view layoutIfNeeded];
}

- (void)setFakeboxButtonsSnapshotFaded:(BOOL)faded {
  _fakeboxButtonsSnapshot.alpha = faded ? 0 : 1;
}

- (void)clearFakeboxButtonsSnapshot {
  [_fakeboxButtonsSnapshot removeFromSuperview];
  _fakeboxButtonsSnapshot = nil;
}

#pragma mark animation helpers

// Computes the target offset for the focus/defocus animation that allows to
// visually match the position of edit and steady views.
- (CGFloat)targetOffset {
  CGFloat offset =
      _isNTP
          ? kOmniboxEditOffset
          : [self.offsetProvider
                xOffsetForString:self.locationBarSteadyView.locationLabel.text];

  CGRect labelRect = [self.view
      convertRect:self.locationBarSteadyView.locationLabel.frame
         fromView:self.locationBarSteadyView.locationLabel.superview];
  CGRect textFieldRect = self.editView.frame;

  CGFloat targetOffset = labelRect.origin.x - textFieldRect.origin.x - offset;
  return targetOffset;
}

#pragma mark - UIIndirectScribbleInteractionDelegate

- (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction*)interaction
              requestElementsInRect:(CGRect)rect
                         completion:
                             (void (^)(NSArray<UIScribbleElementIdentifier>*
                                           elements))completion {
  completion(@[ kScribbleOmniboxElementId ]);
}

- (BOOL)indirectScribbleInteraction:(UIIndirectScribbleInteraction*)interaction
                   isElementFocused:
                       (UIScribbleElementIdentifier)elementIdentifier {
  DCHECK(elementIdentifier == kScribbleOmniboxElementId);
  return self.delegate.omniboxScribbleForwardingTarget.isFirstResponder;
}

- (CGRect)
    indirectScribbleInteraction:(UIIndirectScribbleInteraction*)interaction
                frameForElement:(UIScribbleElementIdentifier)elementIdentifier {
  DCHECK(elementIdentifier == kScribbleOmniboxElementId);

  // Imitate the entire location bar being scribblable.
  return self.view.bounds;
}

- (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction*)interaction
               focusElementIfNeeded:
                   (UIScribbleElementIdentifier)elementIdentifier
                     referencePoint:(CGPoint)focusReferencePoint
                         completion:
                             (void (^)(UIResponder<UITextInput>* focusedInput))
                                 completion {
  if (!self.delegate.omniboxScribbleForwardingTarget.isFirstResponder) {
    [self.delegate locationBarRequestScribbleTargetFocus];
  }

  completion(self.delegate.omniboxScribbleForwardingTarget);
}

#pragma mark - private

- (void)locationBarSteadyViewTapped {
  base::RecordAction(base::UserMetricsAction("MobileLocationBarTapped"));
  TriggerHapticFeedbackForSelectionChange();
  [self.delegate locationBarSteadyViewTapped];
}

- (void)updateTrailingButton {
  // Stop constraining the voice guide to the trailing button if transitioning
  // from kVoiceSearchButton.
  UIView* referencedView =
      [self.layoutGuideCenter referencedViewUnderName:kVoiceSearchButtonGuide];
  if (referencedView == self.locationBarSteadyView.trailingButton) {
    [self.layoutGuideCenter referenceView:nil
                                underName:kVoiceSearchButtonGuide];
  }

  // Cancel previous possible state.
  [self.locationBarSteadyView.trailingButton
          removeTarget:nil
                action:nil
      forControlEvents:UIControlEventAllEvents];
  [self.locationBarSteadyView setTrailingButtonHidden:NO];

  TrailingButtonState state = self.trailingButtonState;
  if (state == kShareButton && self.hideShareButtonWhileOnIncognitoNTP) {
    state = kNoButton;
  }

  switch (state) {
    case kNoButton: {
      [self.locationBarSteadyView setTrailingButtonHidden:YES];
      break;
    };
    case kShareButton: {
      [self.locationBarSteadyView.trailingButton
                 addTarget:self.dispatcher
                    action:@selector(showShareSheetFromShareButton:)
          forControlEvents:UIControlEventTouchUpInside];

      // Add self as a target to collect the metrics.
      [self.locationBarSteadyView.trailingButton
                 addTarget:self
                    action:@selector(shareButtonPressed)
          forControlEvents:UIControlEventTouchUpInside];

      // The system share image has uneven vertical padding. Add a small bottom
      // padding to balance it.
      UIImage* shareImage =
          SymbolWithPointSize(SymbolShare, kSymbolImagePointSize);

      UIGraphicsImageRendererFormat* format =
          [UIGraphicsImageRendererFormat preferredFormat];
      format.scale = 0.0;
      format.opaque = NO;
      UIGraphicsImageRenderer* renderer = [[UIGraphicsImageRenderer alloc]
          initWithSize:CGSizeMake(shareImage.size.width,
                                  shareImage.size.height +
                                      kShareIconBalancingHeightPadding)
                format:format];
      UIImage* paddedShareImage = [renderer
          imageWithActions:^(UIGraphicsImageRendererContext* context) {
            [shareImage drawInRect:CGRectMake(0, 0, shareImage.size.width,
                                              shareImage.size.height)];
          }];

      [self.locationBarSteadyView.trailingButton setImage:paddedShareImage
                                                 forState:UIControlStateNormal];
      self.locationBarSteadyView.trailingButton.accessibilityLabel =
          l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SHARE);
      self.locationBarSteadyView.trailingButton.accessibilityIdentifier =
          kOmniboxShareButtonIdentifier;
      [self.locationBarSteadyView enableTrailingButton:self.shareButtonEnabled];

      if (self.shareButtonEnabled) {
        [self.layoutGuideCenter
            referenceView:self.locationBarSteadyView.trailingButton
                underName:kShareButtonGuide];
      }
      break;
    };
    case kVoiceSearchButton: {
      [self.locationBarSteadyView.trailingButton
                 addTarget:self.dispatcher
                    action:@selector(preloadVoiceSearch)
          forControlEvents:UIControlEventTouchDown];
      [self.locationBarSteadyView.trailingButton
                 addTarget:self
                    action:@selector(startVoiceSearch)
          forControlEvents:UIControlEventTouchUpInside];

      UIImage* micImage =
          SymbolWithPointSize(SymbolMicrophone, kSymbolImagePointSize);
      [self.locationBarSteadyView.trailingButton setImage:micImage
                                                 forState:UIControlStateNormal];
      self.locationBarSteadyView.trailingButton.accessibilityLabel =
          l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_VOICE_SEARCH);
      self.locationBarSteadyView.trailingButton.accessibilityIdentifier =
          kOmniboxVoiceSearchButtonIdentifier;
      self.locationBarSteadyView.trailingButton.layer.cornerRadius =
          self.locationBarSteadyView.trailingButton.frame.size.width / 2;
      self.locationBarSteadyView.trailingButton.clipsToBounds = YES;

      [self.locationBarSteadyView enableTrailingButton:self.voiceSearchEnabled];
    }
  }
}

- (void)setTrailingButtonState:(TrailingButtonState)state {
  if (IsChromeNextIaEnabled() && !IsChromeNextIaShareIconVisible() &&
      state == kShareButton) {
    state = kNoButton;
  }

  if (_trailingButtonState == state) {
    return;
  }
  _trailingButtonState = state;

  if (state == kShareButton) {
    base::UmaHistogramEnumeration("Mobile.ShareThisPage.Shown",
                                  ShareThisPageLocation::kLocationBar);
  }

  [self updateTrailingButton];
}

- (void)startVoiceSearch {
  [self.layoutGuideCenter
      referenceView:self.locationBarSteadyView.trailingButton
          underName:kVoiceSearchButtonGuide];
  base::RecordAction(base::UserMetricsAction("MobileOmniboxVoiceSearch"));
  [self.dispatcher startVoiceSearch];
}

// Called when the share button is pressed.
// The actual share dialog is opened by the dispatcher, only collect the metrics
// here.
- (void)shareButtonPressed {
  RecordAction(UserMetricsAction("MobileToolbarShareMenu"));
  base::UmaHistogramEnumeration("Mobile.ShareThisPage.Used",
                                ShareThisPageLocation::kLocationBar);
  [self.delegate recordShareButtonPressed];
}

- (BOOL)shouldUseLensInLongPressMenu {
  return ios::provider::IsLensSupported() &&
         base::FeatureList::IsEnabled(kEnableLensInOmniboxCopiedImage) &&
         self.lensImageEnabled;
}

// Updates placeholder in the steady view.
- (void)updatePlaceholder {
  [self.locationBarSteadyView
      setLocationLabelPlaceholderText:self.placeholderText];
}

- (void)createAIMPlusButton {
  _plusButton = [ExtendedTouchTargetButton buttonWithType:UIButtonTypeSystem];
  [_plusButton setImage:SymbolWithPointSize(SymbolPlus, kSymbolActionPointSize)
               forState:UIControlStateNormal];
  _plusButton.translatesAutoresizingMaskIntoConstraints = NO;
  _plusButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  _plusButton.tintColor = [UIColor colorNamed:kToolbarButtonColor];
  _plusButton.accessibilityLabel = l10n_util::GetNSString(
      IDS_IOS_COMPOSEBOX_ADD_ATTACHMENT_BUTTON_ACCESSIBILITY_LABEL);
  [NSLayoutConstraint activateConstraints:@[
    [_plusButton.widthAnchor
        constraintGreaterThanOrEqualToConstant:kPlusButtonMinimumWidth]
  ]];

  [_plusButton addTarget:self
                  action:@selector(handlePlusButtonPressed)
        forControlEvents:UIControlEventTouchUpInside];
}

// Updates the layout guides to point to the entrypoints in this toolbar.
- (void)updateLayoutGuides {
  if (!self.isViewLoaded) {
    return;
  }
  // The _active flag is only used when NextIA is enabled. When it is disabled,
  // the location bar should always be treated as active for layout guides.
  BOOL isActive = _active || !IsChromeNextIaEnabled();
  if (isActive) {
    if (self.readerModeChipView) {
      [self.layoutGuideCenter referenceView:self.readerModeChipView
                                  underName:kReaderModeOptionsEntrypointGuide];
    }
    if (_pageActionMenuEntrypointView) {
      [self.layoutGuideCenter referenceView:_pageActionMenuEntrypointView
                                  underName:kPageActionMenuEntrypointGuide];
    }
    if (IsLensOverlayAllowedByPolicy(_profilePrefs)) {
      [self.layoutGuideCenter referenceView:_lensOverlayPlaceholderView
                                  underName:kLensOverlayEntrypointGuide];
    }
  } else {
    if (self.readerModeChipView &&
        [self.layoutGuideCenter
            referencedViewUnderName:kReaderModeOptionsEntrypointGuide] ==
            self.readerModeChipView) {
      [self.layoutGuideCenter referenceView:nil
                                  underName:kReaderModeOptionsEntrypointGuide];
    }
    if (_pageActionMenuEntrypointView &&
        [self.layoutGuideCenter
            referencedViewUnderName:kPageActionMenuEntrypointGuide] ==
            _pageActionMenuEntrypointView) {
      [self.layoutGuideCenter referenceView:nil
                                  underName:kPageActionMenuEntrypointGuide];
    }
    if (IsLensOverlayAllowedByPolicy(_profilePrefs) &&
        [self.layoutGuideCenter
            referencedViewUnderName:kLensOverlayEntrypointGuide] ==
            _lensOverlayPlaceholderView) {
      [self.layoutGuideCenter referenceView:nil
                                  underName:kLensOverlayEntrypointGuide];
    }
  }
}

#pragma mark - UIContextMenuInteractionDelegate

- (UIMenu*)contextMenuUIMenu:(NSArray<UIMenuElement*>*)suggestedActions {
  NSMutableArray<UIMenuElement*>* menuElements = [[NSMutableArray alloc] init];
  __weak __typeof__(self) weakSelf = self;

  if (IsChromeNextIaEnabled() && !IsChromeNextIaShareIconVisible() &&
      self.shareButtonEnabled) {
    base::UmaHistogramEnumeration("Mobile.ShareThisPage.Shown",
                                  ShareThisPageLocation::kOmniboxLongPress);
    UIImage* image = SymbolWithPointSize(SymbolShare, kSymbolImagePointSize);

    UIAction* shareThisPageAction =
        [UIAction actionWithTitle:l10n_util::GetNSString(
                                      IDS_IOS_TOOLS_MENU_SHARE_THIS_PAGE)
                            image:image
                       identifier:nil
                          handler:^(UIAction* action) {
                            [weakSelf shareThisPage];
                          }];

    UIMenu* divider = [UIMenu menuWithTitle:@""
                                      image:nil
                                 identifier:nil
                                    options:UIMenuOptionsDisplayInline
                                   children:@[ shareThisPageAction ]];
    [menuElements addObject:divider];
  }

  UIImage* pasteImage = nil;
  if (IsBottomOmniboxAvailable()) {
    pasteImage = SymbolWithPointSize(SymbolPasteAction, kSymbolActionPointSize);

    // Copy link action.
    BOOL canShowCopyLinkAction = NO;
    if (IsChromeNextIaEnabled()) {
      canShowCopyLinkAction =
          !self.locationBarSteadyView.isHidden && !(_isNTP && self.incognito);
    } else {
      canShowCopyLinkAction = !self.locationBarSteadyView.isHidden;
    }

    if (canShowCopyLinkAction) {
      UIAction* copyAction = [UIAction
          actionWithTitle:l10n_util::GetNSString(IDS_IOS_COPY_LINK_ACTION_TITLE)
                    image:SymbolWithPointSize(SymbolCopyAction,
                                              kSymbolActionPointSize)
               identifier:nil
                  handler:^(UIAction* action) {
                    [weakSelf.delegate locationBarCopyTapped];
                  }];
      [menuElements addObject:copyAction];
    }
  } else {
    // Keep the suggested actions to have the copy action in a separate section.
    [menuElements addObjectsFromArray:suggestedActions];
  }

  if ([self.delegate locationBarCanSendTabToSelf]) {
    ActionFactory* actionFactory = [[ActionFactory alloc]
        initWithScenario:kMenuScenarioHistogramToolbarMenu];
    UIAction* sendTabAction = [actionFactory actionToSendTabToSelfWithBlock:^{
      dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.delegate locationBarSendTabToSelfTapped];
      });
    }];
    [menuElements addObject:sendTabAction];
  }

  std::optional<std::set<ClipboardContentType>> clipboard_content_types =
      ClipboardRecentContent::GetInstance()->GetCachedClipboardContentTypes();

  if (clipboard_content_types.has_value()) {
    std::set<ClipboardContentType> clipboard_content_types_values =
        clipboard_content_types.value();
    if (clipboard_content_types_values.contains(ClipboardContentType::Image)) {
      // Either add an option to search the copied image with Lens, or via the
      // default search engine's reverse image search functionality.
      if (self.shouldUseLensInLongPressMenu) {
        id lensCopiedImageHandler = ^(UIAction* action) {
          [weakSelf lensCopiedImage:nil];
        };
        UIAction* lensCopiedImageAction = [UIAction
            actionWithTitle:l10n_util::GetNSString(
                                (IDS_IOS_SEARCH_COPIED_IMAGE_WITH_LENS))
                      image:pasteImage
                 identifier:nil
                    handler:lensCopiedImageHandler];
        [menuElements addObject:lensCopiedImageAction];
      } else {
        id searchCopiedImageHandler = ^(UIAction* action) {
          [weakSelf searchCopiedImage:nil];
        };
        UIAction* searchCopiedImageAction =
            [UIAction actionWithTitle:l10n_util::GetNSString(
                                          (IDS_IOS_SEARCH_COPIED_IMAGE))
                                image:pasteImage
                           identifier:nil
                              handler:searchCopiedImageHandler];
        [menuElements addObject:searchCopiedImageAction];
      }
    } else if (clipboard_content_types_values.contains(
                   ClipboardContentType::URL)) {
      id visitCopiedLinkHandler = ^(UIAction* action) {
        [self visitCopiedLink:nil];
      };
      UIAction* visitCopiedLinkAction = [UIAction
          actionWithTitle:l10n_util::GetNSString((IDS_IOS_VISIT_COPIED_LINK))
                    image:pasteImage
               identifier:nil
                  handler:visitCopiedLinkHandler];
      [menuElements addObject:visitCopiedLinkAction];
    } else if (clipboard_content_types_values.contains(
                   ClipboardContentType::Text)) {
      id searchCopiedTextHandler = ^(UIAction* action) {
        [self searchCopiedText:nil];
      };
      UIAction* searchCopiedTextAction = [UIAction
          actionWithTitle:l10n_util::GetNSString((IDS_IOS_SEARCH_COPIED_TEXT))
                    image:pasteImage
               identifier:nil
                  handler:searchCopiedTextHandler];
      [menuElements addObject:searchCopiedTextAction];
    }
  }

  // Used to easily trigger the Assistant sheet during development.
  if (IsAssistantContainerDebugEnabled()) {
    UIAction* assistantAction = [UIAction
        actionWithTitle:l10n_util::GetNSString(IDS_IOS_APP_BAR_ASK_GEMINI)
                  image:SymbolWithPointSize(SymbolMagicStack,
                                            kSymbolActionPointSize)
             identifier:nil
                handler:^(UIAction* action) {
                  [weakSelf.dispatcher showAssistant];
                }];
    [menuElements addObject:assistantAction];
  }

  // Show Top or Bottom Address Bar action.
  BOOL canShowMoveAddressBarAction = NO;
  if (IsChromeNextIaEnabled()) {
    canShowMoveAddressBarAction = IsBottomOmniboxAvailable();
  } else {
    canShowMoveAddressBarAction =
        IsBottomOmniboxAvailable() && IsSplitToolbarMode(self);
  }

  if (canShowMoveAddressBarAction) {
    NSString* title = nil;
    UIImage* image = nil;
    ToolbarType targetToolbarType;
    if (GetApplicationContext()->GetLocalState()->GetBoolean(
            omnibox::kIsOmniboxInBottomPosition)) {
      title = l10n_util::GetNSString(IDS_IOS_TOOLBAR_MENU_TOP_OMNIBOX);
      image = SymbolWithPointSize(SymbolMovePlatterToTopPhone,
                                  kSymbolActionPointSize);
      targetToolbarType = ToolbarType::kPrimary;
    } else {
      title = l10n_util::GetNSString(IDS_IOS_TOOLBAR_MENU_BOTTOM_OMNIBOX);
      image = SymbolWithPointSize(SymbolMovePlatterToBottomPhone,
                                  kSymbolActionPointSize);
      targetToolbarType = ToolbarType::kSecondary;
    }
    UIAction* moveAddressBarAction = [UIAction
        actionWithTitle:title
                  image:image
             identifier:nil
                handler:^(UIAction* action) {
                  [weakSelf moveOmniboxToToolbarType:targetToolbarType];
                }];

    UIMenu* divider = [UIMenu menuWithTitle:@""
                                      image:nil
                                 identifier:nil
                                    options:UIMenuOptionsDisplayInline
                                   children:@[ moveAddressBarAction ]];
    [menuElements addObject:divider];
  }

  if (IsHideToolbarEnabled()) {
    UIImage* image = SymbolWithPointSize(SymbolExpand, kSymbolActionPointSize);

    UIAction* hideAddressBarAction =
        [UIAction actionWithTitle:l10n_util::GetNSString(
                                      IDS_IOS_OVERFLOW_MENU_HIDE_TOOLBARS)
                            image:image
                       identifier:nil
                          handler:^(UIAction* action) {
                            [weakSelf hideToolbars];
                          }];

    UIMenu* divider = [UIMenu menuWithTitle:@""
                                      image:nil
                                 identifier:nil
                                    options:UIMenuOptionsDisplayInline
                                   children:@[ hideAddressBarAction ]];
    [menuElements addObject:divider];
  }

  return [UIMenu menuWithTitle:@"" children:menuElements];
}

- (UITargetedPreview*)contextMenuInteraction:
                          (UIContextMenuInteraction*)interaction
    previewForHighlightingMenuWithConfiguration:
        (UIContextMenuConfiguration*)configuration {
  // Use the location bar's container view because that's the view that has the
  // background color and corner radius.
  UIPreviewParameters* previewParameters = [[UIPreviewParameters alloc] init];
  previewParameters.backgroundColor = self.view.superview.backgroundColor;
  return [[UITargetedPreview alloc] initWithView:self.view.superview
                                      parameters:previewParameters];
}

- (UIContextMenuConfiguration*)contextMenuInteraction:
                                   (UIContextMenuInteraction*)interaction
                       configurationForMenuAtLocation:(CGPoint)location {
  __weak LocationBarViewController* weakSelf = self;

  UIContextMenuConfiguration* configuration = [UIContextMenuConfiguration
      configurationWithIdentifier:nil
                  previewProvider:nil
                   actionProvider:^UIMenu*(
                       NSArray<UIMenuElement*>* suggestedActions) {
                     return [weakSelf contextMenuUIMenu:suggestedActions];
                   }];

  if (IsBottomOmniboxAvailable()) {
    configuration.preferredMenuElementOrder =
        UIContextMenuConfigurationElementOrderPriority;
  }
  return configuration;
}

- (void)contextMenuInteraction:(UIContextMenuInteraction*)interaction
    willDisplayMenuForConfiguration:(UIContextMenuConfiguration*)configuration
                           animator:
                               (id<UIContextMenuInteractionAnimating>)animator {
  if (IsPageActionMenuEnabled()) {
    [self.geminiHandler
        hideFloatyIfInvokedAnimated:YES
                         fromSource:gemini::FloatyUpdateSource::ContextMenu];
  }
}

- (void)contextMenuInteraction:(UIContextMenuInteraction*)interaction
       willEndForConfiguration:(UIContextMenuConfiguration*)configuration
                      animator:(id<UIContextMenuInteractionAnimating>)animator {
  self.activeContextMenuAnimator = animator;
  __weak LocationBarViewController* weakSelf = self;

  if (IsPageActionMenuEnabled()) {
    [animator addAnimations:^{
      [weakSelf.geminiHandler
          updateFloatyVisibilityIfEligibleAnimated:NO
                                        fromSource:gemini::FloatyUpdateSource::
                                                       ContextMenu];
    }];
  }

  [animator addCompletion:^{
    weakSelf.activeContextMenuAnimator = nil;
  }];
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
  // Allow copying if the steady location bar is visible.
  if (!self.locationBarSteadyView.hidden && action == @selector(copy:)) {
    return YES;
  }

  return NO;
}

- (void)copy:(id)sender {
  [self.delegate locationBarCopyTapped];
}

- (void)searchCopiedImage:(id)sender {
  RecordAction(
      UserMetricsAction("Mobile.OmniboxContextMenu.SearchCopiedImage"));
  [self.delegate searchCopiedImage];
}

- (void)lensCopiedImage:(id)sender {
  RecordAction(UserMetricsAction("Mobile.OmniboxContextMenu.LensCopiedImage"));
  [self.delegate lensCopiedImage];
}

- (void)visitCopiedLink:(id)sender {
  // A search using clipboard link is activity that should indicate a user
  // that would be interested in setting Chrome as the default browser.
  [self.delegate locationBarVisitCopyLinkTapped];
  RecordAction(UserMetricsAction("Mobile.OmniboxContextMenu.VisitCopiedLink"));
  ClipboardRecentContent::GetInstance()->GetRecentURLFromClipboard(
      base::BindOnce(^(std::optional<GURL> optionalURL) {
        if (!optionalURL) {
          return;
        }
        NSString* url = base::SysUTF8ToNSString(optionalURL.value().spec());
        dispatch_async(dispatch_get_main_queue(), ^{
          [self.mutator loadQuery:url];
          [self.dispatcher hideComposebox];
        });
      }));
}

- (void)searchCopiedText:(id)sender {
  // A search using clipboard text is activity that should indicate a user
  // that would be interested in setting Chrome as the default browser.
  [self.delegate locationBarSearchCopiedTextTapped];
  RecordAction(UserMetricsAction("Mobile.OmniboxContextMenu.SearchCopiedText"));
  ClipboardRecentContent::GetInstance()->GetRecentTextFromClipboard(
      base::BindOnce(^(std::optional<std::u16string> optionalText) {
        if (!optionalText) {
          return;
        }
        NSString* query = base::SysUTF16ToNSString(optionalText.value());
        dispatch_async(dispatch_get_main_queue(), ^{
          [self.mutator loadQuery:query];
          [self.dispatcher hideComposebox];
        });
      }));
}

/// Shows the Share this page sheet.
- (void)shareThisPage {
  base::UmaHistogramEnumeration("Mobile.ShareThisPage.Used",
                                ShareThisPageLocation::kOmniboxLongPress);
  [self.dispatcher showShareSheetFromShareButton:_locationBarSteadyView];
}

/// Set the preferred omnibox position to `toolbarType`.
- (void)moveOmniboxToToolbarType:(ToolbarType)toolbarType {
  GetApplicationContext()->GetLocalState()->SetBoolean(
      omnibox::kIsOmniboxInBottomPosition,
      toolbarType == ToolbarType::kSecondary);

  if (toolbarType == ToolbarType::kPrimary) {
    RecordAction(
        UserMetricsAction("Mobile.OmniboxContextMenu.MoveAddressBarToTop"));
  } else {
    RecordAction(
        UserMetricsAction("Mobile.OmniboxContextMenu.MoveAddressBarToBottom"));
  }
}

- (void)hideToolbars {
  [self.delegate locationBarHideToolbarTapped];
}

- (void)handlePlusButtonPressed {
  RecordAction(UserMetricsAction("MobileToolbarPlusButtonTap"));
  TriggerHapticFeedbackForSelectionChange();
  [self.dispatcher showMultimodalActionsMenu];
}

- (void)handleLensEntrypointPressed {
  RecordAction(UserMetricsAction("MobileToolbarLensOverlayTap"));
  if (self.lensOverlayVisible) {
    [self destroyLensOverlay];
  } else {
    [self openLensOverlay];
  }
}

- (void)handlePageActionMenuEntrypointTapped {
  if (_pageActionMenuEntrypointView.newBadgeVisible) {
    RecordAIHubNewBadgeTapped();
    [self.delegate locationBarDidTapAIHubNewBadge];
    _pageActionMenuEntrypointView.newBadgeVisible = NO;
  }
  if (IsDirectBWGEntryPoint()) {
    [self.geminiHandler
        startGeminiEntryFlowWithStartupState:
            [[GeminiStartupState alloc]
                initWithEntryPoint:gemini::EntryPoint::DirectOmniboxBadge]
                          baseViewController:self
                    showSnackbarOnCompletion:YES
                                  completion:nil];
  } else {
    RecordAIHubIconTapped();
    [self.pageActionMenuHandler showPageActionMenu];
  }
}

- (void)updateAIHubNewBadgeVisibility {
  if (_placeholderType == LocationBarPlaceholderType::kPageActionMenu) {
    [self updatePlaceholderView];
  }
}

// Creates and shows the LVF input selection UI.
- (void)openLensViewFinder {
  TriggerHapticFeedbackForSelectionChange();
  OpenLensInputSelectionCommand* command = [[OpenLensInputSelectionCommand
      alloc]
          initWithEntryPoint:LensEntrypoint::LensOverlayLocationBar
           presentationStyle:LensInputSelectionPresentationStyle::SlideFromRight
      presentationCompletion:nil];
  [self.dispatcher openLensInputSelection:command];
}

// Creates and shows the lens overlay UI.
- (void)openLensOverlay {
  // TODO(crbug.com/427478234): This event should be fired by the mediator.
  if (self.tracker) {
    self.tracker->NotifyEvent(
        feature_engagement::events::kLensOverlayEntrypointUsed);
  }
  TriggerHapticFeedbackForSelectionChange();
  [self.dispatcher createAndShowLensUI:YES
                            entrypoint:LensOverlayEntrypoint::kLocationBar
                            completion:nil];
}

// Creates and shows the lens overlay UI.
- (void)destroyLensOverlay {
  TriggerHapticFeedbackForSelectionChange();
  [self.dispatcher destroyLensUI:YES
                          reason:lens::LensOverlayDismissalSource::kToolbar];
}

- (void)updatePlaceholderView {
  switch (_placeholderType) {
    case LocationBarPlaceholderType::kNone:
      [self.locationBarSteadyView setPlaceholderView:nil type:_placeholderType];
      break;
    case LocationBarPlaceholderType::kPlusButton:
      [self.locationBarSteadyView setPlaceholderView:_plusButton
                                                type:_placeholderType];
      break;
    case LocationBarPlaceholderType::kLensOverlay:
      [self.locationBarSteadyView setPlaceholderView:_lensOverlayPlaceholderView
                                                type:_placeholderType];
      break;
    case LocationBarPlaceholderType::kPageActionMenu:
      CHECK(IsPageActionMenuEnabled());
      [self.locationBarSteadyView
          setPlaceholderView:_pageActionMenuEntrypointView
                        type:_placeholderType];
      if (!_pageActionMenuEntrypointView.newBadgeVisible) {
        _pageActionMenuEntrypointView.newBadgeVisible =
            [self.delegate shouldShowAIHubNewFeatureBadge];
      }
      break;
    case LocationBarPlaceholderType::kDefaultSearchEngineIcon:
      [self.locationBarSteadyView
          setPlaceholderView:_defaultSearchEngineIconView
                        type:_placeholderType];
      break;
  }
}

- (void)sizeClassDidChange {
  [self updateLensVisibilityIndicationIfNeeded];
}

- (void)setLensOverlayVisible:(BOOL)lensOverlayVisible {
  if (lensOverlayVisible == _lensOverlayVisible) {
    return;
  }

  _lensOverlayVisible = lensOverlayVisible;
  [self updateLensVisibilityIndicationIfNeeded];
}

- (void)updateLensVisibilityIndicationIfNeeded {
  // Only indicate Lens Overlay in use when the presentation does not cover the
  // location bar.
  BOOL shouldIndicateLensInUse =
      lens::ContainerPresentationFor(self) !=
      lens::ContainerPresentationType::kFullscreenCover;
  [_lensOverlayPlaceholderView
      setLensOverlayActive:shouldIndicateLensInUse && _lensOverlayVisible];
}

- (UIView*)locationBarSteadyViewVisualCopy {
  LocationBarSteadyView* copyView = [[LocationBarSteadyView alloc] init];
  copyView.locationLabel.text = self.locationBarSteadyView.locationLabel.text;
  copyView.locationLabel.textColor =
      self.locationBarSteadyView.locationLabel.textColor;
  UIImage* trailingButtonImage =
      self.locationBarSteadyView.trailingButton.currentImage;
  [copyView.trailingButton setImage:trailingButtonImage
                           forState:UIControlStateNormal];
  copyView.colorScheme = self.locationBarSteadyView.colorScheme;
  copyView.frame = self.locationBarSteadyView.frame;

  return copyView;
}

- (void)setCustomLeadingViewType:(CustomLeadingViewType)type {
  if (_customLeadingViewType == type) {
    return;
  }
  _customLeadingViewType = type;
  [self updateCustomLeadingViewVisibilityAnimated:YES];
}

- (void)updateCustomLeadingViewVisibilityAnimated:(BOOL)animated {
  // Only show the custom leading view when the location bar is in fullscreen.
  BOOL visible = (_customLeadingViewType != CustomLeadingViewType::kNone) &&
                 (_fullscreenProgress < 0.1);
  [self.locationBarSteadyView updateCustomLeadingViewVisibility:visible
                                                       animated:animated];
}

@end
