blob: 90e3dff23cad0f4712e677d072765f054c554eed [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/autofill/autofill_field_promo_view_impl.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/autofill/popup/popup_view_utils.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/views/view_class_properties.h"
namespace {
views::View* GetContentsWebView(content::WebContents* web_contents) {
Browser* browser = chrome::FindBrowserWithTab(web_contents);
if (!browser) {
return nullptr;
}
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
return browser_view ? browser_view->contents_web_view() : nullptr;
}
} // namespace
namespace autofill {
// static
base::WeakPtr<AutofillFieldPromoView> AutofillFieldPromoView::CreateAndShow(
content::WebContents* web_contents,
const gfx::RectF& element_bounds,
const ui::ElementIdentifier& promo_element_identifier) {
views::View* contents_web_view = GetContentsWebView(web_contents);
if (!contents_web_view) {
return nullptr;
}
base::WeakPtr<AutofillFieldPromoView> promo_view =
contents_web_view
->AddChildView(base::WrapUnique<AutofillFieldPromoViewImpl>(
new AutofillFieldPromoViewImpl(web_contents, element_bounds,
promo_element_identifier)))
->GetWeakPtr();
return promo_view;
}
AutofillFieldPromoViewImpl::AutofillFieldPromoViewImpl(
content::WebContents* web_contents,
const gfx::RectF& element_bounds,
const ui::ElementIdentifier& promo_element_identifier)
: web_contents_(web_contents) {
SetViewBounds(element_bounds);
SetProperty(views::kElementIdentifierKey, promo_element_identifier);
}
AutofillFieldPromoViewImpl::~AutofillFieldPromoViewImpl() = default;
bool AutofillFieldPromoViewImpl::OverlapsWithPictureInPictureWindow() const {
return BoundsOverlapWithPictureInPictureWindow(GetBoundsInScreen());
}
void AutofillFieldPromoViewImpl::Close() {
views::View* contents_web_view = GetContentsWebView(web_contents_);
// If the contents web view no longer exists, neither do its children.
if (!contents_web_view) {
return;
}
if (contents_web_view->Contains(this)) {
contents_web_view->RemoveChildView(this);
}
weak_ptr_factory_.InvalidateWeakPtrs();
// The deletion of the view is asynchronous, so it can die after
// `web_contents_` is destroyed. In order to avoid dangling pointers,
// `web_contents_` is set to null.
web_contents_ = nullptr;
base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
this);
}
base::WeakPtr<AutofillFieldPromoView> AutofillFieldPromoViewImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void AutofillFieldPromoViewImpl::SetViewBounds(
const gfx::RectF& element_bounds) {
// The type of `element_bounds` is changed from `gfx::RectF` to `gfx::Rect`.
// They need to have the same type as the web contents bounds, because they
// are later intersected with them.
gfx::Rect element_bounds_container_bounds_intersection =
gfx::ToEnclosingRect(element_bounds);
// The coordinates of the element bounds are represented in a coordinate
// system which has the origin in the top-left corner of the `web_contents_`.
//
// The coordinates of `web_contents_` are represented in a coordinate system
// which has the origin in the top-left corner of the screen.
//
// O1 - origin of the coordinate system of `web_contents_` (top-left corner
// of the screen) O2 - origin of the coordinate system of the element
// (top-left corner of `web_contents_`)
//
// O1
// *___________________________________________________________
// | THIS AREA IS THE SCREEN |
// | |
// | O2 |
// | *_____________________________________ |
// | | | |
// | | THIS AREA IS THE WEB CONTENTS | |
// | | | |
// | | _________ | |
// | | |_________| <-- ELEMENT | |
// | | | |
// | |_____________________________________| |
// | |
// |__________________________________________________________|
//
// In order to be able to intersect them, they need to be represented in the
// same coordinate system. Thus, the bounds of the `web_contents_` are
// translated so that O1 overlaps with O2.
gfx::Rect web_contents_translated_bounds =
web_contents_->GetContainerBounds();
web_contents_translated_bounds -=
web_contents_translated_bounds.OffsetFromOrigin();
// Some elements may be partially outside the visible content area. Thus, we
// take into consideration only the part of the element which is inside the
// visible content area (i.e. the intersection between the web content bounds
// and the element bounds). Otherwise, the view would be partially outside of
// the visible content area and the IPH could be displayed there.
element_bounds_container_bounds_intersection.Intersect(
web_contents_translated_bounds);
// The view is drawn such that it completely overlaps with the bottom part of
// the element (it cannot be below the element, because the IPH would then
// float in the air). Height is not relevant as long as it is greater than 0.
SetBounds(element_bounds_container_bounds_intersection.x(),
element_bounds_container_bounds_intersection.y() +
element_bounds_container_bounds_intersection.height() - 1,
element_bounds_container_bounds_intersection.width(), 1);
SetPaintToLayer();
}
BEGIN_METADATA(AutofillFieldPromoViewImpl)
END_METADATA
} // namespace autofill