1 // Copyright (c) 2012 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.h"
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "ui/aura/window.h"
10 #include "ui/views/controls/native/native_view_host_test_base.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/widget.h"
16 class NativeViewHostTest
: public test::NativeViewHostTestBase
{
18 NativeViewHostTest() {
21 void SetUp() override
{
22 ViewsTestBase::SetUp();
27 DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest
);
32 // View implementation used by NativeViewHierarchyChanged to count number of
33 // times NativeViewHierarchyChanged() is invoked.
34 class NativeViewHierarchyChangedTestView
: public View
{
36 NativeViewHierarchyChangedTestView() : notification_count_(0) {
40 notification_count_
= 0;
43 int notification_count() const { return notification_count_
; }
45 // Overriden from View:
46 void NativeViewHierarchyChanged() override
{
47 ++notification_count_
;
48 View::NativeViewHierarchyChanged();
52 int notification_count_
;
54 DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView
);
57 aura::Window
* GetNativeParent(aura::Window
* window
) {
58 return window
->parent();
61 class ViewHierarchyChangedTestHost
: public NativeViewHost
{
63 ViewHierarchyChangedTestHost()
64 : num_parent_changes_(0) {
67 void ResetParentChanges() {
68 num_parent_changes_
= 0;
71 int num_parent_changes() const {
72 return num_parent_changes_
;
75 // Overriden from NativeViewHost:
76 void ViewHierarchyChanged(
77 const ViewHierarchyChangedDetails
& details
) override
{
78 gfx::NativeView parent_before
= native_view() ?
79 GetNativeParent(native_view()) : NULL
;
80 NativeViewHost::ViewHierarchyChanged(details
);
81 gfx::NativeView parent_after
= native_view() ?
82 GetNativeParent(native_view()) : NULL
;
83 if (parent_before
!= parent_after
)
84 ++num_parent_changes_
;
88 int num_parent_changes_
;
90 DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost
);
95 // Verifies NativeViewHierarchyChanged is sent.
96 TEST_F(NativeViewHostTest
, NativeViewHierarchyChanged
) {
97 // Create a child widget.
98 NativeViewHierarchyChangedTestView
* test_view
=
99 new NativeViewHierarchyChangedTestView
;
100 NativeViewHost
* host
= new NativeViewHost
;
101 scoped_ptr
<Widget
> child(CreateChildForHost(toplevel()->GetNativeView(),
102 toplevel()->GetRootView(),
105 #if defined(USE_AURA)
106 // Two notifications are generated from inserting the native view into the
107 // clipping window and then inserting the clipping window into the root
109 EXPECT_EQ(2, test_view
->notification_count());
111 EXPECT_EQ(0, test_view
->notification_count());
113 test_view
->ResetCount();
115 // Detaching should send a NativeViewHierarchyChanged() notification and
116 // change the parent.
118 #if defined(USE_AURA)
119 // Two notifications are generated from removing the native view from the
120 // clipping window and then reparenting it to the root window.
121 EXPECT_EQ(2, test_view
->notification_count());
123 EXPECT_EQ(1, test_view
->notification_count());
125 EXPECT_NE(toplevel()->GetNativeView(),
126 GetNativeParent(child
->GetNativeView()));
127 test_view
->ResetCount();
129 // Attaching should send a NativeViewHierarchyChanged() notification and
131 host
->Attach(child
->GetNativeView());
132 #if defined(USE_AURA)
133 // There is a clipping window inserted above the native view that needs to be
134 // accounted for when looking at the relationship between the native views.
135 EXPECT_EQ(2, test_view
->notification_count());
136 EXPECT_EQ(toplevel()->GetNativeView(),
137 GetNativeParent(GetNativeParent(child
->GetNativeView())));
139 EXPECT_EQ(1, test_view
->notification_count());
140 EXPECT_EQ(toplevel()->GetNativeView(),
141 GetNativeParent(child
->GetNativeView()));
145 // Verifies ViewHierarchyChanged handles NativeViewHost remove, add and move
146 // (reparent) operations with correct parent changes.
147 // This exercises the non-recursive code paths in
148 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
149 TEST_F(NativeViewHostTest
, ViewHierarchyChangedForHost
) {
152 // +-- host0 (NativeViewHost)
153 // +-- child0 (Widget, attached to host0)
154 // +-- test_host (ViewHierarchyChangedTestHost)
155 // +-- test_child (Widget, attached to test_host)
156 // +-- host1 (NativeViewHost)
157 // +-- child1 (Widget, attached to host1)
159 // Add two children widgets attached to a NativeViewHost, and a test
160 // grandchild as child widget of host0.
161 NativeViewHost
* host0
= new NativeViewHost
;
162 scoped_ptr
<Widget
> child0(CreateChildForHost(toplevel()->GetNativeView(),
163 toplevel()->GetRootView(),
166 NativeViewHost
* host1
= new NativeViewHost
;
167 scoped_ptr
<Widget
> child1(CreateChildForHost(toplevel()->GetNativeView(),
168 toplevel()->GetRootView(),
171 ViewHierarchyChangedTestHost
* test_host
= new ViewHierarchyChangedTestHost
;
172 scoped_ptr
<Widget
> test_child(CreateChildForHost(host0
->native_view(),
177 // Remove test_host from host0, expect 1 parent change.
178 test_host
->ResetParentChanges();
179 EXPECT_EQ(0, test_host
->num_parent_changes());
180 host0
->RemoveChildView(test_host
);
181 EXPECT_EQ(1, test_host
->num_parent_changes());
183 // Add test_host back to host0, expect 1 parent change.
184 test_host
->ResetParentChanges();
185 EXPECT_EQ(0, test_host
->num_parent_changes());
186 host0
->AddChildView(test_host
);
187 EXPECT_EQ(1, test_host
->num_parent_changes());
189 // Reparent test_host to host1, expect no parent change because the old and
190 // new parents, host0 and host1, belong to the same toplevel widget.
191 test_host
->ResetParentChanges();
192 EXPECT_EQ(0, test_host
->num_parent_changes());
193 host1
->AddChildView(test_host
);
194 EXPECT_EQ(0, test_host
->num_parent_changes());
196 // Reparent test_host to contents view of child0, expect 2 parent changes
197 // because the old parent belongs to the toplevel widget whereas the new
198 // parent belongs to the child0.
199 test_host
->ResetParentChanges();
200 EXPECT_EQ(0, test_host
->num_parent_changes());
201 child0
->GetContentsView()->AddChildView(test_host
);
202 EXPECT_EQ(2, test_host
->num_parent_changes());
205 // Verifies ViewHierarchyChanged handles NativeViewHost's parent remove, add and
206 // move (reparent) operations with correct parent changes.
207 // This exercises the recursive code paths in
208 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
209 TEST_F(NativeViewHostTest
, ViewHierarchyChangedForHostParent
) {
213 // +-- host0 (NativeViewHierarchyChangedTestHost)
214 // +-- child0 (Widget, attached to host0)
216 // +-- host1 (NativeViewHierarchyChangedTestHost)
217 // +-- child1 (Widget, attached to host1)
219 // Add two children views.
220 View
* view0
= new View
;
221 toplevel()->GetRootView()->AddChildView(view0
);
222 View
* view1
= new View
;
223 toplevel()->GetRootView()->AddChildView(view1
);
225 // To each child view, add a child widget.
226 ViewHierarchyChangedTestHost
* host0
= new ViewHierarchyChangedTestHost
;
227 scoped_ptr
<Widget
> child0(CreateChildForHost(toplevel()->GetNativeView(),
231 ViewHierarchyChangedTestHost
* host1
= new ViewHierarchyChangedTestHost
;
232 scoped_ptr
<Widget
> child1(CreateChildForHost(toplevel()->GetNativeView(),
237 // Remove view0 from top level, expect 1 parent change.
238 host0
->ResetParentChanges();
239 EXPECT_EQ(0, host0
->num_parent_changes());
240 toplevel()->GetRootView()->RemoveChildView(view0
);
241 EXPECT_EQ(1, host0
->num_parent_changes());
243 // Add view0 back to top level, expect 1 parent change.
244 host0
->ResetParentChanges();
245 EXPECT_EQ(0, host0
->num_parent_changes());
246 toplevel()->GetRootView()->AddChildView(view0
);
247 EXPECT_EQ(1, host0
->num_parent_changes());
249 // Reparent view0 to view1, expect no parent change because the old and new
250 // parents of both view0 and view1 belong to the same toplevel widget.
251 host0
->ResetParentChanges();
252 host1
->ResetParentChanges();
253 EXPECT_EQ(0, host0
->num_parent_changes());
254 EXPECT_EQ(0, host1
->num_parent_changes());
255 view1
->AddChildView(view0
);
256 EXPECT_EQ(0, host0
->num_parent_changes());
257 EXPECT_EQ(0, host1
->num_parent_changes());
259 // Restore original view hierarchy by adding back view0 to top level.
260 // Then, reparent view1 to contents view of child0.
261 // Expect 2 parent changes because the old parent belongs to the toplevel
262 // widget whereas the new parent belongs to the 1st child widget.
263 toplevel()->GetRootView()->AddChildView(view0
);
264 host0
->ResetParentChanges();
265 host1
->ResetParentChanges();
266 EXPECT_EQ(0, host0
->num_parent_changes());
267 EXPECT_EQ(0, host1
->num_parent_changes());
268 child0
->GetContentsView()->AddChildView(view1
);
269 EXPECT_EQ(0, host0
->num_parent_changes());
270 EXPECT_EQ(2, host1
->num_parent_changes());