Index: ui/views/cocoa/bridged_native_widget.mm |
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm |
index 16af930118c3a8fdb3e9b673893bcc1a3b7512ef..7a1225ca7de064346ce11887334687304fcd72fd 100644 |
--- a/ui/views/cocoa/bridged_native_widget.mm |
+++ b/ui/views/cocoa/bridged_native_widget.mm |
@@ -9,6 +9,7 @@ |
#include <stdint.h> |
#include "base/logging.h" |
+#import "base/mac/bind_objc_block.h" |
#import "base/mac/foundation_util.h" |
#include "base/mac/mac_util.h" |
#import "base/mac/sdk_forward_declarations.h" |
@@ -28,6 +29,7 @@ |
#import "ui/views/cocoa/cocoa_mouse_capture.h" |
#import "ui/views/cocoa/cocoa_window_move_loop.h" |
#include "ui/views/cocoa/tooltip_manager_mac.h" |
+#import "ui/views/cocoa/views_nswindow_close_animator.h" |
#import "ui/views/cocoa/views_nswindow_delegate.h" |
#import "ui/views/cocoa/widget_owner_nswindow_adapter.h" |
#include "ui/views/view.h" |
@@ -350,7 +352,7 @@ BridgedNativeWidget::~BridgedNativeWidget() { |
// DialogClientView assumes its delegate is alive when closing, which isn't |
// true after endSheet: synchronously calls OnNativeWidgetDestroyed(). |
// So ban it. Modal dialogs should be closed via Widget::Close(). |
- DCHECK(!native_widget_mac_->IsWindowModalSheet()); |
+ DCHECK(!IsWindowModalSheet()); |
// If the delegate is still set, it means OnWindowWillClose() has not been |
// called and the window is still open. Usually, -[NSWindow close] would |
@@ -494,7 +496,7 @@ void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { |
DCHECK(!clamped_content_size.IsEmpty()) |
<< "Zero-sized windows not supported on Mac"; |
- if (!window_visible_ && native_widget_mac_->IsWindowModalSheet()) { |
+ if (!window_visible_ && IsWindowModalSheet()) { |
// Window-Modal dialogs (i.e. sheets) are positioned by Cocoa when shown for |
// the first time. They also have no frame, so just update the content size. |
[window_ setContentSize:NSMakeSize(clamped_content_size.width(), |
@@ -559,7 +561,7 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { |
if (parent() && !parent()->IsVisibleParent()) |
return; |
- if (native_widget_mac_->IsWindowModalSheet()) { |
+ if (IsWindowModalSheet()) { |
ShowAsModalSheet(); |
return; |
} |
@@ -596,6 +598,46 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { |
} |
} |
+void BridgedNativeWidget::CloseAsynchronously() { |
+ // In a past life, the window would be synchronously hidden for the non-modal |
+ // case. AppKit UI could do funny things before an asynchronous close finished |
+ // (see http://crbug.com/156101). But that shouldn't be needed with toolkit- |
+ // views. But a synchronous hide would have released capture, so do it now. |
+ ReleaseCapture(); |
+ |
+ if (IsWindowModalSheet()) { |
+ // Sheets can't be closed normally. This starts the sheet closing. Once the |
+ // sheet has finished animating, it will call sheetDidEnd: on the parent |
+ // window's delegate. Note it still needs to be asynchronous, since code |
+ // calling Widget::Close() doesn't expect things to be deleted upon return. |
+ [NSApp performSelector:@selector(endSheet:) |
+ withObject:window_ |
+ afterDelay:0]; |
+ return; |
+ } |
+ |
+ // For other modal types, animate the close. |
+ if (native_widget_mac_->GetWidget()->IsModal()) { |
+ CloseNativeWindowWithAnimation(window_); |
+ return; |
+ } |
+ |
+ // Clear the view early to suppress repaints. |
+ SetRootView(nullptr); |
+ |
+ // Widget::Close() ensures [Non]ClientView::CanClose() returns true, so there |
+ // is no need to call the NSWindow or its delegate's -windowShouldClose: |
+ // implementation in the manner of -[NSWindow performClose:]. |
+ |
+ // Many tests assume that base::RunLoop().RunUntilIdle() is always sufficient |
+ // to execute a close. However, in rare cases, -performSelector:..afterDelay:0 |
+ // does not do this. So post a regular task. |
+ NSWindow* window = window_; // Ensure the block retains the window. |
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindBlock(^{ |
+ [window close]; |
+ })); |
+} |
+ |
void BridgedNativeWidget::AcquireCapture() { |
DCHECK(!HasCapture()); |
if (!window_visible_) |
@@ -809,6 +851,21 @@ void BridgedNativeWidget::OnVisibilityChanged() { |
[parent_->GetNSWindow() removeChildWindow:window_]; |
} |
+ NotifyVisibilityChangeDown(); |
+ |
+ // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking |
+ // for an "empty" draw, disable auto-display while hidden. For example, this |
+ // prevents Cocoa drawing just *after* a minimize, resulting in a blank window |
+ // represented in the deminiaturize animation. |
+ [window_ setAutodisplay:window_visible_]; |
+ |
+ // |bridged_view_| is null when an asynchronous close is pending. Don't |
+ // propagate the change further. Note it's not possible to block window |
+ // visibility changes coming from native messages, so the bookkeeping above |
+ // should still be done. |
+ if (!bridged_view_) |
+ return; |
+ |
// TODO(tapted): Investigate whether we want this for Mac. This is what Aura |
// does, and it is what tests expect. However, because layer drawing is |
// asynchronous (and things like deminiaturize in AppKit are not), it can |
@@ -821,16 +878,8 @@ void BridgedNativeWidget::OnVisibilityChanged() { |
layer()->SchedulePaint(gfx::Rect(GetClientAreaSize())); |
} |
- NotifyVisibilityChangeDown(); |
- |
native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged( |
window_visible_); |
- |
- // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking |
- // for an "empty" draw, disable auto-display while hidden. For example, this |
- // prevents Cocoa drawing just *after* a minimize, resulting in a blank window |
- // represented in the deminiaturize animation. |
- [window_ setAutodisplay:window_visible_]; |
} |
void BridgedNativeWidget::OnBackingPropertiesChanged() { |
@@ -962,7 +1011,7 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type, |
// the composited layer. This assumes the native window shape is a good match |
// for the composited NonClientFrameView, which should be the case since the |
// native shape is what's most appropriate for displaying sheets on Mac. |
- if (translucent && !native_widget_mac_->IsWindowModalSheet()) { |
+ if (translucent && !IsWindowModalSheet()) { |
[window_ setOpaque:NO]; |
// For Mac OS versions earlier than Yosemite, the Window server isn't able |
// to generate a window shadow from the composited CALayer. To get around |
@@ -1320,6 +1369,11 @@ void BridgedNativeWidget::MaybeWaitForFrame(const gfx::Size& size_in_dip) { |
} |
} |
+bool BridgedNativeWidget::IsWindowModalSheet() const { |
+ return native_widget_mac_->GetWidget()->widget_delegate()->GetModalType() == |
+ ui::MODAL_TYPE_WINDOW; |
+} |
+ |
void BridgedNativeWidget::ShowAsModalSheet() { |
// -[NSApp beginSheet:] will block the UI thread while the animation runs. |
// So that it doesn't animate a fully transparent window, first wait for a |