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 #if defined(OS_WIN) && !defined(USE_AURA)
11 #include "base/basictypes.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "ui/views/test/views_test_base.h"
14 #include "ui/views/widget/widget.h"
17 #include "ui/aura/window.h"
22 class NativeViewHostTest : public ViewsTestBase {
24 NativeViewHostTest() {
27 virtual void SetUp() OVERRIDE {
28 ViewsTestBase::SetUp();
30 // Create the top level widget.
31 toplevel_.reset(new Widget);
32 Widget::InitParams toplevel_params =
33 CreateParams(Widget::InitParams::TYPE_WINDOW);
34 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
35 toplevel_->Init(toplevel_params);
38 // Create a child widget whose native parent is |native_parent_view|, uses
39 // |contents_view|, and is attached to |host| which is added as a child to
41 Widget* CreateChildForHost(gfx::NativeView native_parent_view,
44 NativeViewHost* host) {
45 Widget* child = new Widget;
46 Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
47 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
48 child_params.parent = native_parent_view;
49 child->Init(child_params);
50 child->SetContentsView(contents_view);
52 // Owned by |parent_view|.
53 parent_view->AddChildView(host);
54 host->Attach(child->GetNativeView());
60 return toplevel_.get();
64 scoped_ptr<Widget> toplevel_;
66 DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest);
71 // View implementation used by NativeViewHierarchyChanged to count number of
72 // times NativeViewHierarchyChanged() is invoked.
73 class NativeViewHierarchyChangedTestView : public View {
75 NativeViewHierarchyChangedTestView() : notification_count_(0) {
79 notification_count_ = 0;
82 int notification_count() const { return notification_count_; }
84 // Overriden from View:
85 virtual void NativeViewHierarchyChanged() OVERRIDE {
86 ++notification_count_;
87 View::NativeViewHierarchyChanged();
91 int notification_count_;
93 DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView);
97 aura::Window* GetNativeParent(aura::Window* window) {
98 return window->parent();
100 #elif defined(OS_WIN)
101 HWND GetNativeParent(HWND window) {
102 return GetParent(window);
106 class ViewHierarchyChangedTestHost : public NativeViewHost {
108 ViewHierarchyChangedTestHost()
109 : num_parent_changes_(0) {
112 void ResetParentChanges() {
113 num_parent_changes_ = 0;
116 int num_parent_changes() const {
117 return num_parent_changes_;
120 // Overriden from NativeViewHost:
121 virtual void ViewHierarchyChanged(
122 const ViewHierarchyChangedDetails& details) OVERRIDE {
123 gfx::NativeView parent_before = native_view() ?
124 GetNativeParent(native_view()) : NULL;
125 NativeViewHost::ViewHierarchyChanged(details);
126 gfx::NativeView parent_after = native_view() ?
127 GetNativeParent(native_view()) : NULL;
128 if (parent_before != parent_after)
129 ++num_parent_changes_;
133 int num_parent_changes_;
135 DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost);
140 // Verifies NativeViewHierarchyChanged is sent.
141 TEST_F(NativeViewHostTest, NativeViewHierarchyChanged) {
142 // Create a child widget.
143 NativeViewHierarchyChangedTestView* test_view =
144 new NativeViewHierarchyChangedTestView;
145 NativeViewHost* host = new NativeViewHost;
146 scoped_ptr<Widget> child(CreateChildForHost(toplevel()->GetNativeView(),
147 toplevel()->GetRootView(),
151 EXPECT_EQ(0, test_view->notification_count());
152 test_view->ResetCount();
154 // Detaching should send a NativeViewHierarchyChanged() notification and
155 // change the parent.
157 EXPECT_EQ(1, test_view->notification_count());
158 EXPECT_NE(toplevel()->GetNativeView(),
159 GetNativeParent(child->GetNativeView()));
160 test_view->ResetCount();
162 // Attaching should send a NativeViewHierarchyChanged() notification and
164 host->Attach(child->GetNativeView());
165 EXPECT_EQ(1, test_view->notification_count());
166 EXPECT_EQ(toplevel()->GetNativeView(),
167 GetNativeParent(child->GetNativeView()));
170 // Verifies ViewHierarchyChanged handles NativeViewHost remove, add and move
171 // (reparent) operations with correct parent changes.
172 // This exercises the non-recursive code paths in
173 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
174 TEST_F(NativeViewHostTest, ViewHierarchyChangedForHost) {
177 // +-- host0 (NativeViewHost)
178 // +-- child0 (Widget, attached to host0)
179 // +-- test_host (ViewHierarchyChangedTestHost)
180 // +-- test_child (Widget, attached to test_host)
181 // +-- host1 (NativeViewHost)
182 // +-- child1 (Widget, attached to host1)
184 // Add two children widgets attached to a NativeViewHost, and a test
185 // grandchild as child widget of host0.
186 NativeViewHost* host0 = new NativeViewHost;
187 scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(),
188 toplevel()->GetRootView(),
191 NativeViewHost* host1 = new NativeViewHost;
192 scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
193 toplevel()->GetRootView(),
196 ViewHierarchyChangedTestHost* test_host = new ViewHierarchyChangedTestHost;
197 scoped_ptr<Widget> test_child(CreateChildForHost(host0->native_view(),
202 // Remove test_host from host0, expect 1 parent change.
203 test_host->ResetParentChanges();
204 EXPECT_EQ(0, test_host->num_parent_changes());
205 host0->RemoveChildView(test_host);
206 EXPECT_EQ(1, test_host->num_parent_changes());
208 // Add test_host back to host0, expect 1 parent change.
209 test_host->ResetParentChanges();
210 EXPECT_EQ(0, test_host->num_parent_changes());
211 host0->AddChildView(test_host);
212 EXPECT_EQ(1, test_host->num_parent_changes());
214 // Reparent test_host to host1, expect no parent change because the old and
215 // new parents, host0 and host1, belong to the same toplevel widget.
216 test_host->ResetParentChanges();
217 EXPECT_EQ(0, test_host->num_parent_changes());
218 host1->AddChildView(test_host);
219 EXPECT_EQ(0, test_host->num_parent_changes());
221 // Reparent test_host to contents view of child0, expect 2 parent changes
222 // because the old parent belongs to the toplevel widget whereas the new
223 // parent belongs to the child0.
224 test_host->ResetParentChanges();
225 EXPECT_EQ(0, test_host->num_parent_changes());
226 child0->GetContentsView()->AddChildView(test_host);
227 EXPECT_EQ(2, test_host->num_parent_changes());
230 // Verifies ViewHierarchyChanged handles NativeViewHost's parent remove, add and
231 // move (reparent) operations with correct parent changes.
232 // This exercises the recursive code paths in
233 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
234 TEST_F(NativeViewHostTest, ViewHierarchyChangedForHostParent) {
238 // +-- host0 (NativeViewHierarchyChangedTestHost)
239 // +-- child0 (Widget, attached to host0)
241 // +-- host1 (NativeViewHierarchyChangedTestHost)
242 // +-- child1 (Widget, attached to host1)
244 // Add two children views.
245 View* view0 = new View;
246 toplevel()->GetRootView()->AddChildView(view0);
247 View* view1 = new View;
248 toplevel()->GetRootView()->AddChildView(view1);
250 // To each child view, add a child widget.
251 ViewHierarchyChangedTestHost* host0 = new ViewHierarchyChangedTestHost;
252 scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(),
256 ViewHierarchyChangedTestHost* host1 = new ViewHierarchyChangedTestHost;
257 scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
262 // Remove view0 from top level, expect 1 parent change.
263 host0->ResetParentChanges();
264 EXPECT_EQ(0, host0->num_parent_changes());
265 toplevel()->GetRootView()->RemoveChildView(view0);
266 EXPECT_EQ(1, host0->num_parent_changes());
268 // Add view0 back to top level, expect 1 parent change.
269 host0->ResetParentChanges();
270 EXPECT_EQ(0, host0->num_parent_changes());
271 toplevel()->GetRootView()->AddChildView(view0);
272 EXPECT_EQ(1, host0->num_parent_changes());
274 // Reparent view0 to view1, expect no parent change because the old and new
275 // parents of both view0 and view1 belong to the same toplevel widget.
276 host0->ResetParentChanges();
277 host1->ResetParentChanges();
278 EXPECT_EQ(0, host0->num_parent_changes());
279 EXPECT_EQ(0, host1->num_parent_changes());
280 view1->AddChildView(view0);
281 EXPECT_EQ(0, host0->num_parent_changes());
282 EXPECT_EQ(0, host1->num_parent_changes());
284 // Restore original view hierarchy by adding back view0 to top level.
285 // Then, reparent view1 to contents view of child0.
286 // Expect 2 parent changes because the old parent belongs to the toplevel
287 // widget whereas the new parent belongs to the 1st child widget.
288 toplevel()->GetRootView()->AddChildView(view0);
289 host0->ResetParentChanges();
290 host1->ResetParentChanges();
291 EXPECT_EQ(0, host0->num_parent_changes());
292 EXPECT_EQ(0, host1->num_parent_changes());
293 child0->GetContentsView()->AddChildView(view1);
294 EXPECT_EQ(0, host0->num_parent_changes());
295 EXPECT_EQ(2, host1->num_parent_changes());