chromium/chrome/browser/ui/views/autofill/popup/popup_view_utils.h

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

#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_VIEW_UTILS_H_
#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_VIEW_UTILS_H_

#include "base/containers/span.h"
#include "components/autofill/core/browser/ui/popup_open_enums.h"
#include "components/autofill/core/browser/ui/suggestion_type.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_border_arrow_utils.h"
#include "ui/views/style/typography.h"

namespace content {
class WebContents;
}  // namespace content

namespace autofill {

// Specifies how the popup cell was selected.
enum PopupCellSelectionSource {};

// Sets the `y` and `height` components of `popup_bounds` as the y-coordinate
// of the starting point and the height of the popup, taking into account the
// direction it's supposed to grow (either up or down). Components `x` and
// `width` of `popup_bounds` are not changed.
void CalculatePopupYAndHeight(int popup_preferred_height,
                              const gfx::Rect& content_area_bounds,
                              const gfx::Rect& element_bounds,
                              gfx::Rect* popup_bounds);

// Returns whether there is enough height within `content_area_bounds` above or
// below `element_bounds` to display `item_height`, and that the first dropdown
// item will actually be visible within the bounds of the content area.
bool CanShowDropdownHere(int item_height,
                         const gfx::Rect& content_area_bounds,
                         const gfx::Rect& element_bounds);

// Returns whether there is any open prompt in `web_contents` with bounds that
// overlap `screen_bounds`.
// This is unreliable on Windows because bubbles are not necessarily children of
// the root view of the current tab.
bool BoundsOverlapWithAnyOpenPrompt(const gfx::Rect& screen_bounds,
                                    content::WebContents* web_contents);

// Returns the total vertical space on `content_area_bounds` on a specific
// `side` of the `element_bounds`.
int GetAvailableVerticalSpaceOnSideOfElement(
    const gfx::Rect& content_area_bounds,
    const gfx::Rect& element_bounds,
    views::BubbleArrowSide side);

// Returns the total horizontal space on `content_area_bounds` on a
// specific `side` of the `element_bounds`.
int GetAvailableHorizontalSpaceOnSideOfElement(
    const gfx::Rect& content_area_bounds,
    const gfx::Rect& element_bounds,
    views::BubbleArrowSide side);

// Returns true if there is enough space to place the popup with
// `popup_preferred_size` plus an additional `spacing` on a specific
// `side` of the `element_bounds` in the `content_area_bounds`. `spacing`
// defines the number of additional pixels the popup should be displaced from
// the element.
bool IsPopupPlaceableOnSideOfElement(const gfx::Rect& content_area_bounds,
                                     const gfx::Rect& element_bounds,
                                     const gfx::Size& popup_preferred_size,
                                     int spacing,
                                     views::BubbleArrowSide side);

// Returns the first side from popup_preferred_sides, for which the popup with
// a `popup_preferred_size` fits on the side of the `element_bounds` in
// the `content_area_bounds` taking the arrow length into account.
// If neither side bits, the function returns kBottom.
// `anchor_type` is used to define whether the `element_bounds` width
// is taken into account to decide if vertical positioning is allowed.
// In the case of `PopupAnchorType::kCaret`, this width
// check does not apply. Since carets are narrow by design.
views::BubbleArrowSide GetOptimalArrowSide(
    const gfx::Rect& content_area_bounds,
    const gfx::Rect& element_bounds,
    const gfx::Size& popup_preferred_size,
    base::span<const views::BubbleArrowSide> popup_preferred_sides,
    PopupAnchorType anchor_type);

// Determines the optimal position of a popup with `popup_preferred_size` next
// to an UI element with `element_bounds`. The arrow pointer dimensions are
// not taken into account if it is present. `content_area_bounds` are the
// boundaries of the view port, `right_to_left` indicates if the website uses
// text written from right to left. `scrollbar_width` is the width of a scroll
// bar and `maximum_offset_to_center` is the maximum number of pixels the popup
// can be moved towards the center of `element_bounds` and
// `maximum_width_percentage_to_center` is the maxmum percentage of the
// element's width for the popup to move towards the center. `popup_bounds` is
// the current rect of the popup that is modified by this function. The
// function returns the arrow position that is used on the popup.
views::BubbleBorder::Arrow GetOptimalPopupPlacement(
    const gfx::Rect& content_area_bounds,
    const gfx::Rect& element_bounds,
    const gfx::Size& popup_preferred_size,
    bool right_to_left,
    int scrollbar_width,
    int maximum_pixel_offset_to_center,
    int maximum_width_percentage_to_center,
    gfx::Rect& popup_bounds,
    base::span<const views::BubbleArrowSide> popup_preferred_sides,
    PopupAnchorType anchor_type);

// Returns whether there is an open permissions prompt in `web_contents` with
// bounds that overlap `screen_bounds`.
bool BoundsOverlapWithOpenPermissionsPrompt(const gfx::Rect& screen_bounds,
                                            content::WebContents* web_contents);

// Returns whether there is picture-in-picture window with bounds that overlap
// `screen_bounds`. Assuming:
// 1. the pip window is shown in the active tab.
// 2. this function is called from the frame of the contents that show the
// autofill popup.
bool BoundsOverlapWithPictureInPictureWindow(const gfx::Rect& screen_bounds);

// Returns whether a popup may vertically exceed the bounds of `web_contents`.
// This is permitted for extension popups. Here we only enforce that the
// autofill popup is at least attached to the extension popup (or overlaps the
// extension popup) and stays within the bounds of the browser window.
bool PopupMayExceedContentAreaBounds(content::WebContents* web_contents);

// Returns whether the suggestion with this `type` can have child
// suggestions.
bool IsExpandableSuggestionType(SuggestionType type);

}  // namespace autofill

#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_VIEW_UTILS_H_