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
3 // found in the LICENSE file.
5 #include "ui/views/controls/native/native_view_host_mac.h"
7 #import <Cocoa/Cocoa.h>
9 #import "base/mac/scoped_nsautorelease_pool.h"
10 #import "base/mac/scoped_nsobject.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "ui/views/controls/native/native_view_host.h"
13 #include "ui/views/controls/native/native_view_host_test_base.h"
14 #include "ui/views/view.h"
15 #include "ui/views/widget/widget.h"
19 class NativeViewHostMacTest : public test::NativeViewHostTestBase {
21 NativeViewHostMacTest() {}
24 void TearDown() override {
25 // On Aura, the compositor is destroyed when the WindowTreeHost provided by
26 // AuraTestHelper is destroyed. On Mac, the Widget is the host, so it must
27 // be closed before the ContextFactory is torn down by ViewsTestBase.
29 NativeViewHostTestBase::TearDown();
32 NativeViewHostMac* native_host() {
33 return static_cast<NativeViewHostMac*>(GetNativeWrapper());
39 native_view_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
41 // Verify the expectation that the NativeViewHostWrapper is only created
42 // after the NativeViewHost is added to a widget.
43 EXPECT_FALSE(native_host());
44 toplevel()->GetRootView()->AddChildView(host());
45 EXPECT_TRUE(native_host());
47 host()->Attach(native_view_);
51 base::scoped_nsobject<NSView> native_view_;
54 DISALLOW_COPY_AND_ASSIGN(NativeViewHostMacTest);
57 // Test destroying the top level widget before destroying the NativeViewHost.
58 // On Mac, also ensure that the native view is removed from its superview when
59 // the Widget containing its host is destroyed.
60 TEST_F(NativeViewHostMacTest, DestroyWidget) {
61 ResetHostDestroyedCount();
64 EXPECT_EQ(0, host_destroyed_count());
65 EXPECT_TRUE([native_view_ superview]);
67 EXPECT_FALSE([native_view_ superview]);
68 EXPECT_EQ(1, host_destroyed_count());
71 // Ensure the native view receives the correct bounds when it is attached. On
72 // Mac, the bounds of the native view is relative to the NSWindow it is in, not
73 // the screen, and the coordinates have to be flipped.
74 TEST_F(NativeViewHostMacTest, Attach) {
76 EXPECT_TRUE([native_view_ superview]);
77 EXPECT_TRUE([native_view_ window]);
81 [native_view_ setFrame:NSZeroRect];
82 toplevel()->SetBounds(gfx::Rect(64, 48, 100, 200));
83 host()->SetBounds(10, 10, 80, 60);
85 EXPECT_FALSE([native_view_ superview]);
86 EXPECT_FALSE([native_view_ window]);
87 EXPECT_TRUE(NSEqualRects(NSZeroRect, [native_view_ frame]));
89 host()->Attach(native_view_);
90 EXPECT_TRUE([native_view_ superview]);
91 EXPECT_TRUE([native_view_ window]);
93 // Expect the top-left to be 10 pixels below the titlebar.
94 int bottom = toplevel()->GetClientAreaBoundsInScreen().height() - 10 - 60;
95 EXPECT_TRUE(NSEqualRects(NSMakeRect(10, bottom, 80, 60),
96 [native_view_ frame]));
101 // Ensure the native view is hidden along with its host, and when detaching, or
102 // when attaching to a host that is already hidden.
103 TEST_F(NativeViewHostMacTest, NativeViewHidden) {
105 toplevel()->SetBounds(gfx::Rect(0, 0, 100, 100));
106 host()->SetBounds(10, 10, 80, 60);
108 EXPECT_FALSE([native_view_ isHidden]);
109 host()->SetVisible(false);
110 EXPECT_TRUE([native_view_ isHidden]);
111 host()->SetVisible(true);
112 EXPECT_FALSE([native_view_ isHidden]);
115 EXPECT_TRUE([native_view_ isHidden]); // Hidden when detached.
116 [native_view_ setHidden:NO];
118 host()->SetVisible(false);
119 EXPECT_FALSE([native_view_ isHidden]); // Stays visible.
120 host()->Attach(native_view_);
121 EXPECT_TRUE([native_view_ isHidden]); // Hidden when attached.
124 [native_view_ setHidden:YES];
125 host()->SetVisible(true);
126 EXPECT_TRUE([native_view_ isHidden]); // Stays hidden.
127 host()->Attach(native_view_);
128 EXPECT_FALSE([native_view_ isHidden]); // Made visible when attached.
130 EXPECT_TRUE([native_view_ superview]);
131 toplevel()->GetRootView()->RemoveChildView(host());
132 EXPECT_TRUE([native_view_ isHidden]); // Hidden when removed from Widget.
133 EXPECT_FALSE([native_view_ superview]);
135 toplevel()->GetRootView()->AddChildView(host());
136 EXPECT_FALSE([native_view_ isHidden]); // And visible when added.
137 EXPECT_TRUE([native_view_ superview]);
142 // Check that we can destroy cleanly even if the native view has already been
144 TEST_F(NativeViewHostMacTest, NativeViewReleased) {
146 base::mac::ScopedNSAutoreleasePool pool;
148 // In practice the native view is a WebContentsViewCocoa which is retained
149 // by its superview (a TabContentsContainerView) and by WebContentsViewMac.
150 // It's possible for both of them to be destroyed without calling
151 // NativeHostView::Detach().
152 [native_view_ removeFromSuperview];
153 native_view_.reset();
156 // During teardown, NativeViewDetaching() is called in RemovedFromWidget().
157 // Just trigger it with Detach().