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
5#include "chrome/browser/ui/views/constrained_window_views.h"
6
7#include <algorithm>
8
9#include "chrome/browser/guest_view/web_view/web_view_guest.h"
10#include "chrome/browser/ui/browser_finder.h"
11#include "components/web_modal/web_contents_modal_dialog_host.h"
12#include "components/web_modal/web_contents_modal_dialog_manager.h"
13#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
14#include "ui/views/border.h"
15#include "ui/views/widget/widget.h"
16#include "ui/views/widget/widget_observer.h"
17#include "ui/views/window/dialog_delegate.h"
18
19using web_modal::ModalDialogHost;
20using web_modal::ModalDialogHostObserver;
21
22namespace {
23// The name of a key to store on the window handle to associate
24// BrowserModalDialogHostObserverViews with the Widget.
25const char* const kBrowserModalDialogHostObserverViewsKey =
26    "__BROWSER_MODAL_DIALOG_HOST_OBSERVER_VIEWS__";
27
28// Applies positioning changes from the ModalDialogHost to the Widget.
29class BrowserModalDialogHostObserverViews
30    : public views::WidgetObserver,
31      public ModalDialogHostObserver {
32 public:
33  BrowserModalDialogHostObserverViews(ModalDialogHost* host,
34                                      views::Widget* target_widget,
35                                      const char *const native_window_property)
36      : host_(host),
37        target_widget_(target_widget),
38        native_window_property_(native_window_property) {
39    DCHECK(host_);
40    DCHECK(target_widget_);
41    host_->AddObserver(this);
42    target_widget_->AddObserver(this);
43  }
44
45  virtual ~BrowserModalDialogHostObserverViews() {
46    if (host_)
47      host_->RemoveObserver(this);
48    target_widget_->RemoveObserver(this);
49    target_widget_->SetNativeWindowProperty(native_window_property_, NULL);
50  }
51
52  // WidgetObserver overrides
53  virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
54    delete this;
55  }
56
57  // WebContentsModalDialogHostObserver overrides
58  virtual void OnPositionRequiresUpdate() OVERRIDE {
59    UpdateBrowserModalDialogPosition(target_widget_, host_);
60  }
61
62  virtual void OnHostDestroying() OVERRIDE {
63    host_->RemoveObserver(this);
64    host_ = NULL;
65  }
66
67 private:
68  ModalDialogHost* host_;
69  views::Widget* target_widget_;
70  const char* const native_window_property_;
71
72  DISALLOW_COPY_AND_ASSIGN(BrowserModalDialogHostObserverViews);
73};
74
75void UpdateModalDialogPosition(views::Widget* widget,
76                               web_modal::ModalDialogHost* dialog_host,
77                               const gfx::Size& size) {
78  // Do not forcibly update the dialog widget position if it is being dragged.
79  if (widget->HasCapture())
80    return;
81
82  gfx::Point position = dialog_host->GetDialogPosition(size);
83  views::Border* border = widget->non_client_view()->frame_view()->border();
84  // Border may be null during widget initialization.
85  if (border) {
86    // Align the first row of pixels inside the border. This is the apparent
87    // top of the dialog.
88    position.set_y(position.y() - border->GetInsets().top());
89  }
90
91  if (widget->is_top_level()) {
92    position +=
93        views::Widget::GetWidgetForNativeView(dialog_host->GetHostView())->
94            GetClientAreaBoundsInScreen().OffsetFromOrigin();
95  }
96
97  widget->SetBounds(gfx::Rect(position, size));
98}
99
100}  // namespace
101
102void UpdateWebContentsModalDialogPosition(
103    views::Widget* widget,
104    web_modal::WebContentsModalDialogHost* dialog_host) {
105  gfx::Size size = widget->GetRootView()->GetPreferredSize();
106  gfx::Size max_size = dialog_host->GetMaximumDialogSize();
107  // Enlarge the max size by the top border, as the dialog will be shifted
108  // outside the area specified by the dialog host by this amount later.
109  views::Border* border =
110      widget->non_client_view()->frame_view()->border();
111  // Border may be null during widget initialization.
112  if (border)
113    max_size.Enlarge(0, border->GetInsets().top());
114  size.SetToMin(max_size);
115  UpdateModalDialogPosition(widget, dialog_host, size);
116}
117
118void UpdateBrowserModalDialogPosition(views::Widget* widget,
119                                      web_modal::ModalDialogHost* dialog_host) {
120  UpdateModalDialogPosition(widget, dialog_host,
121                            widget->GetRootView()->GetPreferredSize());
122}
123
124views::Widget* ShowWebModalDialogViews(
125    views::WidgetDelegate* dialog,
126    content::WebContents* initiator_web_contents) {
127  WebViewGuest* web_view_guest = WebViewGuest::FromWebContents(
128      initiator_web_contents);
129  // For embedded WebContents, use the embedder's WebContents for constrained
130  // window.
131  content::WebContents* web_contents =
132      web_view_guest && web_view_guest->embedder_web_contents() ?
133          web_view_guest->embedder_web_contents() : initiator_web_contents;
134  views::Widget* widget = CreateWebModalDialogViews(dialog, web_contents);
135  web_modal::WebContentsModalDialogManager::FromWebContents(web_contents)->
136      ShowModalDialog(widget->GetNativeWindow());
137  return widget;
138}
139
140views::Widget* CreateWebModalDialogViews(views::WidgetDelegate* dialog,
141                                         content::WebContents* web_contents) {
142  DCHECK_EQ(ui::MODAL_TYPE_CHILD, dialog->GetModalType());
143  web_modal::WebContentsModalDialogManager* manager =
144      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
145  const gfx::NativeWindow parent =
146      manager->delegate()->GetWebContentsModalDialogHost()->GetHostView();
147  return views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent);
148}
149
150views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog,
151                                             gfx::NativeWindow parent) {
152  views::Widget* widget =
153      views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent);
154  if (!dialog->UseNewStyleForThisDialog())
155    return widget;
156
157  // Get the browser dialog management and hosting components from |parent|.
158  Browser* browser = chrome::FindBrowserWithWindow(parent);
159  if (browser) {
160    ChromeWebModalDialogManagerDelegate* manager = browser;
161    ModalDialogHost* host = manager->GetWebContentsModalDialogHost();
162    DCHECK_EQ(parent, host->GetHostView());
163    ModalDialogHostObserver* dialog_host_observer =
164        new BrowserModalDialogHostObserverViews(
165            host, widget, kBrowserModalDialogHostObserverViewsKey);
166    dialog_host_observer->OnPositionRequiresUpdate();
167  }
168  return widget;
169}
170