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