diff --git chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc index b169371e4d42f..509e4bda85b47 100644 --- chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc +++ chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc @@ -100,7 +100,7 @@ javascript_dialogs::AppModalDialogView* CreateViewsJavaScriptDialog( gfx::NativeWindow parent_window = controller->web_contents()->GetTopLevelNativeWindow(); #if defined(USE_AURA) - if (!parent_window->GetRootWindow()) { + if (parent_window && !parent_window->GetRootWindow()) { // When we are part of a WebContents that isn't actually being displayed // on the screen, we can't actually attach to it. parent_window = nullptr; diff --git components/constrained_window/constrained_window_views.cc components/constrained_window/constrained_window_views.cc index b82cb011d2359..e224eef611704 100644 --- components/constrained_window/constrained_window_views.cc +++ components/constrained_window/constrained_window_views.cc @@ -102,10 +102,17 @@ class ModalDialogHostObserverViews : public ModalDialogHostObserver { gfx::Rect GetModalDialogBounds(views::Widget* widget, web_modal::ModalDialogHost* dialog_host, const gfx::Size& size) { - views::Widget* const host_widget = - views::Widget::GetWidgetForNativeView(dialog_host->GetHostView()); + // |host_view| will be nullptr with CEF windowless rendering. + auto host_view = dialog_host->GetHostView(); + views::Widget* host_widget = + host_view ? views::Widget::GetWidgetForNativeView(host_view) : nullptr; + + // If the host view is not backed by a Views::Widget, just update the widget + // size. This can happen on MacViews under the Cocoa browser where the window + // modal dialogs are displayed as sheets, and their position is managed by a + // ConstrainedWindowSheetController instance. if (!host_widget) { - return gfx::Rect(); + return gfx::Rect(dialog_host->GetDialogPosition(size), size); } gfx::Point position = dialog_host->GetDialogPosition(size); @@ -114,43 +121,22 @@ gfx::Rect GetModalDialogBounds(views::Widget* widget, position.set_y(position.y() - widget->non_client_view()->frame_view()->GetInsets().top()); - gfx::Rect dialog_bounds(position, size); - if (widget->is_top_level() && SupportsGlobalScreenCoordinates()) { - gfx::Rect dialog_screen_bounds = - dialog_bounds + - host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin(); - const gfx::Rect host_screen_bounds = host_widget->GetWindowBoundsInScreen(); - - // TODO(crbug.com/40851111): The requested dialog bounds should never fall - // outside the bounds of the transient parent. - DCHECK(dialog_screen_bounds.Intersects(host_screen_bounds)); - - // Adjust the dialog bound to ensure it remains visible on the display. - const gfx::Rect display_work_area = - display::Screen::GetScreen() - ->GetDisplayNearestView(dialog_host->GetHostView()) - .work_area(); - if (!display_work_area.Contains(dialog_screen_bounds)) { - dialog_screen_bounds.AdjustToFit(display_work_area); - } - - // For platforms that clip transient children to the viewport we must - // maximize its bounds on the display whilst keeping it within the host - // bounds to avoid viewport clipping. - // In the case that the host window bounds do not have sufficient overlap - // with the display, and the dialog cannot be shown in its entirety, this is - // a recoverable state as users are still able to reposition the host window - // back onto the display. - if (PlatformClipsChildrenToViewport() && - !host_screen_bounds.Contains(dialog_screen_bounds)) { - dialog_screen_bounds.AdjustToFit(host_screen_bounds); - } - - // Readjust the position of the dialog. - dialog_bounds.set_origin(dialog_screen_bounds.origin()); + position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin(); + // If the dialog extends partially off any display, clamp its position to + // be fully visible within that display. If the dialog doesn't intersect + // with any display clamp its position to be fully on the nearest display. + gfx::Rect display_rect = gfx::Rect(position, size); + const display::Display display = + display::Screen::GetScreen()->GetDisplayNearestView( + dialog_host->GetHostView()); + const gfx::Rect work_area = display.work_area(); + if (!work_area.Contains(display_rect)) + display_rect.AdjustToFit(work_area); + position = display_rect.origin(); } - return dialog_bounds; + + return gfx::Rect(position, size); } void UpdateModalDialogPosition(views::Widget* widget, @@ -161,15 +147,24 @@ void UpdateModalDialogPosition(views::Widget* widget, return; } - views::Widget* const host_widget = - views::Widget::GetWidgetForNativeView(dialog_host->GetHostView()); + // |host_view| will be nullptr with CEF windowless rendering. + auto host_view = dialog_host->GetHostView(); + views::Widget* host_widget = + host_view ? views::Widget::GetWidgetForNativeView(host_view) : nullptr; // If the host view is not backed by a Views::Widget, just update the widget // size. This can happen on MacViews under the Cocoa browser where the window // modal dialogs are displayed as sheets, and their position is managed by a // ConstrainedWindowSheetController instance. if (!host_widget) { +#if BUILDFLAG(IS_MAC) widget->SetSize(size); +#elif BUILDFLAG(IS_POSIX) + // Set the bounds here instead of relying on the default behavior of + // DesktopWindowTreeHostPlatform::CenterWindow which incorrectly centers + // the window on the screen. + widget->SetBounds(gfx::Rect(dialog_host->GetDialogPosition(size), size)); +#endif return; } @@ -299,8 +294,13 @@ views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog, gfx::NativeView parent_view = parent ? CurrentBrowserModalClient()->GetDialogHostView(parent) : nullptr; + // Use with CEF windowless rendering. + gfx::AcceleratedWidget parent_widget = + parent ? CurrentBrowserModalClient()->GetModalDialogHost(parent)-> + GetAcceleratedWidget() : gfx::kNullAcceleratedWidget; views::Widget* widget = - views::DialogDelegate::CreateDialogWidget(dialog, nullptr, parent_view); + views::DialogDelegate::CreateDialogWidget(dialog, nullptr, parent_view, + parent_widget); widget->SetNativeWindowProperty( views::kWidgetIdentifierKey, const_cast(kConstrainedWindowWidgetIdentifier)); @@ -317,8 +317,7 @@ views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog, return widget; ModalDialogHost* host = - parent ? CurrentBrowserModalClient()->GetModalDialogHost(parent) - : nullptr; + CurrentBrowserModalClient()->GetModalDialogHost(parent); if (host) { DCHECK_EQ(parent_view, host->GetHostView()); std::unique_ptr observer = @@ -335,11 +334,17 @@ views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog, views::Widget* ShowBrowserModal(std::unique_ptr dialog_model, gfx::NativeWindow parent) { + gfx::NativeView parent_view = + parent ? CurrentBrowserModalClient()->GetDialogHostView(parent) : nullptr; + // Use with CEF windowless rendering. + gfx::AcceleratedWidget parent_widget = + parent ? CurrentBrowserModalClient()->GetModalDialogHost(parent)-> + GetAcceleratedWidget() : gfx::kNullAcceleratedWidget; + // TODO(crbug.com/41493925): Remove will_use_custom_frame once native frame // dialogs support autosize. bool will_use_custom_frame = views::DialogDelegate::CanSupportCustomFrame( - parent ? CurrentBrowserModalClient()->GetDialogHostView(parent) - : nullptr); + parent_view, parent_widget); auto dialog = views::BubbleDialogModelHost::CreateModal( std::move(dialog_model), ui::mojom::ModalType::kWindow, will_use_custom_frame); diff --git components/constrained_window/native_web_contents_modal_dialog_manager_views.cc components/constrained_window/native_web_contents_modal_dialog_manager_views.cc index 2b495a8ab092c..01a28aca853d0 100644 --- components/constrained_window/native_web_contents_modal_dialog_manager_views.cc +++ components/constrained_window/native_web_contents_modal_dialog_manager_views.cc @@ -190,9 +190,20 @@ void NativeWebContentsModalDialogManagerViews::HostChanged( if (host_) { host_->AddObserver(this); - for (views::Widget* widget : observed_widgets_) { - views::Widget::ReparentNativeView(widget->GetNativeView(), - host_->GetHostView()); + // |host_view| will be nullptr with CEF windowless rendering. + if (auto host_view = host_->GetHostView()) { + for (views::Widget* widget : observed_widgets_) { +#if defined(USE_AURA) + auto widget_view = widget->GetNativeView(); + // Don't reparent between different root windows. Doing so causes + // issues with layout of dialogs containing Chrome browsers. + if (host_view->GetRootWindow() == widget_view->GetRootWindow()) { + views::Widget::ReparentNativeView(widget_view, host_view); + } +#else + views::Widget::ReparentNativeView(widget->GetNativeView(), host_view); +#endif + } } OnPositionRequiresUpdate(); diff --git components/web_modal/modal_dialog_host.h components/web_modal/modal_dialog_host.h index 51ed6bcf6b540..c6e1161140655 100644 --- components/web_modal/modal_dialog_host.h +++ components/web_modal/modal_dialog_host.h @@ -34,6 +34,10 @@ class WEB_MODAL_EXPORT ModalDialogHost { // Returns the view against which the dialog is positioned and parented. virtual gfx::NativeView GetHostView() const = 0; + // Returns the widget against which the dialog is positioned and parented. + // Used with CEF windowless rendering. + virtual gfx::AcceleratedWidget GetAcceleratedWidget() const { + return gfx::kNullAcceleratedWidget; } // Gets the position for the dialog in coordinates relative to the host view. virtual gfx::Point GetDialogPosition(const gfx::Size& size) = 0; // Returns whether a dialog currently about to be shown should be activated. diff --git ui/views/window/dialog_delegate.cc ui/views/window/dialog_delegate.cc index bc119f10f749e..d81c1ce4e786f 100644 --- ui/views/window/dialog_delegate.cc +++ ui/views/window/dialog_delegate.cc @@ -87,10 +87,12 @@ DialogDelegate::DialogDelegate() { // static Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate, gfx::NativeWindow context, - gfx::NativeView parent) { + gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget) { views::Widget* widget = new DialogWidget; views::Widget::InitParams params = - GetDialogWidgetInitParams(delegate, context, parent, gfx::Rect()); + GetDialogWidgetInitParams(delegate, context, parent, gfx::Rect(), + parent_widget); widget->Init(std::move(params)); return widget; } @@ -99,16 +101,18 @@ Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate, Widget* DialogDelegate::CreateDialogWidget( std::unique_ptr delegate, gfx::NativeWindow context, - gfx::NativeView parent) { - return CreateDialogWidget(delegate.release(), context, parent); + gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget) { + return CreateDialogWidget(delegate.release(), context, parent, parent_widget); } // static -bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent) { +bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget) { #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \ BUILDFLAG(ENABLE_DESKTOP_AURA) // The new style doesn't support unparented dialogs on Linux desktop. - return parent != nullptr; + return parent != nullptr || parent_widget != gfx::kNullAcceleratedWidget; #else return true; #endif @@ -119,7 +123,8 @@ Widget::InitParams DialogDelegate::GetDialogWidgetInitParams( WidgetDelegate* delegate, gfx::NativeWindow context, gfx::NativeView parent, - const gfx::Rect& bounds) { + const gfx::Rect& bounds, + gfx::AcceleratedWidget parent_widget) { DialogDelegate* dialog = delegate->AsDialogDelegate(); views::Widget::InitParams params( @@ -129,7 +134,7 @@ Widget::InitParams DialogDelegate::GetDialogWidgetInitParams( params.bounds = bounds; if (dialog) - dialog->params_.custom_frame &= CanSupportCustomFrame(parent); + dialog->params_.custom_frame &= CanSupportCustomFrame(parent, parent_widget); if (!dialog || dialog->use_custom_frame()) { params.opacity = Widget::InitParams::WindowOpacity::kTranslucent; @@ -142,6 +147,7 @@ Widget::InitParams DialogDelegate::GetDialogWidgetInitParams( } params.context = context; params.parent = parent; + params.parent_widget = parent_widget; #if !BUILDFLAG(IS_APPLE) // Web-modal (ui::mojom::ModalType::kChild) dialogs with parents are marked as // child widgets to prevent top-level window behavior (independent movement, diff --git ui/views/window/dialog_delegate.h ui/views/window/dialog_delegate.h index 0777e2fe71f04..546994cad2b14 100644 --- ui/views/window/dialog_delegate.h +++ ui/views/window/dialog_delegate.h @@ -106,13 +106,18 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { // your use case. static Widget* CreateDialogWidget(std::unique_ptr delegate, gfx::NativeWindow context, - gfx::NativeView parent); + gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget = + gfx::kNullAcceleratedWidget); static Widget* CreateDialogWidget(WidgetDelegate* delegate, gfx::NativeWindow context, - gfx::NativeView parent); + gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget = + gfx::kNullAcceleratedWidget); // Whether using custom dialog frame is supported for this dialog. - static bool CanSupportCustomFrame(gfx::NativeView parent); + static bool CanSupportCustomFrame(gfx::NativeView parent, + gfx::AcceleratedWidget parent_widget); // Returns the dialog widget InitParams for a given |context| or |parent|. // If |bounds| is not empty, used to initially place the dialog, otherwise @@ -120,7 +125,9 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { static Widget::InitParams GetDialogWidgetInitParams(WidgetDelegate* delegate, gfx::NativeWindow context, gfx::NativeView parent, - const gfx::Rect& bounds); + const gfx::Rect& bounds, + gfx::AcceleratedWidget parent_widget = + gfx::kNullAcceleratedWidget); // Returns a mask specifying which of the available DialogButtons are visible // for the dialog.