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/test/views_test_base.h"
11 #include "ui/views/widget/widget.h"
15 class NativeViewHostTest
: public ViewsTestBase
{
17 NativeViewHostTest() {
20 virtual void SetUp() OVERRIDE
{
21 ViewsTestBase::SetUp();
23 // Create the top level widget.
24 toplevel_
.reset(new Widget
);
25 Widget::InitParams toplevel_params
=
26 CreateParams(Widget::InitParams::TYPE_WINDOW
);
27 toplevel_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
28 toplevel_
->Init(toplevel_params
);
31 // Create a child widget whose native parent is |native_parent_view|, uses
32 // |contents_view|, and is attached to |host| which is added as a child to
34 Widget
* CreateChildForHost(gfx::NativeView native_parent_view
,
37 NativeViewHost
* host
) {
38 Widget
* child
= new Widget
;
39 Widget::InitParams
child_params(Widget::InitParams::TYPE_CONTROL
);
40 child_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
41 child_params
.parent
= native_parent_view
;
42 child
->Init(child_params
);
43 child
->SetContentsView(contents_view
);
45 // Owned by |parent_view|.
46 parent_view
->AddChildView(host
);
47 host
->Attach(child
->GetNativeView());
53 return toplevel_
.get();
57 scoped_ptr
<Widget
> toplevel_
;
59 DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest
);
64 // View implementation used by NativeViewHierarchyChanged to count number of
65 // times NativeViewHierarchyChanged() is invoked.
66 class NativeViewHierarchyChangedTestView
: public View
{
68 NativeViewHierarchyChangedTestView() : notification_count_(0) {
72 notification_count_
= 0;
75 int notification_count() const { return notification_count_
; }
77 // Overriden from View:
78 virtual void NativeViewHierarchyChanged() OVERRIDE
{
79 ++notification_count_
;
80 View::NativeViewHierarchyChanged();
84 int notification_count_
;
86 DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView
);
89 aura::Window
* GetNativeParent(aura::Window
* window
) {
90 return window
->parent();
93 class ViewHierarchyChangedTestHost
: public NativeViewHost
{
95 ViewHierarchyChangedTestHost()
96 : num_parent_changes_(0) {
99 void ResetParentChanges() {
100 num_parent_changes_
= 0;
103 int num_parent_changes() const {
104 return num_parent_changes_
;
107 // Overriden from NativeViewHost:
108 virtual void ViewHierarchyChanged(
109 const ViewHierarchyChangedDetails
& details
) OVERRIDE
{
110 gfx::NativeView parent_before
= native_view() ?
111 GetNativeParent(native_view()) : NULL
;
112 NativeViewHost::ViewHierarchyChanged(details
);
113 gfx::NativeView parent_after
= native_view() ?
114 GetNativeParent(native_view()) : NULL
;
115 if (parent_before
!= parent_after
)
116 ++num_parent_changes_
;
120 int num_parent_changes_
;
122 DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost
);
127 // Verifies NativeViewHierarchyChanged is sent.
128 TEST_F(NativeViewHostTest
, NativeViewHierarchyChanged
) {
129 // Create a child widget.
130 NativeViewHierarchyChangedTestView
* test_view
=
131 new NativeViewHierarchyChangedTestView
;
132 NativeViewHost
* host
= new NativeViewHost
;
133 scoped_ptr
<Widget
> child(CreateChildForHost(toplevel()->GetNativeView(),
134 toplevel()->GetRootView(),
137 #if defined(USE_AURA)
138 // One notification is generated from inserting the native view into the
140 EXPECT_EQ(1, test_view
->notification_count());
142 EXPECT_EQ(0, test_view
->notification_count());
144 test_view
->ResetCount();
146 // Detaching should send a NativeViewHierarchyChanged() notification and
147 // change the parent.
149 EXPECT_EQ(1, test_view
->notification_count());
150 EXPECT_NE(toplevel()->GetNativeView(),
151 GetNativeParent(child
->GetNativeView()));
152 test_view
->ResetCount();
154 // Attaching should send a NativeViewHierarchyChanged() notification and
156 host
->Attach(child
->GetNativeView());
157 EXPECT_EQ(1, test_view
->notification_count());
158 #if defined(USE_AURA)
159 // There is a clipping window inserted above the native view that needs to be
160 // accounted for when looking at the relationship between the native views.
161 EXPECT_EQ(toplevel()->GetNativeView(),
162 GetNativeParent(GetNativeParent(child
->GetNativeView())));
164 EXPECT_EQ(toplevel()->GetNativeView(),
165 GetNativeParent(child
->GetNativeView()));
169 // Verifies ViewHierarchyChanged handles NativeViewHost remove, add and move
170 // (reparent) operations with correct parent changes.
171 // This exercises the non-recursive code paths in
172 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
173 TEST_F(NativeViewHostTest
, ViewHierarchyChangedForHost
) {
176 // +-- host0 (NativeViewHost)
177 // +-- child0 (Widget, attached to host0)
178 // +-- test_host (ViewHierarchyChangedTestHost)
179 // +-- test_child (Widget, attached to test_host)
180 // +-- host1 (NativeViewHost)
181 // +-- child1 (Widget, attached to host1)
183 // Add two children widgets attached to a NativeViewHost, and a test
184 // grandchild as child widget of host0.
185 NativeViewHost
* host0
= new NativeViewHost
;
186 scoped_ptr
<Widget
> child0(CreateChildForHost(toplevel()->GetNativeView(),
187 toplevel()->GetRootView(),
190 NativeViewHost
* host1
= new NativeViewHost
;
191 scoped_ptr
<Widget
> child1(CreateChildForHost(toplevel()->GetNativeView(),
192 toplevel()->GetRootView(),
195 ViewHierarchyChangedTestHost
* test_host
= new ViewHierarchyChangedTestHost
;
196 scoped_ptr
<Widget
> test_child(CreateChildForHost(host0
->native_view(),
201 // Remove test_host from host0, expect 1 parent change.
202 test_host
->ResetParentChanges();
203 EXPECT_EQ(0, test_host
->num_parent_changes());
204 host0
->RemoveChildView(test_host
);
205 EXPECT_EQ(1, test_host
->num_parent_changes());
207 // Add test_host back to host0, expect 1 parent change.
208 test_host
->ResetParentChanges();
209 EXPECT_EQ(0, test_host
->num_parent_changes());
210 host0
->AddChildView(test_host
);
211 EXPECT_EQ(1, test_host
->num_parent_changes());
213 // Reparent test_host to host1, expect no parent change because the old and
214 // new parents, host0 and host1, belong to the same toplevel widget.
215 test_host
->ResetParentChanges();
216 EXPECT_EQ(0, test_host
->num_parent_changes());
217 host1
->AddChildView(test_host
);
218 EXPECT_EQ(0, test_host
->num_parent_changes());
220 // Reparent test_host to contents view of child0, expect 2 parent changes
221 // because the old parent belongs to the toplevel widget whereas the new
222 // parent belongs to the child0.
223 test_host
->ResetParentChanges();
224 EXPECT_EQ(0, test_host
->num_parent_changes());
225 child0
->GetContentsView()->AddChildView(test_host
);
226 EXPECT_EQ(2, test_host
->num_parent_changes());
229 // Verifies ViewHierarchyChanged handles NativeViewHost's parent remove, add and
230 // move (reparent) operations with correct parent changes.
231 // This exercises the recursive code paths in
232 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
233 TEST_F(NativeViewHostTest
, ViewHierarchyChangedForHostParent
) {
237 // +-- host0 (NativeViewHierarchyChangedTestHost)
238 // +-- child0 (Widget, attached to host0)
240 // +-- host1 (NativeViewHierarchyChangedTestHost)
241 // +-- child1 (Widget, attached to host1)
243 // Add two children views.
244 View
* view0
= new View
;
245 toplevel()->GetRootView()->AddChildView(view0
);
246 View
* view1
= new View
;
247 toplevel()->GetRootView()->AddChildView(view1
);
249 // To each child view, add a child widget.
250 ViewHierarchyChangedTestHost
* host0
= new ViewHierarchyChangedTestHost
;
251 scoped_ptr
<Widget
> child0(CreateChildForHost(toplevel()->GetNativeView(),
255 ViewHierarchyChangedTestHost
* host1
= new ViewHierarchyChangedTestHost
;
256 scoped_ptr
<Widget
> child1(CreateChildForHost(toplevel()->GetNativeView(),
261 // Remove view0 from top level, expect 1 parent change.
262 host0
->ResetParentChanges();
263 EXPECT_EQ(0, host0
->num_parent_changes());
264 toplevel()->GetRootView()->RemoveChildView(view0
);
265 EXPECT_EQ(1, host0
->num_parent_changes());
267 // Add view0 back to top level, expect 1 parent change.
268 host0
->ResetParentChanges();
269 EXPECT_EQ(0, host0
->num_parent_changes());
270 toplevel()->GetRootView()->AddChildView(view0
);
271 EXPECT_EQ(1, host0
->num_parent_changes());
273 // Reparent view0 to view1, expect no parent change because the old and new
274 // parents of both view0 and view1 belong to the same toplevel widget.
275 host0
->ResetParentChanges();
276 host1
->ResetParentChanges();
277 EXPECT_EQ(0, host0
->num_parent_changes());
278 EXPECT_EQ(0, host1
->num_parent_changes());
279 view1
->AddChildView(view0
);
280 EXPECT_EQ(0, host0
->num_parent_changes());
281 EXPECT_EQ(0, host1
->num_parent_changes());
283 // Restore original view hierarchy by adding back view0 to top level.
284 // Then, reparent view1 to contents view of child0.
285 // Expect 2 parent changes because the old parent belongs to the toplevel
286 // widget whereas the new parent belongs to the 1st child widget.
287 toplevel()->GetRootView()->AddChildView(view0
);
288 host0
->ResetParentChanges();
289 host1
->ResetParentChanges();
290 EXPECT_EQ(0, host0
->num_parent_changes());
291 EXPECT_EQ(0, host1
->num_parent_changes());
292 child0
->GetContentsView()->AddChildView(view1
);
293 EXPECT_EQ(0, host0
->num_parent_changes());
294 EXPECT_EQ(2, host1
->num_parent_changes());