[M120 Migration][VD] Enable direct rendering for TVPlus
[platform/framework/web/chromium-efl.git] / components / constrained_window / constrained_window_views.cc
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/constrained_window/constrained_window_views.h"
6 #include "base/memory/raw_ptr.h"
7
8 #include <algorithm>
9 #include <memory>
10
11 #include "base/functional/callback.h"
12 #include "base/no_destructor.h"
13 #include "build/build_config.h"
14 #include "components/constrained_window/constrained_window_views_client.h"
15 #include "components/guest_view/browser/guest_view_base.h"
16 #include "components/web_modal/web_contents_modal_dialog_host.h"
17 #include "components/web_modal/web_contents_modal_dialog_manager.h"
18 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
19 #include "ui/display/display.h"
20 #include "ui/display/screen.h"
21 #include "ui/views/bubble/bubble_dialog_model_host.h"
22 #include "ui/views/widget/native_widget.h"
23 #include "ui/views/widget/widget.h"
24 #include "ui/views/widget/widget_observer.h"
25 #include "ui/views/window/dialog_delegate.h"
26 #include "url/gurl.h"
27
28 #if BUILDFLAG(IS_OZONE)
29 #include "ui/ozone/public/ozone_platform.h"
30 #endif
31
32 using web_modal::ModalDialogHost;
33 using web_modal::ModalDialogHostObserver;
34
35 namespace constrained_window {
36
37 const void* kConstrainedWindowWidgetIdentifier = "ConstrainedWindowWidget";
38
39 namespace {
40
41 // Storage access for the currently active ConstrainedWindowViewsClient.
42 std::unique_ptr<ConstrainedWindowViewsClient>& CurrentClient() {
43   static base::NoDestructor<std::unique_ptr<ConstrainedWindowViewsClient>>
44       client;
45   return *client;
46 }
47
48 // The name of a key to store on the window handle to associate
49 // WidgetModalDialogHostObserverViews with the Widget.
50 const char* const kWidgetModalDialogHostObserverViewsKey =
51     "__WIDGET_MODAL_DIALOG_HOST_OBSERVER_VIEWS__";
52
53 // Applies positioning changes from the ModalDialogHost to the Widget.
54 class WidgetModalDialogHostObserverViews : public views::WidgetObserver,
55                                            public ModalDialogHostObserver {
56  public:
57   WidgetModalDialogHostObserverViews(ModalDialogHost* host,
58                                      views::Widget* target_widget,
59                                      const char* const native_window_property)
60       : host_(host),
61         target_widget_(target_widget),
62         native_window_property_(native_window_property) {
63     DCHECK(host_);
64     DCHECK(target_widget_);
65     host_->AddObserver(this);
66     target_widget_->AddObserver(this);
67   }
68
69   WidgetModalDialogHostObserverViews(
70       const WidgetModalDialogHostObserverViews&) = delete;
71   WidgetModalDialogHostObserverViews& operator=(
72       const WidgetModalDialogHostObserverViews&) = delete;
73
74   ~WidgetModalDialogHostObserverViews() override {
75     if (host_)
76       host_->RemoveObserver(this);
77     target_widget_->RemoveObserver(this);
78     target_widget_->SetNativeWindowProperty(native_window_property_, nullptr);
79     CHECK(!IsInObserverList());
80   }
81
82   // WidgetObserver overrides
83   void OnWidgetDestroying(views::Widget* widget) override { delete this; }
84
85   // WebContentsModalDialogHostObserver overrides
86   void OnPositionRequiresUpdate() override {
87     UpdateWidgetModalDialogPosition(target_widget_, host_);
88   }
89
90   void OnHostDestroying() override {
91     host_->RemoveObserver(this);
92     host_ = nullptr;
93   }
94
95  private:
96   raw_ptr<ModalDialogHost> host_;
97   raw_ptr<views::Widget> target_widget_;
98   const char* const native_window_property_;
99 };
100
101 void UpdateModalDialogPosition(views::Widget* widget,
102                                web_modal::ModalDialogHost* dialog_host,
103                                const gfx::Size& size) {
104   // Do not forcibly update the dialog widget position if it is being dragged.
105   if (widget->HasCapture())
106     return;
107
108   views::Widget* host_widget =
109       views::Widget::GetWidgetForNativeView(dialog_host->GetHostView());
110
111   // If the host view is not backed by a Views::Widget, just update the widget
112   // size. This can happen on MacViews under the Cocoa browser where the window
113   // modal dialogs are displayed as sheets, and their position is managed by a
114   // ConstrainedWindowSheetController instance.
115   if (!host_widget) {
116     widget->SetSize(size);
117     return;
118   }
119
120   gfx::Point position = dialog_host->GetDialogPosition(size);
121   // Align the first row of pixels inside the border. This is the apparent top
122   // of the dialog.
123   position.set_y(position.y() -
124                  widget->non_client_view()->frame_view()->GetInsets().top());
125
126   const bool supports_global_screen_coordinates =
127 #if !BUILDFLAG(IS_OZONE)
128       true;
129 #else
130       ui::OzonePlatform::GetInstance()
131           ->GetPlatformProperties()
132           .supports_global_screen_coordinates;
133 #endif
134
135   if (widget->is_top_level() && supports_global_screen_coordinates) {
136     position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
137     // If the dialog extends partially off any display, clamp its position to
138     // be fully visible within that display. If the dialog doesn't intersect
139     // with any display clamp its position to be fully on the nearest display.
140     gfx::Rect display_rect = gfx::Rect(position, size);
141     const display::Display display =
142         display::Screen::GetScreen()->GetDisplayNearestView(
143             dialog_host->GetHostView());
144     const gfx::Rect work_area = display.work_area();
145     if (!work_area.Contains(display_rect))
146       display_rect.AdjustToFit(work_area);
147     position = display_rect.origin();
148   }
149
150   widget->SetBounds(gfx::Rect(position, size));
151 }
152
153 }  // namespace
154
155 // static
156 void SetConstrainedWindowViewsClient(
157     std::unique_ptr<ConstrainedWindowViewsClient> new_client) {
158   CurrentClient() = std::move(new_client);
159 }
160
161 void UpdateWebContentsModalDialogPosition(
162     views::Widget* widget,
163     web_modal::WebContentsModalDialogHost* dialog_host) {
164   gfx::Size size = widget->GetRootView()->GetPreferredSize();
165   gfx::Size max_size = dialog_host->GetMaximumDialogSize();
166   // Enlarge the max size by the top border, as the dialog will be shifted
167   // outside the area specified by the dialog host by this amount later.
168   max_size.Enlarge(0,
169                    widget->non_client_view()->frame_view()->GetInsets().top());
170   size.SetToMin(max_size);
171   UpdateModalDialogPosition(widget, dialog_host, size);
172 }
173
174 void UpdateWidgetModalDialogPosition(views::Widget* widget,
175                                      web_modal::ModalDialogHost* dialog_host) {
176   UpdateModalDialogPosition(widget, dialog_host,
177                             widget->GetRootView()->GetPreferredSize());
178 }
179
180 content::WebContents* GetTopLevelWebContents(
181     content::WebContents* initiator_web_contents) {
182   // TODO(mcnee): While calling both `GetResponsibleWebContents` and
183   // `GetTopLevelWebContents` appears redundant, there appears to still be cases
184   // where users of guest view are not initializing the guest WebContents
185   // properly, causing GetResponsibleWebContents to break. See
186   // https://crbug.com/1325850
187   // The order of composing these methods is arbitrary.
188   return guest_view::GuestViewBase::GetTopLevelWebContents(
189       initiator_web_contents->GetResponsibleWebContents());
190 }
191
192 views::Widget* ShowWebModalDialogViews(
193     views::WidgetDelegate* dialog,
194     content::WebContents* initiator_web_contents) {
195   DCHECK(CurrentClient());
196   // For embedded WebContents, use the embedder's WebContents for constrained
197   // window.
198   content::WebContents* web_contents =
199       GetTopLevelWebContents(initiator_web_contents);
200   views::Widget* widget = CreateWebModalDialogViews(dialog, web_contents);
201   ShowModalDialog(widget->GetNativeWindow(), web_contents);
202   return widget;
203 }
204
205 views::Widget* CreateWebModalDialogViews(views::WidgetDelegate* dialog,
206                                          content::WebContents* web_contents) {
207   DCHECK_EQ(ui::MODAL_TYPE_CHILD, dialog->GetModalType());
208   web_modal::WebContentsModalDialogManager* manager =
209       web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
210
211   // TODO(http://crbug/1273287): Drop "if" and DEBUG_ALIAS_FOR_GURL after fix.
212   if (!manager) {
213     const GURL& url = web_contents->GetLastCommittedURL();
214     DEBUG_ALIAS_FOR_GURL(url_alias, url);
215     LOG_IF(FATAL, !manager)
216         << "CreateWebModalDialogViews without a manager"
217         << ", scheme=" << url.scheme_piece() << ", host=" << url.host_piece();
218   }
219
220   views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
221       dialog, nullptr,
222       manager->delegate()->GetWebContentsModalDialogHost()->GetHostView());
223   widget->SetNativeWindowProperty(
224       views::kWidgetIdentifierKey,
225       const_cast<void*>(kConstrainedWindowWidgetIdentifier));
226
227   return widget;
228 }
229
230 views::Widget* CreateBrowserModalDialogViews(
231     std::unique_ptr<views::DialogDelegate> dialog,
232     gfx::NativeWindow parent) {
233   return CreateBrowserModalDialogViews(dialog.release(), parent);
234 }
235
236 views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog,
237                                              gfx::NativeWindow parent) {
238   DCHECK_NE(ui::MODAL_TYPE_CHILD, dialog->GetModalType());
239   DCHECK_NE(ui::MODAL_TYPE_NONE, dialog->GetModalType());
240   DCHECK(!parent || CurrentClient());
241
242   gfx::NativeView parent_view =
243       parent ? CurrentClient()->GetDialogHostView(parent) : nullptr;
244   views::Widget* widget =
245       views::DialogDelegate::CreateDialogWidget(dialog, nullptr, parent_view);
246   widget->SetNativeWindowProperty(
247       views::kWidgetIdentifierKey,
248       const_cast<void*>(kConstrainedWindowWidgetIdentifier));
249
250   bool requires_positioning = dialog->use_custom_frame();
251
252 #if BUILDFLAG(IS_APPLE)
253   // On Mac, window modal dialogs are displayed as sheets, so their position is
254   // managed by the parent window.
255   requires_positioning = false;
256 #endif
257
258   if (!requires_positioning)
259     return widget;
260
261   ModalDialogHost* host =
262       parent ? CurrentClient()->GetModalDialogHost(parent) : nullptr;
263   if (host) {
264     DCHECK_EQ(parent_view, host->GetHostView());
265     ModalDialogHostObserver* dialog_host_observer =
266         new WidgetModalDialogHostObserverViews(
267             host, widget, kWidgetModalDialogHostObserverViewsKey);
268     dialog_host_observer->OnPositionRequiresUpdate();
269   }
270   return widget;
271 }
272
273 views::Widget* ShowBrowserModal(std::unique_ptr<ui::DialogModel> dialog_model,
274                                 gfx::NativeWindow parent) {
275   auto dialog = views::BubbleDialogModelHost::CreateModal(
276       std::move(dialog_model), ui::MODAL_TYPE_WINDOW);
277   dialog->SetOwnedByWidget(true);
278   auto* widget = constrained_window::CreateBrowserModalDialogViews(
279       std::move(dialog), parent);
280   widget->Show();
281   return widget;
282 }
283
284 views::Widget* ShowWebModal(std::unique_ptr<ui::DialogModel> dialog_model,
285                             content::WebContents* web_contents) {
286   return constrained_window::ShowWebModalDialogViews(
287       views::BubbleDialogModelHost::CreateModal(std::move(dialog_model),
288                                                 ui::MODAL_TYPE_CHILD)
289           .release(),
290       web_contents);
291 }
292
293 }  // namespace constrained_window