| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |