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 <set>
6
7#include "base/memory/scoped_ptr.h"
8#include "chrome/browser/platform_util.h"
9#include "chrome/browser/ui/views/constrained_window_views.h"
10#include "components/web_modal/native_web_contents_modal_dialog_manager.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 "content/public/browser/web_contents_view.h"
14#include "ui/gfx/point.h"
15#include "ui/gfx/size.h"
16#include "ui/views/border.h"
17#include "ui/views/widget/widget.h"
18#include "ui/views/widget/widget_delegate.h"
19#include "ui/views/widget/widget_observer.h"
20#include "ui/views/window/dialog_delegate.h"
21#include "ui/views/window/non_client_view.h"
22
23#if defined(USE_AURA)
24#include "ui/aura/client/aura_constants.h"
25#include "ui/aura/window.h"
26#include "ui/views/corewm/visibility_controller.h"
27#include "ui/views/corewm/window_animations.h"
28#include "ui/views/corewm/window_modality_controller.h"
29#endif
30
31// TODO(wittman): this code should not depend on ash.
32#if defined(USE_ASH)
33#include "ash/ash_constants.h"
34#include "ash/shell.h"
35#include "ash/wm/custom_frame_view_ash.h"
36#endif
37
38using web_modal::NativeWebContentsModalDialog;
39using web_modal::NativeWebContentsModalDialogManager;
40using web_modal::NativeWebContentsModalDialogManagerDelegate;
41using web_modal::WebContentsModalDialogHost;
42using web_modal::ModalDialogHostObserver;
43
44namespace {
45
46class NativeWebContentsModalDialogManagerViews
47    : public NativeWebContentsModalDialogManager,
48      public ModalDialogHostObserver,
49      public views::WidgetObserver {
50 public:
51  NativeWebContentsModalDialogManagerViews(
52      NativeWebContentsModalDialogManagerDelegate* native_delegate)
53      : native_delegate_(native_delegate),
54        host_(NULL) {
55  }
56
57  virtual ~NativeWebContentsModalDialogManagerViews() {
58    if (host_)
59      host_->RemoveObserver(this);
60
61    for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
62         it != observed_widgets_.end();
63         ++it) {
64      (*it)->RemoveObserver(this);
65    }
66  }
67
68  // NativeWebContentsModalDialogManager overrides
69  virtual void ManageDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
70    views::Widget* widget = GetWidget(dialog);
71    widget->AddObserver(this);
72    observed_widgets_.insert(widget);
73    widget->set_movement_disabled(true);
74
75#if defined(USE_AURA)
76    // TODO(wittman): remove once the new visual style is complete
77    widget->GetNativeWindow()->SetProperty(aura::client::kConstrainedWindowKey,
78                                           true);
79
80    views::corewm::SetWindowVisibilityAnimationType(
81        widget->GetNativeWindow(),
82        views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE);
83#endif
84
85#if defined(USE_ASH)
86    gfx::NativeView parent = platform_util::GetParent(widget->GetNativeView());
87    views::corewm::SetChildWindowVisibilityChangesAnimated(parent);
88    // No animations should get performed on the window since that will re-order
89    // the window stack which will then cause many problems.
90    if (parent && parent->parent()) {
91      parent->parent()->SetProperty(aura::client::kAnimationsDisabledKey, true);
92    }
93
94    views::corewm::SetModalParent(
95        widget->GetNativeWindow(),
96        platform_util::GetParent(widget->GetNativeView()));
97#endif
98  }
99
100  virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
101    views::Widget* widget = GetWidget(dialog);
102#if defined(USE_AURA)
103    scoped_ptr<views::corewm::SuspendChildWindowVisibilityAnimations> suspend;
104    if (shown_widgets_.find(widget) != shown_widgets_.end()) {
105      suspend.reset(new views::corewm::SuspendChildWindowVisibilityAnimations(
106          widget->GetNativeWindow()->parent()));
107    }
108#endif
109    // Host may be NULL during tab drag on Views/Win32.
110    if (host_)
111      UpdateWebContentsModalDialogPosition(widget, host_);
112    widget->Show();
113    FocusDialog(dialog);
114
115#if defined(USE_AURA)
116    // TODO(pkotwicz): Control the z-order of the constrained dialog via
117    // views::kHostViewKey. We will need to ensure that the parent window's
118    // shadows are below the constrained dialog in z-order when we do this.
119    shown_widgets_.insert(widget);
120#endif
121  }
122
123  virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
124    views::Widget* widget = GetWidget(dialog);
125#if defined(USE_AURA)
126    scoped_ptr<views::corewm::SuspendChildWindowVisibilityAnimations> suspend;
127    suspend.reset(new views::corewm::SuspendChildWindowVisibilityAnimations(
128        widget->GetNativeWindow()->parent()));
129#endif
130    widget->Hide();
131  }
132
133  virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
134    GetWidget(dialog)->Close();
135  }
136
137  virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
138    views::Widget* widget = GetWidget(dialog);
139    if (widget->widget_delegate() &&
140        widget->widget_delegate()->GetInitiallyFocusedView())
141      widget->widget_delegate()->GetInitiallyFocusedView()->RequestFocus();
142#if defined(USE_ASH)
143    // We don't necessarily have a RootWindow yet.
144    if (widget->GetNativeView()->GetRootWindow())
145      widget->GetNativeView()->Focus();
146#endif
147  }
148
149  virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
150  }
151
152  // WebContentsModalDialogHostObserver overrides
153  virtual void OnPositionRequiresUpdate() OVERRIDE {
154    DCHECK(host_);
155
156    for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
157         it != observed_widgets_.end();
158         ++it) {
159      UpdateWebContentsModalDialogPosition(*it, host_);
160    }
161  }
162
163  virtual void OnHostDestroying() OVERRIDE {
164    host_->RemoveObserver(this);
165    host_ = NULL;
166  }
167
168  // views::WidgetObserver overrides
169
170  // NOTE(wittman): OnWidgetClosing is overriden to ensure that, when the widget
171  // is explicitly closed, the destruction occurs within the same call
172  // stack. This avoids event races that lead to non-deterministic destruction
173  // ordering in e.g. the print preview dialog. OnWidgetDestroying is overridden
174  // because OnWidgetClosing is *only* invoked on explicit close, not when the
175  // widget is implicitly destroyed due to its parent being closed. This
176  // situation occurs with app windows.  WidgetClosing removes the observer, so
177  // only one of these two functions is ever invoked for a given widget.
178  virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
179    WidgetClosing(widget);
180  }
181
182  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
183    WidgetClosing(widget);
184  }
185
186  virtual void HostChanged(
187      web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {
188    if (host_)
189      host_->RemoveObserver(this);
190
191    host_ = new_host;
192
193    // |host_| may be null during WebContents destruction or Win32 tab dragging.
194    if (host_) {
195      host_->AddObserver(this);
196
197      for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
198           it != observed_widgets_.end();
199           ++it) {
200        views::Widget::ReparentNativeView((*it)->GetNativeView(),
201                                          host_->GetHostView());
202      }
203
204      OnPositionRequiresUpdate();
205    }
206  }
207
208 private:
209  static views::Widget* GetWidget(NativeWebContentsModalDialog dialog) {
210    views::Widget* widget = views::Widget::GetWidgetForNativeWindow(dialog);
211    DCHECK(widget);
212    return widget;
213  }
214
215  void WidgetClosing(views::Widget* widget) {
216#if defined(USE_ASH)
217    gfx::NativeView view = platform_util::GetParent(widget->GetNativeView());
218    // Allow the parent to animate again.
219    if (view && view->parent())
220      view->parent()->ClearProperty(aura::client::kAnimationsDisabledKey);
221#endif
222    widget->RemoveObserver(this);
223    native_delegate_->WillClose(widget->GetNativeView());
224    observed_widgets_.erase(widget);
225#if defined(USE_AURA)
226    shown_widgets_.erase(widget);
227#endif
228  }
229
230  NativeWebContentsModalDialogManagerDelegate* native_delegate_;
231  WebContentsModalDialogHost* host_;
232  std::set<views::Widget*> observed_widgets_;
233  std::set<views::Widget*> shown_widgets_;
234
235  DISALLOW_COPY_AND_ASSIGN(NativeWebContentsModalDialogManagerViews);
236};
237
238}  // namespace
239
240namespace web_modal {
241
242NativeWebContentsModalDialogManager* WebContentsModalDialogManager::
243CreateNativeManager(
244    NativeWebContentsModalDialogManagerDelegate* native_delegate) {
245  return new NativeWebContentsModalDialogManagerViews(native_delegate);
246}
247
248}  // namespace web_modal
249