web_contents_modal_dialog_manager_views.cc revision f2477e01787aa58f445919b809d89e252beef54f
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    // TODO(wittman): remove once the new visual style is complete
95    widget->GetNativeWindow()->SetProperty(ash::kConstrainedWindowKey, true);
96    views::corewm::SetModalParent(
97        widget->GetNativeWindow(),
98        platform_util::GetParent(widget->GetNativeView()));
99#endif
100  }
101
102  virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
103    views::Widget* widget = GetWidget(dialog);
104#if defined(USE_AURA)
105    scoped_ptr<views::corewm::SuspendChildWindowVisibilityAnimations> suspend;
106    if (shown_widgets_.find(widget) != shown_widgets_.end()) {
107      suspend.reset(new views::corewm::SuspendChildWindowVisibilityAnimations(
108          widget->GetNativeWindow()->parent()));
109    }
110#endif
111    // Host may be NULL during tab drag on Views/Win32.
112    if (host_)
113      UpdateWebContentsModalDialogPosition(widget, host_);
114    widget->Show();
115    FocusDialog(dialog);
116
117#if defined(USE_AURA)
118    // TODO(pkotwicz): Control the z-order of the constrained dialog via
119    // views::kHostViewKey. We will need to ensure that the parent window's
120    // shadows are below the constrained dialog in z-order when we do this.
121    shown_widgets_.insert(widget);
122#endif
123  }
124
125  virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
126    views::Widget* widget = GetWidget(dialog);
127#if defined(USE_AURA)
128    scoped_ptr<views::corewm::SuspendChildWindowVisibilityAnimations> suspend;
129    suspend.reset(new views::corewm::SuspendChildWindowVisibilityAnimations(
130        widget->GetNativeWindow()->parent()));
131#endif
132    widget->Hide();
133  }
134
135  virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
136    GetWidget(dialog)->Close();
137  }
138
139  virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
140    views::Widget* widget = GetWidget(dialog);
141    if (widget->widget_delegate() &&
142        widget->widget_delegate()->GetInitiallyFocusedView())
143      widget->widget_delegate()->GetInitiallyFocusedView()->RequestFocus();
144#if defined(USE_ASH)
145    // We don't necessarily have a RootWindow yet.
146    if (widget->GetNativeView()->GetRootWindow())
147      widget->GetNativeView()->Focus();
148#endif
149  }
150
151  virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
152  }
153
154  // WebContentsModalDialogHostObserver overrides
155  virtual void OnPositionRequiresUpdate() OVERRIDE {
156    DCHECK(host_);
157
158    for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
159         it != observed_widgets_.end();
160         ++it) {
161      UpdateWebContentsModalDialogPosition(*it, host_);
162    }
163  }
164
165  virtual void OnHostDestroying() OVERRIDE {
166    host_->RemoveObserver(this);
167    host_ = NULL;
168  }
169
170  // views::WidgetObserver overrides
171
172  // NOTE(wittman): OnWidgetClosing is overriden to ensure that, when the widget
173  // is explicitly closed, the destruction occurs within the same call
174  // stack. This avoids event races that lead to non-deterministic destruction
175  // ordering in e.g. the print preview dialog. OnWidgetDestroying is overridden
176  // because OnWidgetClosing is *only* invoked on explicit close, not when the
177  // widget is implicitly destroyed due to its parent being closed. This
178  // situation occurs with app windows.  WidgetClosing removes the observer, so
179  // only one of these two functions is ever invoked for a given widget.
180  virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
181    WidgetClosing(widget);
182  }
183
184  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
185    WidgetClosing(widget);
186  }
187
188  virtual void HostChanged(
189      web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {
190    if (host_)
191      host_->RemoveObserver(this);
192
193    host_ = new_host;
194
195    // |host_| may be null during WebContents destruction or Win32 tab dragging.
196    if (host_) {
197      host_->AddObserver(this);
198
199      for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
200           it != observed_widgets_.end();
201           ++it) {
202        views::Widget::ReparentNativeView((*it)->GetNativeView(),
203                                          host_->GetHostView());
204      }
205
206      OnPositionRequiresUpdate();
207    }
208  }
209
210 private:
211  static views::Widget* GetWidget(NativeWebContentsModalDialog dialog) {
212    views::Widget* widget = views::Widget::GetWidgetForNativeWindow(dialog);
213    DCHECK(widget);
214    return widget;
215  }
216
217  void WidgetClosing(views::Widget* widget) {
218#if defined(USE_ASH)
219    gfx::NativeView view = platform_util::GetParent(widget->GetNativeView());
220    // Allow the parent to animate again.
221    if (view && view->parent())
222      view->parent()->ClearProperty(aura::client::kAnimationsDisabledKey);
223#endif
224    widget->RemoveObserver(this);
225    native_delegate_->WillClose(widget->GetNativeView());
226    observed_widgets_.erase(widget);
227#if defined(USE_AURA)
228    shown_widgets_.erase(widget);
229#endif
230  }
231
232  NativeWebContentsModalDialogManagerDelegate* native_delegate_;
233  WebContentsModalDialogHost* host_;
234  std::set<views::Widget*> observed_widgets_;
235  std::set<views::Widget*> shown_widgets_;
236
237  DISALLOW_COPY_AND_ASSIGN(NativeWebContentsModalDialogManagerViews);
238};
239
240}  // namespace
241
242namespace web_modal {
243
244NativeWebContentsModalDialogManager* WebContentsModalDialogManager::
245CreateNativeManager(
246    NativeWebContentsModalDialogManagerDelegate* native_delegate) {
247  return new NativeWebContentsModalDialogManagerViews(native_delegate);
248}
249
250}  // namespace web_modal
251