chrome: Improve positioning of dialogs (fixes #3628)

Dialogs will be excluded from regions near the top of the window
that contain overlays, draggable regions or titlebar.
This commit is contained in:
Marshall Greenblatt 2024-01-11 18:32:08 -05:00
parent 9ec3172ffa
commit b5d542d38d
16 changed files with 274 additions and 85 deletions

View File

@ -264,9 +264,6 @@ std::unique_ptr<CefMenuRunner> CefBrowserPlatformDelegate::CreateMenuRunner() {
return nullptr;
}
void CefBrowserPlatformDelegate::UpdateFindBarBoundingBox(
gfx::Rect* bounds) const {}
bool CefBrowserPlatformDelegate::IsWindowless() const {
return false;
}

View File

@ -286,9 +286,6 @@ class CefBrowserPlatformDelegate {
// Create the platform-specific menu runner.
virtual std::unique_ptr<CefMenuRunner> CreateMenuRunner();
// Optionally modify the bounding box for the Chrome Find bar.
virtual void UpdateFindBarBoundingBox(gfx::Rect* bounds) const;
// Returns true if this delegate implements windowless rendering. May be
// called on multiple threads.
virtual bool IsWindowless() const;

View File

@ -7,11 +7,11 @@
#pragma once
#include <memory>
#include <optional>
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/ui/page_action/page_action_icon_type.h"
#include "content/public/browser/web_contents_delegate.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/window_open_disposition.h"
@ -107,6 +107,9 @@ class BrowserDelegate : public content::WebContentsDelegate {
// Optionally modify the bounding box for the Find bar.
virtual void UpdateFindBarBoundingBox(gfx::Rect* bounds) {}
// Optionally modify the top inset for dialogs.
virtual void UpdateDialogTopInset(int* dialog_top_y) {}
// Same as RequestMediaAccessPermission but returning |callback| if the
// request is unhandled.
[[nodiscard]] virtual content::MediaResponseCallback
@ -118,8 +121,8 @@ class BrowserDelegate : public content::WebContentsDelegate {
// Optionally override support for the specified window feature of type
// Browser::WindowFeature.
virtual absl::optional<bool> SupportsWindowFeature(int feature) const {
return absl::nullopt;
virtual std::optional<bool> SupportsWindowFeature(int feature) const {
return std::nullopt;
}
// Returns true if draggable regions are supported.
@ -128,8 +131,8 @@ class BrowserDelegate : public content::WebContentsDelegate {
// Returns the draggable region, if any, relative to the web contents.
// Called from PictureInPictureBrowserFrameView::NonClientHitTest and
// BrowserView::ShouldDescendIntoChildForEventHandling.
virtual const absl::optional<SkRegion> GetDraggableRegion() const {
return absl::nullopt;
virtual const std::optional<SkRegion> GetDraggableRegion() const {
return std::nullopt;
}
// Set the draggable region relative to web contents.

View File

@ -336,8 +336,16 @@ bool ChromeBrowserDelegate::IsToolbarButtonVisible(
}
void ChromeBrowserDelegate::UpdateFindBarBoundingBox(gfx::Rect* bounds) {
if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) {
browser->platform_delegate()->UpdateFindBarBoundingBox(bounds);
if (auto cef_window_view = GetCefWindowView()) {
cef_window_view->UpdateFindBarBoundingBox(bounds);
}
}
void ChromeBrowserDelegate::UpdateDialogTopInset(int* dialog_top_y) {
// This may be called during Browser initialization (before Tab/WebContents
// creation), so we can't route through the ChromeBrowserHostImpl.
if (auto cef_window_view = GetCefWindowView()) {
cef_window_view->UpdateDialogTopInset(dialog_top_y);
}
}
@ -377,7 +385,7 @@ bool ChromeBrowserDelegate::SupportsFramelessPictureInPicture() const {
return *frameless_pip_;
}
absl::optional<bool> ChromeBrowserDelegate::SupportsWindowFeature(
std::optional<bool> ChromeBrowserDelegate::SupportsWindowFeature(
int feature) const {
// Override the default value from
// Browser::PictureInPictureBrowserSupportsWindowFeature.
@ -386,14 +394,14 @@ absl::optional<bool> ChromeBrowserDelegate::SupportsWindowFeature(
// Return false to hide titlebar and enable draggable regions.
return !SupportsFramelessPictureInPicture();
}
return absl::nullopt;
return std::nullopt;
}
bool ChromeBrowserDelegate::SupportsDraggableRegion() const {
return SupportsFramelessPictureInPicture();
}
const absl::optional<SkRegion> ChromeBrowserDelegate::GetDraggableRegion()
const std::optional<SkRegion> ChromeBrowserDelegate::GetDraggableRegion()
const {
DCHECK(SupportsDraggableRegion());
return draggable_region_;
@ -408,14 +416,11 @@ void ChromeBrowserDelegate::WindowFullscreenStateChanged() {
// Use a synchronous callback for notification on Windows/Linux. MacOS gets
// notified asynchronously via CefNativeWidgetMac callbacks.
#if !BUILDFLAG(IS_MAC)
if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) {
if (auto chrome_browser_view = browser->chrome_browser_view()) {
auto* cef_window = chrome_browser_view->cef_browser_view()->cef_window();
if (auto* delegate = cef_window->delegate()) {
// Give the CefWindowDelegate a chance to handle the event.
delegate->OnWindowFullscreenTransition(cef_window,
/*is_completed=*/true);
}
if (auto cef_window_impl = GetCefWindowImpl()) {
if (auto* delegate = cef_window_impl->delegate()) {
// Give the CefWindowDelegate a chance to handle the event.
delegate->OnWindowFullscreenTransition(cef_window_impl,
/*is_completed=*/true);
}
}
#endif
@ -646,7 +651,7 @@ ChromeBrowserDelegate::CreateBrowserHostForPopup(
}
CefBrowserContentsDelegate* ChromeBrowserDelegate::GetDelegateForWebContents(
content::WebContents* web_contents) {
content::WebContents* web_contents) const {
auto browser_host =
ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
if (browser_host) {
@ -655,6 +660,28 @@ CefBrowserContentsDelegate* ChromeBrowserDelegate::GetDelegateForWebContents(
return nullptr;
}
bool ChromeBrowserDelegate::IsViewsHosted() const {
return create_params_.browser_view != nullptr ||
create_params_.popup_with_views_hosted_opener;
}
CefWindowImpl* ChromeBrowserDelegate::GetCefWindowImpl() const {
if (IsViewsHosted()) {
if (auto chrome_browser_view =
static_cast<ChromeBrowserView*>(browser_->window())) {
return chrome_browser_view->cef_browser_view()->cef_window_impl();
}
}
return nullptr;
}
CefWindowView* ChromeBrowserDelegate::GetCefWindowView() const {
if (auto cef_window_impl = GetCefWindowImpl()) {
return cef_window_impl->cef_window_view();
}
return nullptr;
}
namespace cef {
// static

View File

@ -16,6 +16,8 @@
class CefBrowserContentsDelegate;
class CefRequestContextImpl;
class CefWindowImpl;
class CefWindowView;
class ChromeBrowserHostImpl;
// Implementation of the cef::BrowserDelegate interface. Lifespan is controlled
@ -69,13 +71,14 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate {
bool IsPageActionIconVisible(PageActionIconType icon_type) override;
bool IsToolbarButtonVisible(ToolbarButtonType button_type) override;
void UpdateFindBarBoundingBox(gfx::Rect* bounds) override;
void UpdateDialogTopInset(int* dialog_top_y) override;
[[nodiscard]] content::MediaResponseCallback RequestMediaAccessPermissionEx(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) override;
absl::optional<bool> SupportsWindowFeature(int feature) const override;
std::optional<bool> SupportsWindowFeature(int feature) const override;
bool SupportsDraggableRegion() const override;
const absl::optional<SkRegion> GetDraggableRegion() const override;
const std::optional<SkRegion> GetDraggableRegion() const override;
void UpdateDraggableRegion(const SkRegion& region) override;
void WindowFullscreenStateChanged() override;
@ -137,19 +140,25 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate {
CefRefPtr<ChromeBrowserHostImpl> opener);
CefBrowserContentsDelegate* GetDelegateForWebContents(
content::WebContents* web_contents);
content::WebContents* web_contents) const;
bool SupportsFramelessPictureInPicture() const;
bool IsViewsHosted() const;
// Will return nullptr if the Browser is not Views-hosted.
CefWindowImpl* GetCefWindowImpl() const;
CefWindowView* GetCefWindowView() const;
Browser* const browser_;
base::WeakPtr<ChromeBrowserHostImpl> opener_host_;
// Used when creating a new browser host.
const CefBrowserCreateParams create_params_;
absl::optional<bool> show_status_bubble_;
absl::optional<SkRegion> draggable_region_;
mutable absl::optional<bool> frameless_pip_;
std::optional<bool> show_status_bubble_;
std::optional<SkRegion> draggable_region_;
mutable std::optional<bool> frameless_pip_;
std::unique_ptr<CefShowDevToolsParams> pending_show_devtools_params_;
};

View File

@ -93,8 +93,15 @@ CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserHostImpl::GetBrowserForGlobalId(
CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserHostImpl::GetBrowserForBrowser(
const Browser* browser) {
REQUIRE_CHROME_RUNTIME();
return GetBrowserForContents(
browser->tab_strip_model()->GetActiveWebContents());
// Return the ChromeBrowserHostImpl that is currently active.
// Views-hosted Browsers will contain a single ChromeBrowserHostImpl.
// Otherwise, there will be a ChromeBrowserHostImpl per Tab/WebContents.
// |contents| may be nullptr during Browser initialization or destruction.
auto contents = browser->tab_strip_model()->GetActiveWebContents();
if (!contents) {
return nullptr;
}
return GetBrowserForContents(contents);
}
ChromeBrowserHostImpl::~ChromeBrowserHostImpl() = default;

View File

@ -195,15 +195,6 @@ void CefBrowserPlatformDelegateChromeViews::PopupBrowserCreated(
}
}
void CefBrowserPlatformDelegateChromeViews::UpdateFindBarBoundingBox(
gfx::Rect* bounds) const {
if (auto* window_impl = GetWindowImpl()) {
if (window_impl->root_view()) {
window_impl->root_view()->UpdateFindBarBoundingBox(bounds);
}
}
}
bool CefBrowserPlatformDelegateChromeViews::IsViewsHosted() const {
return true;
}

View File

@ -37,7 +37,6 @@ class CefBrowserPlatformDelegateChromeViews
bool is_devtools) override;
void PopupBrowserCreated(CefBrowserHostBase* new_browser,
bool is_devtools) override;
void UpdateFindBarBoundingBox(gfx::Rect* bounds) const override;
bool IsViewsHosted() const override;
CefRefPtr<CefBrowserViewImpl> browser_view() const { return browser_view_; }

View File

@ -141,7 +141,7 @@ bool CefBrowserViewImpl::HandleKeyboardEvent(
}
// Give the CefWindowDelegate a chance to handle the event.
if (auto* window_impl = cef_window()) {
if (auto* window_impl = cef_window_impl()) {
CefKeyEvent cef_event;
if (browser_util::GetCefKeyEvent(event, cef_event) &&
window_impl->OnKeyEvent(cef_event)) {
@ -316,7 +316,11 @@ ChromeBrowserView* CefBrowserViewImpl::chrome_browser_view() const {
return static_cast<ChromeBrowserView*>(root_view());
}
CefWindowImpl* CefBrowserViewImpl::cef_window() const {
CefWindowImpl* CefBrowserViewImpl::cef_window_impl() const {
// Same implementation as GetWindow().
if (!root_view()) {
return nullptr;
}
CefRefPtr<CefWindow> window =
view_util::GetWindowFor(root_view()->GetWidget());
return static_cast<CefWindowImpl*>(window.get());

View File

@ -85,7 +85,7 @@ class CefBrowserViewImpl
ChromeBrowserView* chrome_browser_view() const;
// Return the CefWindowImpl hosting this object.
CefWindowImpl* cef_window() const;
CefWindowImpl* cef_window_impl() const;
private:
// Create a new implementation object.

View File

@ -278,6 +278,7 @@ void CefOverlayViewHost::SetOverlayBounds(const gfx::Rect& bounds) {
view_->SetSize(bounds_.size());
}
widget_->SetBounds(bounds_);
window_view_->OnOverlayBoundsChanged();
bounds_changing_ = false;
}

View File

@ -137,7 +137,7 @@ void CefWindowImpl::ShowAsBrowserModalDialog(
static_cast<CefBrowserViewImpl*>(browser_view.get());
// |browser_view| must belong to the host widget.
auto* host_widget = static_cast<CefWindowView*>(root_view())->host_widget();
auto* host_widget = cef_window_view()->host_widget();
CHECK(host_widget &&
browser_view_impl->root_view()->GetWidget() == host_widget);
@ -739,6 +739,10 @@ void CefWindowImpl::RemoveAllAccelerators() {
focus_manager->UnregisterAccelerators(this);
}
CefWindowView* CefWindowImpl::cef_window_view() const {
return static_cast<CefWindowView*>(root_view());
}
CefWindowImpl::CefWindowImpl(CefRefPtr<CefWindowDelegate> delegate)
: ParentClass(delegate) {}
@ -747,7 +751,7 @@ CefWindowView* CefWindowImpl::CreateRootView() {
}
void CefWindowImpl::InitializeRootView() {
static_cast<CefWindowView*>(root_view())->Initialize();
cef_window_view()->Initialize();
}
void CefWindowImpl::CreateWidget(gfx::AcceleratedWidget parent_widget) {

View File

@ -133,6 +133,8 @@ class CefWindowImpl
cef_menu_anchor_position_t anchor_position);
void MenuClosed();
CefWindowView* cef_window_view() const;
views::Widget* widget() const { return widget_; }
bool initialized() const { return initialized_; }

View File

@ -92,7 +92,7 @@ class NativeFrameViewEx : public views::NativeFrameView {
}
if (!view_->IsFrameless()) {
if (auto titlebar_height = view_->GetTitlebarHeight()) {
if (auto titlebar_height = view_->GetTitlebarHeight(/*required=*/true)) {
window_bounds.Inset(gfx::Insets::TLBR(-(*titlebar_height), 0, 0, 0));
}
}
@ -829,12 +829,23 @@ void CefWindowView::MoveOverlaysIfNecessary() {
}
}
void CefWindowView::InvalidateExclusionRegions() {
if (last_dialog_top_inset_ != -1) {
last_dialog_top_y_ = last_dialog_top_inset_ = -1;
}
}
void CefWindowView::SetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
if (regions.empty() && !draggable_region_) {
// Still empty.
return;
}
InvalidateExclusionRegions();
if (regions.empty()) {
if (draggable_region_) {
draggable_region_.reset(nullptr);
}
draggable_region_.reset();
draggable_rects_.clear();
return;
}
@ -854,6 +865,10 @@ void CefWindowView::SetDraggableRegions(
}
}
void CefWindowView::OnOverlayBoundsChanged() {
InvalidateExclusionRegions();
}
views::NonClientFrameView* CefWindowView::GetNonClientFrameView() const {
const views::Widget* widget = GetWidget();
if (!widget) {
@ -865,7 +880,8 @@ views::NonClientFrameView* CefWindowView::GetNonClientFrameView() const {
return widget->non_client_view()->frame_view();
}
void CefWindowView::UpdateFindBarBoundingBox(gfx::Rect* bounds) const {
void CefWindowView::UpdateBoundingBox(gfx::Rect* bounds,
bool add_titlebar_height) const {
// Max distance from the edges of |bounds| to qualify for subtraction.
const int kMaxDistance = 10;
@ -878,16 +894,8 @@ void CefWindowView::UpdateFindBarBoundingBox(gfx::Rect* bounds) const {
*bounds = SubtractOverlayFromBoundingBox(*bounds, rect, kMaxDistance);
}
if (auto titlebar_height = GetTitlebarHeight()) {
if (auto titlebar_height = GetTitlebarHeight(add_titlebar_height)) {
gfx::Insets inset;
#if BUILDFLAG(IS_MAC)
// For framed windows on macOS we must add the titlebar height.
const bool add_titlebar_height = !is_frameless_;
#else
const bool add_titlebar_height = false;
#endif
if (add_titlebar_height) {
inset.set_top(*titlebar_height);
} else if (bounds->y() < *titlebar_height) {
@ -900,6 +908,46 @@ void CefWindowView::UpdateFindBarBoundingBox(gfx::Rect* bounds) const {
}
}
void CefWindowView::UpdateFindBarBoundingBox(gfx::Rect* bounds) const {
#if BUILDFLAG(IS_MAC)
// For framed windows on macOS we must add the titlebar height.
const bool add_titlebar_height = !is_frameless_;
#else
const bool add_titlebar_height = false;
#endif
UpdateBoundingBox(bounds, add_titlebar_height);
}
void CefWindowView::UpdateDialogTopInset(int* dialog_top_y) const {
if (*dialog_top_y == last_dialog_top_y_ && last_dialog_top_inset_ != -1) {
// Return the cached value.
*dialog_top_y = last_dialog_top_inset_;
return;
}
const views::Widget* widget = GetWidget();
if (!widget) {
return;
}
gfx::Rect bounds(widget->GetSize());
if (*dialog_top_y > 0) {
// Start with the value computed in
// BrowserViewLayout::LayoutBookmarkAndInfoBars.
gfx::Insets inset;
inset.set_top(*dialog_top_y);
bounds.Inset(inset);
}
UpdateBoundingBox(&bounds, /*add_titlebar_height=*/false);
last_dialog_top_y_ = *dialog_top_y;
last_dialog_top_inset_ = bounds.y();
*dialog_top_y = bounds.y();
}
views::Widget* CefWindowView::host_widget() const {
if (host_widget_destruction_observer_) {
return host_widget_destruction_observer_->widget();
@ -907,7 +955,7 @@ views::Widget* CefWindowView::host_widget() const {
return nullptr;
}
absl::optional<float> CefWindowView::GetTitlebarHeight() const {
std::optional<float> CefWindowView::GetTitlebarHeight(bool required) const {
if (cef_delegate()) {
float title_bar_height = 0;
const bool has_title_bar_height =
@ -918,7 +966,7 @@ absl::optional<float> CefWindowView::GetTitlebarHeight() const {
}
#if BUILDFLAG(IS_MAC)
if (!is_frameless_) {
if (required) {
// For framed windows on macOS we must include the titlebar height in the
// UpdateFindBarBoundingBox() calculation.
return view_util::GetNSWindowTitleBarHeight(
@ -926,5 +974,5 @@ absl::optional<float> CefWindowView::GetTitlebarHeight() const {
}
#endif
return absl::nullopt;
return std::nullopt;
}

View File

@ -6,6 +6,7 @@
#define CEF_LIBCEF_BROWSER_VIEWS_WINDOW_VIEW_H_
#pragma once
#include <optional>
#include <vector>
#include "include/views/cef_window.h"
@ -112,13 +113,19 @@ class CefWindowView
void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
SkRegion* draggable_region() const { return draggable_region_.get(); }
// Called from CefOverlayViewHost::SetOverlayBounds().
void OnOverlayBoundsChanged();
// Returns the NonClientFrameView for this Window. May be nullptr.
views::NonClientFrameView* GetNonClientFrameView() const;
// Optionally modify the bounding box for the Chrome Find bar.
void UpdateFindBarBoundingBox(gfx::Rect* bounds) const;
absl::optional<float> GetTitlebarHeight() const;
// Optionally modify the top inset for Chrome dialogs.
void UpdateDialogTopInset(int* dialog_top_y) const;
std::optional<float> GetTitlebarHeight(bool required) const;
bool IsFrameless() const { return is_frameless_; }
// The Widget that hosts us, if we're a modal dialog. May return nullptr
@ -130,6 +137,9 @@ class CefWindowView
void DeleteDelegate();
void MoveOverlaysIfNecessary();
void InvalidateExclusionRegions();
void UpdateBoundingBox(gfx::Rect* bounds, bool add_titlebar_height) const;
// Not owned by this object.
Delegate* window_delegate_;
@ -144,6 +154,9 @@ class CefWindowView
std::unique_ptr<SkRegion> draggable_region_;
std::vector<gfx::Rect> draggable_rects_;
mutable int last_dialog_top_y_ = -1;
mutable int last_dialog_top_inset_ = -1;
// Tracks the Widget that hosts us, if we're a modal dialog.
std::unique_ptr<WidgetDestructionObserver> host_widget_destruction_observer_;

View File

@ -363,7 +363,7 @@ index 0c231b6ac5b01..6b5af98e18e42 100644
BrowserFrame(const BrowserFrame&) = delete;
BrowserFrame& operator=(const BrowserFrame&) = delete;
diff --git chrome/browser/ui/views/frame/browser_view.cc chrome/browser/ui/views/frame/browser_view.cc
index 4273a5f004935..a46cdb993474b 100644
index 4273a5f004935..55f2782c262e4 100644
--- chrome/browser/ui/views/frame/browser_view.cc
+++ chrome/browser/ui/views/frame/browser_view.cc
@@ -343,11 +343,10 @@ using content::NativeWebKeyboardEvent;
@ -381,7 +381,22 @@ index 4273a5f004935..a46cdb993474b 100644
#if BUILDFLAG(IS_CHROMEOS_ASH)
// UMA histograms that record animation smoothness for tab loading animation.
@@ -862,11 +861,21 @@ class BrowserView::AccessibilityModeObserver : public ui::AXModeObserver {
@@ -701,6 +700,14 @@ class BrowserViewLayoutDelegateImpl : public BrowserViewLayoutDelegate {
return browser_view_->frame()->GetTopInset() - browser_view_->y();
}
+ void UpdateDialogTopInsetInBrowserView(int* dialog_top_y) const override {
+#if BUILDFLAG(ENABLE_CEF)
+ if (auto cef_delegate = browser_view_->browser_->cef_delegate()) {
+ cef_delegate->UpdateDialogTopInset(dialog_top_y);
+ }
+#endif
+ }
+
bool IsToolbarVisible() const override {
return browser_view_->IsToolbarVisible();
}
@@ -862,11 +869,21 @@ class BrowserView::AccessibilityModeObserver : public ui::AXModeObserver {
///////////////////////////////////////////////////////////////////////////////
// BrowserView, public:
@ -404,7 +419,7 @@ index 4273a5f004935..a46cdb993474b 100644
// Store the actions so that the access is available for other classes.
if (features::IsSidePanelPinningEnabled()) {
browser_->SetUserData(BrowserActions::UserDataKey(),
@@ -963,8 +972,15 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser)
@@ -963,8 +980,15 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser)
contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
devtools_web_view_, contents_web_view_));
@ -422,7 +437,7 @@ index 4273a5f004935..a46cdb993474b 100644
contents_separator_ =
top_container_->AddChildView(std::make_unique<ContentsSeparator>());
@@ -1038,7 +1054,9 @@ BrowserView::~BrowserView() {
@@ -1038,7 +1062,9 @@ BrowserView::~BrowserView() {
// All the tabs should have been destroyed already. If we were closed by the
// OS with some tabs than the NativeBrowserFrame should have destroyed them.
@ -432,7 +447,7 @@ index 4273a5f004935..a46cdb993474b 100644
// Stop the animation timer explicitly here to avoid running it in a nested
// message loop, which may run by Browser destructor.
@@ -1052,12 +1070,14 @@ BrowserView::~BrowserView() {
@@ -1052,12 +1078,14 @@ BrowserView::~BrowserView() {
// child views and it is an observer for avatar toolbar button if any.
autofill_bubble_handler_.reset();
@ -447,7 +462,7 @@ index 4273a5f004935..a46cdb993474b 100644
// The TabStrip attaches a listener to the model. Make sure we shut down the
// TabStrip first so that it can cleanly remove the listener.
@@ -1075,7 +1095,9 @@ BrowserView::~BrowserView() {
@@ -1075,7 +1103,9 @@ BrowserView::~BrowserView() {
// `SidePanelUI::RemoveSidePanelUIForBrowser()` deletes the
// SidePanelCoordinator.
@ -457,7 +472,7 @@ index 4273a5f004935..a46cdb993474b 100644
}
// static
@@ -1950,9 +1972,14 @@ void BrowserView::OnExclusiveAccessUserInput() {
@@ -1950,9 +1980,14 @@ void BrowserView::OnExclusiveAccessUserInput() {
bool BrowserView::ShouldHideUIForFullscreen() const {
// Immersive mode needs UI for the slide-down top panel.
@ -473,7 +488,7 @@ index 4273a5f004935..a46cdb993474b 100644
return frame_->GetFrameView()->ShouldHideTopUIForFullscreen();
}
@@ -3065,7 +3092,8 @@ DownloadShelf* BrowserView::GetDownloadShelf() {
@@ -3065,7 +3100,8 @@ DownloadShelf* BrowserView::GetDownloadShelf() {
}
DownloadBubbleUIController* BrowserView::GetDownloadBubbleUIController() {
@ -483,7 +498,7 @@ index 4273a5f004935..a46cdb993474b 100644
if (auto* download_button = toolbar_button_provider_->GetDownloadButton())
return download_button->bubble_controller();
return nullptr;
@@ -3616,7 +3644,8 @@ void BrowserView::ReparentTopContainerForEndOfImmersive() {
@@ -3616,7 +3652,8 @@ void BrowserView::ReparentTopContainerForEndOfImmersive() {
if (top_container()->parent() == this)
return;
@ -493,7 +508,7 @@ index 4273a5f004935..a46cdb993474b 100644
top_container()->DestroyLayer();
AddChildViewAt(top_container(), 0);
EnsureFocusOrder();
@@ -4078,11 +4107,38 @@ void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) {
@@ -4078,11 +4115,38 @@ void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) {
bool BrowserView::ShouldDescendIntoChildForEventHandling(
gfx::NativeView child,
const gfx::Point& location) {
@ -534,7 +549,7 @@ index 4273a5f004935..a46cdb993474b 100644
// Draggable regions are defined relative to the web contents.
gfx::Point point_in_contents_web_view_coords(location);
views::View::ConvertPointToTarget(GetWidget()->GetRootView(),
@@ -4091,7 +4147,7 @@ bool BrowserView::ShouldDescendIntoChildForEventHandling(
@@ -4091,7 +4155,7 @@ bool BrowserView::ShouldDescendIntoChildForEventHandling(
// Draggable regions should be ignored for clicks into any browser view's
// owned widgets, for example alerts, permission prompts or find bar.
@ -543,7 +558,7 @@ index 4273a5f004935..a46cdb993474b 100644
point_in_contents_web_view_coords.x(),
point_in_contents_web_view_coords.y()) ||
WidgetOwnedByAnchorContainsPoint(point_in_contents_web_view_coords);
@@ -4199,8 +4255,10 @@ void BrowserView::Layout() {
@@ -4199,8 +4263,10 @@ void BrowserView::Layout() {
// TODO(jamescook): Why was this in the middle of layout code?
toolbar_->location_bar()->omnibox_view()->SetFocusBehavior(
@ -556,7 +571,7 @@ index 4273a5f004935..a46cdb993474b 100644
#if BUILDFLAG(IS_CHROMEOS_ASH)
// In chromeOS ash we round the bottom two corners of the browser frame by
@@ -4278,6 +4336,11 @@ void BrowserView::AddedToWidget() {
@@ -4278,6 +4344,11 @@ void BrowserView::AddedToWidget() {
SetThemeProfileForWindow(GetNativeWindow(), browser_->profile());
#endif
@ -568,7 +583,7 @@ index 4273a5f004935..a46cdb993474b 100644
toolbar_->Init();
// TODO(pbos): Investigate whether the side panels should be creatable when
@@ -4326,13 +4389,9 @@ void BrowserView::AddedToWidget() {
@@ -4326,13 +4397,9 @@ void BrowserView::AddedToWidget() {
EnsureFocusOrder();
@ -584,7 +599,7 @@ index 4273a5f004935..a46cdb993474b 100644
using_native_frame_ = frame_->ShouldUseNativeFrame();
MaybeInitializeWebUITabStrip();
@@ -4749,7 +4808,8 @@ void BrowserView::ProcessFullscreen(bool fullscreen,
@@ -4749,7 +4816,8 @@ void BrowserView::ProcessFullscreen(bool fullscreen,
// Undo our anti-jankiness hacks and force a re-layout.
in_process_fullscreen_ = false;
ToolbarSizeChanged(false);
@ -594,7 +609,7 @@ index 4273a5f004935..a46cdb993474b 100644
}
bool BrowserView::ShouldUseImmersiveFullscreenForUrl(const GURL& url) const {
@@ -5147,6 +5207,8 @@ Profile* BrowserView::GetProfile() {
@@ -5147,6 +5215,8 @@ Profile* BrowserView::GetProfile() {
}
void BrowserView::UpdateUIForTabFullscreen() {
@ -603,7 +618,7 @@ index 4273a5f004935..a46cdb993474b 100644
frame()->GetFrameView()->UpdateFullscreenTopUI();
}
@@ -5169,6 +5231,8 @@ void BrowserView::HideDownloadShelf() {
@@ -5169,6 +5239,8 @@ void BrowserView::HideDownloadShelf() {
}
bool BrowserView::CanUserExitFullscreen() const {
@ -644,7 +659,7 @@ index 2e4054890db75..1a518ba936fd1 100644
// Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate
// interface to keep these two classes decoupled and testable.
diff --git chrome/browser/ui/views/frame/browser_view_layout.cc chrome/browser/ui/views/frame/browser_view_layout.cc
index 5e39a622e391e..e0b67a6902182 100644
index 5e39a622e391e..1d4504370305f 100644
--- chrome/browser/ui/views/frame/browser_view_layout.cc
+++ chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -48,6 +48,10 @@
@ -658,7 +673,67 @@ index 5e39a622e391e..e0b67a6902182 100644
using views::View;
using web_modal::ModalDialogHostObserver;
using web_modal::WebContentsModalDialogHost;
@@ -583,6 +587,13 @@ int BrowserViewLayout::LayoutWebUITabStrip(int top) {
@@ -92,6 +96,10 @@ class BrowserViewLayout::WebContentsModalDialogHostViews
observer.OnHostDestroying();
}
+ bool HasObservers() const {
+ return !observer_list_.empty();
+ }
+
void NotifyPositionRequiresUpdate() {
for (ModalDialogHostObserver& observer : observer_list_)
observer.OnPositionRequiresUpdate();
@@ -102,7 +110,7 @@ class BrowserViewLayout::WebContentsModalDialogHostViews
views::View* view = browser_view_layout_->contents_container_;
gfx::Rect rect = view->ConvertRectToWidget(view->GetLocalBounds());
const int middle_x = rect.x() + rect.width() / 2;
- const int top = browser_view_layout_->dialog_top_y_;
+ const int top = GetDialogTopY();
return gfx::Point(middle_x - size.width() / 2, top);
}
@@ -117,7 +125,7 @@ class BrowserViewLayout::WebContentsModalDialogHostViews
gfx::Size GetMaximumDialogSize() override {
views::View* view = browser_view_layout_->contents_container_;
gfx::Rect content_area = view->ConvertRectToWidget(view->GetLocalBounds());
- const int top = browser_view_layout_->dialog_top_y_;
+ const int top = GetDialogTopY();
return gfx::Size(content_area.width(), content_area.bottom() - top);
}
@@ -131,6 +139,13 @@ class BrowserViewLayout::WebContentsModalDialogHostViews
return GetHostWidget()->GetNativeView();
}
+ int GetDialogTopY() const {
+ int dialog_top_y = browser_view_layout_->dialog_top_y_;
+ browser_view_layout_->delegate_->UpdateDialogTopInsetInBrowserView(
+ &dialog_top_y);
+ return dialog_top_y;
+ }
+
// Add/remove observer.
void AddObserver(ModalDialogHostObserver* observer) override {
observer_list_.AddObserver(observer);
@@ -441,6 +456,8 @@ void BrowserViewLayout::Layout(views::View* browser_view) {
if (exclusive_access_bubble)
exclusive_access_bubble->RepositionIfVisible();
+ // Avoid unnecessary calls to UpdateDialogTopInsetInBrowserView().
+ if (dialog_host_->HasObservers()) {
// Adjust any hosted dialogs if the browser's dialog hosting bounds changed.
const gfx::Rect dialog_bounds(dialog_host_->GetDialogPosition(gfx::Size()),
dialog_host_->GetMaximumDialogSize());
@@ -454,6 +471,7 @@ void BrowserViewLayout::Layout(views::View* browser_view) {
latest_dialog_bounds_in_screen_ = dialog_bounds_in_screen;
dialog_host_->NotifyPositionRequiresUpdate();
}
+ }
}
// Return the preferred size which is the size required to give each
@@ -583,6 +601,13 @@ int BrowserViewLayout::LayoutWebUITabStrip(int top) {
int BrowserViewLayout::LayoutToolbar(int top) {
TRACE_EVENT0("ui", "BrowserViewLayout::LayoutToolbar");
@ -672,6 +747,18 @@ index 5e39a622e391e..e0b67a6902182 100644
int browser_view_width = vertical_layout_rect_.width();
bool toolbar_visible = delegate_->IsToolbarVisible();
int height = toolbar_visible ? toolbar_->GetPreferredSize().height() : 0;
diff --git chrome/browser/ui/views/frame/browser_view_layout_delegate.h chrome/browser/ui/views/frame/browser_view_layout_delegate.h
index 29ad5517bd3c9..b0fe093467abc 100644
--- chrome/browser/ui/views/frame/browser_view_layout_delegate.h
+++ chrome/browser/ui/views/frame/browser_view_layout_delegate.h
@@ -28,6 +28,7 @@ class BrowserViewLayoutDelegate {
const gfx::Rect& available_space,
views::Label& window_title_label) const = 0;
virtual int GetTopInsetInBrowserView() const = 0;
+ virtual void UpdateDialogTopInsetInBrowserView(int* dialog_top_y) const = 0;
virtual bool IsToolbarVisible() const = 0;
virtual bool IsBookmarkBarVisible() const = 0;
virtual bool IsContentsSeparatorEnabled() const = 0;
diff --git chrome/browser/ui/views/frame/contents_web_view.cc chrome/browser/ui/views/frame/contents_web_view.cc
index 8267a265a8e10..ee08f18e96a34 100644
--- chrome/browser/ui/views/frame/contents_web_view.cc