constrained_window_views.cc 10 KB

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