OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 #import "chrome/browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac.h" | |
6 | |
7 #import <Cocoa/Cocoa.h> | |
8 | |
9 #import "base/mac/foundation_util.h" | |
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h" | |
11 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_con
troller.h" | |
12 #include "content/public/browser/web_contents.h" | |
13 #include "components/web_modal/web_contents_modal_dialog_host.h" | |
14 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" | |
15 #include "ui/views/widget/widget.h" | |
16 | |
17 // A wrapper for a views::Widget dialog to interact with a Cocoa browser's | |
18 // ContrainedWindowSheetController. Similar to CustomConstrainedWindowSheet, but | |
19 // since Widgets of dialog type animate themselves, and also manage their own | |
20 // parenting, there's not much to do except position properly. | |
21 @interface WrappedConstrainedWindowSheet : NSObject<ConstrainedWindowSheet> { | |
22 @private | |
23 base::scoped_nsobject<NSWindow> customWindow_; | |
24 } | |
25 - (id)initWithCustomWindow:(NSWindow*)customWindow; | |
26 @end | |
27 | |
28 @implementation WrappedConstrainedWindowSheet | |
29 | |
30 - (id)initWithCustomWindow:(NSWindow*)customWindow { | |
31 if ((self = [super init])) { | |
32 customWindow_.reset([customWindow retain]); | |
33 } | |
34 return self; | |
35 } | |
36 | |
37 // ConstrainedWindowSheet implementation. | |
38 | |
39 - (void)showSheetForWindow:(NSWindow*)window { | |
40 // This is only called for the initial show, then calls go to unhideSheet. | |
41 // Since Widget::Show() will be called, just update the position. | |
42 [self updateSheetPosition]; | |
43 } | |
44 | |
45 - (void)closeSheetWithAnimation:(BOOL)withAnimation { | |
46 // Nothing to do here. Either SingleWebContentsDialogManagerViewsMac::Close() | |
47 // was called or Widget::Close(). Both cases ending up in OnWidgetClosing() to | |
48 // call [ConstrainedWindowSheetController closeSheet:], which calls this. | |
49 // However, the Widget is already closing in those cases. | |
50 | |
51 // OnWidgetClosing() is also the _only_ trigger. The exception would be | |
52 // -[ConstrainedWindowSheetController onParentWindowWillClose:] which also | |
53 // calls closeSheetWithAnimation:, but a Widget never gets there because | |
54 // WebContentsModalDialogManager::CloseAllDialogs() is triggered from | |
55 // -[BrowserWindowController windowShouldClose:], but the notification that | |
56 // calls onParentWindowWillClose always happens once that has returned YES. | |
57 | |
58 // So, since onParentWindowWillClose never calls this, we can assert that | |
59 // withAnimation is YES, otherwise there's some code path that might not be | |
60 // catered for. | |
61 DCHECK(withAnimation); | |
62 } | |
63 | |
64 - (void)hideSheet { | |
65 // Hide the sheet window by setting the alpha to 0. This technique is used | |
66 // instead of -orderOut: because that may cause a Spaces change or window | |
67 // ordering change. | |
68 [customWindow_ setAlphaValue:0.0]; | |
69 // TODO(tapted): Support child windows. | |
70 DCHECK_EQ(0u, [[customWindow_ childWindows] count]); | |
71 } | |
72 | |
73 - (void)unhideSheet { | |
74 [customWindow_ setAlphaValue:1.0]; | |
75 DCHECK_EQ(0u, [[customWindow_ childWindows] count]); | |
76 } | |
77 | |
78 - (void)pulseSheet { | |
79 base::scoped_nsobject<NSAnimation> animation( | |
80 [[ConstrainedWindowAnimationPulse alloc] initWithWindow:customWindow_]); | |
81 [animation startAnimation]; | |
82 } | |
83 | |
84 - (void)makeSheetKeyAndOrderFront { | |
85 // If the window is not visible, do nothing. Widget::Show() is responsible for | |
86 // showing, and it may want to animate it. | |
87 if ([customWindow_ isVisible]) | |
88 [customWindow_ makeKeyAndOrderFront:nil]; | |
89 } | |
90 | |
91 - (void)updateSheetPosition { | |
92 ConstrainedWindowSheetController* controller = | |
93 [ConstrainedWindowSheetController controllerForSheet:self]; | |
94 NSPoint origin = [controller originForSheet:self | |
95 withWindowSize:[customWindow_ frame].size]; | |
96 [customWindow_ setFrameOrigin:origin]; | |
97 } | |
98 | |
99 - (void)resizeWithNewSize:(NSSize)size { | |
100 // NOOP | |
101 } | |
102 | |
103 - (NSWindow*)sheetWindow { | |
104 return customWindow_; | |
105 } | |
106 | |
107 @end | |
108 | |
109 SingleWebContentsDialogManagerViewsMac::SingleWebContentsDialogManagerViewsMac( | |
110 NSWindow* dialog, | |
111 web_modal::SingleWebContentsDialogManagerDelegate* delegate) | |
112 : delegate_(delegate), host_(nullptr) { | |
113 sheet_.reset( | |
114 [[WrappedConstrainedWindowSheet alloc] initWithCustomWindow:dialog]); | |
115 widget_ = views::Widget::GetWidgetForNativeWindow(dialog); | |
116 DCHECK(widget_); | |
117 widget_->AddObserver(this); | |
118 } | |
119 | |
120 SingleWebContentsDialogManagerViewsMac:: | |
121 ~SingleWebContentsDialogManagerViewsMac() { | |
122 DCHECK(!widget_->HasObserver(this)); | |
123 } | |
124 | |
125 void SingleWebContentsDialogManagerViewsMac::Show() { | |
126 DCHECK(host_); | |
127 | |
128 NSView* parent_view = host_->GetHostView(); | |
129 // Note that simply [parent_view window] for an inactive tab is nil. However, | |
130 // the following should always be non-nil for all WebContents containers. | |
131 NSWindow* parent_window = | |
132 delegate_->GetWebContents()->GetTopLevelNativeWindow(); | |
133 // Register with the ConstrainedWindowSheetController. This ensures that, e.g. | |
134 // the NSView that overlays the Cocoa WebContents to intercept clicks is | |
135 // installed and managed. | |
136 [[ConstrainedWindowSheetController controllerForParentWindow:parent_window] | |
137 showSheet:sheet_ | |
138 forParentView:parent_view]; | |
139 | |
140 if (!widget_->IsVisible()) | |
141 widget_->Show(); | |
142 } | |
143 | |
144 void SingleWebContentsDialogManagerViewsMac::Hide() { | |
145 NSWindow* parent_window = | |
146 delegate_->GetWebContents()->GetTopLevelNativeWindow(); | |
147 [[ConstrainedWindowSheetController controllerForParentWindow:parent_window] | |
148 hideSheet]; | |
149 } | |
150 | |
151 void SingleWebContentsDialogManagerViewsMac::Close() { | |
152 // When the WebContents is destroyed, WebContentsModalDialogManager | |
153 // ::CloseAllDialogs will call this. Close the Widget in the same manner as | |
154 // the dialogs so that codepaths are consistent. | |
155 widget_->Close(); // Note: Synchronously calls OnWidgetClosing() below. | |
156 } | |
157 | |
158 void SingleWebContentsDialogManagerViewsMac::Focus() { | |
159 // Handled by ConstrainedWindowSheetController. | |
160 } | |
161 void SingleWebContentsDialogManagerViewsMac::Pulse() { | |
162 // Handled by ConstrainedWindowSheetController. | |
163 } | |
164 | |
165 void SingleWebContentsDialogManagerViewsMac::HostChanged( | |
166 web_modal::WebContentsModalDialogHost* new_host) { | |
167 // No need to observe the host. For Cocoa, the constrained window controller | |
168 // will reposition the dialog when necessary. The host can also never change. | |
169 // Tabs showing a dialog can not be dragged off a Cocoa browser window. | |
170 // However, closing a tab with a dialog open will set the host back to null. | |
171 DCHECK_NE(!!host_, !!new_host); | |
172 host_ = new_host; | |
173 } | |
174 | |
175 gfx::NativeWindow SingleWebContentsDialogManagerViewsMac::dialog() { | |
176 return [sheet_ sheetWindow]; | |
177 } | |
178 | |
179 // views::WidgetObserver: | |
180 void SingleWebContentsDialogManagerViewsMac::OnWidgetClosing( | |
181 views::Widget* widget) { | |
182 DCHECK_EQ(widget, widget_); | |
183 widget->RemoveObserver(this); | |
184 [[ConstrainedWindowSheetController controllerForSheet:sheet_] | |
185 closeSheet:sheet_]; | |
186 delegate_->WillClose(dialog()); // Deletes |this|. | |
187 } | |
188 | |
189 void SingleWebContentsDialogManagerViewsMac::OnWidgetDestroying( | |
190 views::Widget* widget) { | |
191 // On Mac, this would only be reached if something called -[NSWindow close] | |
192 // on the dialog without going through Widget::Close or CloseNow(). Since | |
193 // dialogs have no titlebar, it won't come from the system. If something | |
194 // internally calls -[NSWindow close] it might break lifetime assumptions | |
195 // made by DialogDelegate. | |
196 NOTREACHED(); | |
197 } | |
OLD | NEW |