Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(545)

Side by Side Diff: ui/views/widget/native_widget_mac_unittest.mm

Issue 2056593002: Mac: Retain the child NSWindow in WidgetOwnerNSWindowAdapter before invoking close (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Refine DCHECK Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/cocoa/widget_owner_nswindow_adapter.mm ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ui/views/widget/native_widget_mac.h" 5 #import "ui/views/widget/native_widget_mac.h"
6 6
7 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 8
9 #import "base/mac/foundation_util.h" 9 #import "base/mac/foundation_util.h"
10 #import "base/mac/scoped_nsobject.h" 10 #import "base/mac/scoped_nsobject.h"
11 #import "base/mac/scoped_nsautorelease_pool.h"
11 #import "base/mac/scoped_objc_class_swizzler.h" 12 #import "base/mac/scoped_objc_class_swizzler.h"
12 #include "base/macros.h" 13 #include "base/macros.h"
13 #include "base/run_loop.h" 14 #include "base/run_loop.h"
14 #include "base/strings/sys_string_conversions.h" 15 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h" 16 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/test_timeouts.h" 17 #include "base/test/test_timeouts.h"
17 #import "testing/gtest_mac.h" 18 #import "testing/gtest_mac.h"
18 #include "third_party/skia/include/core/SkBitmap.h" 19 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "third_party/skia/include/core/SkCanvas.h" 20 #include "third_party/skia/include/core/SkCanvas.h"
20 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" 21 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
(...skipping 27 matching lines...) Expand all
48 @end 49 @end
49 50
50 @interface NSWindow (PrivateAPI) 51 @interface NSWindow (PrivateAPI)
51 - (BOOL)_isTitleHidden; 52 - (BOOL)_isTitleHidden;
52 @end 53 @end
53 54
54 // Test NSWindow that provides hooks via method overrides to verify behavior. 55 // Test NSWindow that provides hooks via method overrides to verify behavior.
55 @interface NativeWidgetMacTestWindow : NativeWidgetMacNSWindow { 56 @interface NativeWidgetMacTestWindow : NativeWidgetMacNSWindow {
56 @private 57 @private
57 int invalidateShadowCount_; 58 int invalidateShadowCount_;
59 bool* deallocFlag_;
58 } 60 }
59 @property(readonly, nonatomic) int invalidateShadowCount; 61 @property(readonly, nonatomic) int invalidateShadowCount;
62 @property(assign, nonatomic) bool* deallocFlag;
60 @end 63 @end
61 64
62 // Used to mock BridgedContentView so that calls to drawRect: can be 65 // Used to mock BridgedContentView so that calls to drawRect: can be
63 // intercepted. 66 // intercepted.
64 @interface MockBridgedView : NSView { 67 @interface MockBridgedView : NSView {
65 @private 68 @private
66 // Number of times -[NSView drawRect:] has been called. 69 // Number of times -[NSView drawRect:] has been called.
67 NSUInteger drawRectCount_; 70 NSUInteger drawRectCount_;
68 71
69 // The dirtyRect parameter passed to last invocation of drawRect:. 72 // The dirtyRect parameter passed to last invocation of drawRect:.
70 NSRect lastDirtyRect_; 73 NSRect lastDirtyRect_;
71 } 74 }
72 75
73 @property(assign, nonatomic) NSUInteger drawRectCount; 76 @property(assign, nonatomic) NSUInteger drawRectCount;
74 @property(assign, nonatomic) NSRect lastDirtyRect; 77 @property(assign, nonatomic) NSRect lastDirtyRect;
75 @end 78 @end
76 79
77 @interface FocusableTestNSView : NSView 80 @interface FocusableTestNSView : NSView
78 @end 81 @end
79 82
83 @interface TestNativeParentWindow : NSWindow
84 @property(assign, nonatomic) bool* deallocFlag;
85 @end
86
80 namespace views { 87 namespace views {
81 namespace test { 88 namespace test {
82 89
83 // BridgedNativeWidget friend to access private members. 90 // BridgedNativeWidget friend to access private members.
84 class BridgedNativeWidgetTestApi { 91 class BridgedNativeWidgetTestApi {
85 public: 92 public:
86 explicit BridgedNativeWidgetTestApi(NSWindow* window) { 93 explicit BridgedNativeWidgetTestApi(NSWindow* window) {
87 bridge_ = NativeWidgetMac::GetBridgeForNativeWindow(window); 94 bridge_ = NativeWidgetMac::GetBridgeForNativeWindow(window);
88 } 95 }
89 96
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 // Tests for parts of NativeWidgetMac not covered by BridgedNativeWidget, which 138 // Tests for parts of NativeWidgetMac not covered by BridgedNativeWidget, which
132 // need access to Cocoa APIs. 139 // need access to Cocoa APIs.
133 class NativeWidgetMacTest : public WidgetTest { 140 class NativeWidgetMacTest : public WidgetTest {
134 public: 141 public:
135 NativeWidgetMacTest() {} 142 NativeWidgetMacTest() {}
136 143
137 // The content size of NSWindows made by MakeNativeParent(). 144 // The content size of NSWindows made by MakeNativeParent().
138 NSRect ParentRect() const { return NSMakeRect(100, 100, 300, 200); } 145 NSRect ParentRect() const { return NSMakeRect(100, 100, 300, 200); }
139 146
140 // Make a native NSWindow with the given |style_mask| to use as a parent. 147 // Make a native NSWindow with the given |style_mask| to use as a parent.
141 NSWindow* MakeNativeParentWithStyle(int style_mask) { 148 TestNativeParentWindow* MakeNativeParentWithStyle(int style_mask) {
142 native_parent_.reset([[NSWindow alloc] 149 native_parent_.reset([[TestNativeParentWindow alloc]
143 initWithContentRect:ParentRect() 150 initWithContentRect:ParentRect()
144 styleMask:style_mask 151 styleMask:style_mask
145 backing:NSBackingStoreBuffered 152 backing:NSBackingStoreBuffered
146 defer:NO]); 153 defer:NO]);
147 [native_parent_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. 154 [native_parent_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject.
148 [native_parent_ makeKeyAndOrderFront:nil]; 155 [native_parent_ makeKeyAndOrderFront:nil];
149 return native_parent_; 156 return native_parent_;
150 } 157 }
151 158
152 // Make a borderless, native NSWindow to use as a parent. 159 // Make a borderless, native NSWindow to use as a parent.
153 NSWindow* MakeNativeParent() { 160 TestNativeParentWindow* MakeNativeParent() {
154 return MakeNativeParentWithStyle(NSBorderlessWindowMask); 161 return MakeNativeParentWithStyle(NSBorderlessWindowMask);
155 } 162 }
156 163
157 // Create a Widget backed by the NativeWidgetMacTestWindow NSWindow subclass. 164 // Create a Widget backed by the NativeWidgetMacTestWindow NSWindow subclass.
158 Widget* CreateWidgetWithTestWindow(Widget::InitParams params, 165 Widget* CreateWidgetWithTestWindow(Widget::InitParams params,
159 NativeWidgetMacTestWindow** window) { 166 NativeWidgetMacTestWindow** window) {
160 Widget* widget = new Widget; 167 Widget* widget = new Widget;
161 params.native_widget = new TestWindowNativeWidgetMac(widget); 168 params.native_widget = new TestWindowNativeWidgetMac(widget);
162 widget->Init(params); 169 widget->Init(params);
163 widget->Show(); 170 widget->Show();
164 *window = base::mac::ObjCCastStrict<NativeWidgetMacTestWindow>( 171 *window = base::mac::ObjCCastStrict<NativeWidgetMacTestWindow>(
165 widget->GetNativeWindow()); 172 widget->GetNativeWindow());
166 EXPECT_TRUE(*window); 173 EXPECT_TRUE(*window);
167 return widget; 174 return widget;
168 } 175 }
169 176
177 protected:
178 base::scoped_nsobject<TestNativeParentWindow> native_parent_;
179
170 private: 180 private:
171 base::scoped_nsobject<NSWindow> native_parent_;
172
173 DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacTest); 181 DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacTest);
174 }; 182 };
175 183
176 class WidgetChangeObserver : public TestWidgetObserver { 184 class WidgetChangeObserver : public TestWidgetObserver {
177 public: 185 public:
178 WidgetChangeObserver(Widget* widget) : TestWidgetObserver(widget) {} 186 WidgetChangeObserver(Widget* widget) : TestWidgetObserver(widget) {}
179 187
180 void WaitForVisibleCounts(int gained, int lost) { 188 void WaitForVisibleCounts(int gained, int lost) {
181 if (gained_visible_count_ >= gained && lost_visible_count_ >= lost) 189 if (gained_visible_count_ >= gained && lost_visible_count_ >= lost)
182 return; 190 return;
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after
639 EXPECT_EQ(native_parent, bridged_native_widget->parent()->GetNSWindow()); 647 EXPECT_EQ(native_parent, bridged_native_widget->parent()->GetNSWindow());
640 648
641 // Closing the parent should close and destroy the child. 649 // Closing the parent should close and destroy the child.
642 EXPECT_FALSE(child_observer.widget_closed()); 650 EXPECT_FALSE(child_observer.widget_closed());
643 [native_parent close]; 651 [native_parent close];
644 EXPECT_TRUE(child_observer.widget_closed()); 652 EXPECT_TRUE(child_observer.widget_closed());
645 653
646 EXPECT_EQ(0u, [[native_parent childWindows] count]); 654 EXPECT_EQ(0u, [[native_parent childWindows] count]);
647 } 655 }
648 656
657 // Tests closing the last remaining NSWindow reference via -windowWillClose:.
658 // This is a regression test for http://crbug.com/616701.
659 TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) {
660 bool child_dealloced = false;
661 bool native_parent_dealloced = false;
662 {
663 base::mac::ScopedNSAutoreleasePool pool;
664 TestNativeParentWindow* native_parent = MakeNativeParent();
665 [native_parent setDeallocFlag:&native_parent_dealloced];
666
667 NativeWidgetMacTestWindow* window;
668 Widget::InitParams init_params =
669 CreateParams(Widget::InitParams::TYPE_POPUP);
670 init_params.parent = [native_parent_ contentView];
671 init_params.bounds = gfx::Rect(0, 0, 100, 200);
672 CreateWidgetWithTestWindow(init_params, &window);
673 [window setDeallocFlag:&child_dealloced];
674 }
675 {
676 // On 10.11, closing a weak reference on the parent window works, but older
677 // versions of AppKit get upset if things are released inside -[NSWindow
678 // close]. This test tries to establish a situation where the last reference
679 // to the child window is released inside WidgetOwnerNSWindowAdapter::
680 // OnWindowWillClose().
681 base::mac::ScopedNSAutoreleasePool pool;
682 [native_parent_.autorelease() close];
683 EXPECT_TRUE(child_dealloced);
684 }
685 EXPECT_TRUE(native_parent_dealloced);
686 }
687
649 // Use Native APIs to query the tooltip text that would be shown once the 688 // Use Native APIs to query the tooltip text that would be shown once the
650 // tooltip delay had elapsed. 689 // tooltip delay had elapsed.
651 base::string16 TooltipTextForWidget(Widget* widget) { 690 base::string16 TooltipTextForWidget(Widget* widget) {
652 // For Mac, the actual location doesn't matter, since there is only one native 691 // For Mac, the actual location doesn't matter, since there is only one native
653 // view and it fills the window. This just assumes the window is at least big 692 // view and it fills the window. This just assumes the window is at least big
654 // big enough for a constant coordinate to be within it. 693 // big enough for a constant coordinate to be within it.
655 NSPoint point = NSMakePoint(30, 30); 694 NSPoint point = NSMakePoint(30, 30);
656 NSView* view = [widget->GetNativeView() hitTest:point]; 695 NSView* view = [widget->GetNativeView() hitTest:point];
657 NSString* text = 696 NSString* text =
658 [view view:view stringForToolTip:0 point:point userData:nullptr]; 697 [view view:view stringForToolTip:0 point:point userData:nullptr];
(...skipping 901 matching lines...) Expand 10 before | Expand all | Expand 10 after
1560 1599
1561 @implementation TestStopAnimationWaiter 1600 @implementation TestStopAnimationWaiter
1562 - (void)setWindowStateForEnd { 1601 - (void)setWindowStateForEnd {
1563 views::test::ScopedSwizzleWaiter::GetMethodAndMarkCalled()(self, _cmd); 1602 views::test::ScopedSwizzleWaiter::GetMethodAndMarkCalled()(self, _cmd);
1564 } 1603 }
1565 @end 1604 @end
1566 1605
1567 @implementation NativeWidgetMacTestWindow 1606 @implementation NativeWidgetMacTestWindow
1568 1607
1569 @synthesize invalidateShadowCount = invalidateShadowCount_; 1608 @synthesize invalidateShadowCount = invalidateShadowCount_;
1609 @synthesize deallocFlag = deallocFlag_;
1610
1611 - (void)dealloc {
1612 if (deallocFlag_) {
1613 DCHECK(!*deallocFlag_);
1614 *deallocFlag_ = true;
1615 }
1616 [super dealloc];
1617 }
1570 1618
1571 - (void)invalidateShadow { 1619 - (void)invalidateShadow {
1572 ++invalidateShadowCount_; 1620 ++invalidateShadowCount_;
1573 [super invalidateShadow]; 1621 [super invalidateShadow];
1574 } 1622 }
1575 1623
1576 @end 1624 @end
1577 1625
1578 @implementation MockBridgedView 1626 @implementation MockBridgedView
1579 1627
1580 @synthesize drawRectCount = drawRectCount_; 1628 @synthesize drawRectCount = drawRectCount_;
1581 @synthesize lastDirtyRect = lastDirtyRect_; 1629 @synthesize lastDirtyRect = lastDirtyRect_;
1582 1630
1583 - (void)drawRect:(NSRect)dirtyRect { 1631 - (void)drawRect:(NSRect)dirtyRect {
1584 ++drawRectCount_; 1632 ++drawRectCount_;
1585 lastDirtyRect_ = dirtyRect; 1633 lastDirtyRect_ = dirtyRect;
1586 } 1634 }
1587 1635
1588 @end 1636 @end
1589 1637
1590 @implementation FocusableTestNSView 1638 @implementation FocusableTestNSView
1591 - (BOOL)acceptsFirstResponder { 1639 - (BOOL)acceptsFirstResponder {
1592 return YES; 1640 return YES;
1593 } 1641 }
1594 @end 1642 @end
1643
1644 @implementation TestNativeParentWindow {
1645 bool* deallocFlag_;
1646 }
1647
1648 @synthesize deallocFlag = deallocFlag_;
1649
1650 - (void)dealloc {
1651 if (deallocFlag_) {
1652 DCHECK(!*deallocFlag_);
1653 *deallocFlag_ = true;
1654 }
1655 [super dealloc];
1656 }
1657
1658 @end
OLDNEW
« no previous file with comments | « ui/views/cocoa/widget_owner_nswindow_adapter.mm ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698