- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / native / native_view_host_unittest.cc
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.
4
5 #include "ui/views/controls/native/native_view_host.h"
6
7 #if defined(OS_WIN) && !defined(USE_AURA)
8 #include <windows.h>
9 #endif
10
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"
15
16 #if defined(USE_AURA)
17 #include "ui/aura/window.h"
18 #endif
19
20 namespace views {
21
22 class NativeViewHostTest : public ViewsTestBase {
23  public:
24   NativeViewHostTest() {
25   }
26
27   virtual void SetUp() OVERRIDE {
28     ViewsTestBase::SetUp();
29
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);
36   }
37
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
40   // |parent_view|.
41   Widget* CreateChildForHost(gfx::NativeView native_parent_view,
42                              View* parent_view,
43                              View* contents_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);
51
52     // Owned by |parent_view|.
53     parent_view->AddChildView(host);
54     host->Attach(child->GetNativeView());
55
56     return child;
57   }
58
59   Widget* toplevel() {
60     return toplevel_.get();
61   }
62
63  private:
64   scoped_ptr<Widget> toplevel_;
65
66   DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest);
67 };
68
69 namespace {
70
71 // View implementation used by NativeViewHierarchyChanged to count number of
72 // times NativeViewHierarchyChanged() is invoked.
73 class NativeViewHierarchyChangedTestView : public View {
74  public:
75   NativeViewHierarchyChangedTestView() : notification_count_(0) {
76   }
77
78   void ResetCount() {
79     notification_count_ = 0;
80   }
81
82   int notification_count() const { return notification_count_; }
83
84   // Overriden from View:
85   virtual void NativeViewHierarchyChanged() OVERRIDE {
86     ++notification_count_;
87     View::NativeViewHierarchyChanged();
88   }
89
90  private:
91   int notification_count_;
92
93   DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView);
94 };
95
96 #if defined(USE_AURA)
97 aura::Window* GetNativeParent(aura::Window* window) {
98   return window->parent();
99 }
100 #elif defined(OS_WIN)
101 HWND GetNativeParent(HWND window) {
102   return GetParent(window);
103 }
104 #endif
105
106 class ViewHierarchyChangedTestHost : public NativeViewHost {
107  public:
108   ViewHierarchyChangedTestHost()
109       : num_parent_changes_(0) {
110   }
111
112   void ResetParentChanges() {
113     num_parent_changes_ = 0;
114   }
115
116   int num_parent_changes() const {
117     return num_parent_changes_;
118   }
119
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_;
130   }
131
132  private:
133   int num_parent_changes_;
134
135   DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost);
136 };
137
138 }  // namespace
139
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(),
148                                               test_view,
149                                               host));
150
151   EXPECT_EQ(0, test_view->notification_count());
152   test_view->ResetCount();
153
154   // Detaching should send a NativeViewHierarchyChanged() notification and
155   // change the parent.
156   host->Detach();
157   EXPECT_EQ(1, test_view->notification_count());
158   EXPECT_NE(toplevel()->GetNativeView(),
159             GetNativeParent(child->GetNativeView()));
160   test_view->ResetCount();
161
162   // Attaching should send a NativeViewHierarchyChanged() notification and
163   // reset the parent.
164   host->Attach(child->GetNativeView());
165   EXPECT_EQ(1, test_view->notification_count());
166   EXPECT_EQ(toplevel()->GetNativeView(),
167             GetNativeParent(child->GetNativeView()));
168 }
169
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) {
175   // Original tree:
176   // toplevel
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)
183
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(),
189                                                new View,
190                                                host0));
191   NativeViewHost* host1 = new NativeViewHost;
192   scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
193                                                toplevel()->GetRootView(),
194                                                new View,
195                                                host1));
196   ViewHierarchyChangedTestHost* test_host = new ViewHierarchyChangedTestHost;
197   scoped_ptr<Widget> test_child(CreateChildForHost(host0->native_view(),
198                                                    host0,
199                                                    new View,
200                                                    test_host));
201
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());
207
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());
213
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());
220
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());
228 }
229
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) {
235   // Original tree:
236   // toplevel
237   // +-- view0 (View)
238   //     +-- host0 (NativeViewHierarchyChangedTestHost)
239   //         +-- child0 (Widget, attached to host0)
240   // +-- view1 (View)
241   //     +-- host1 (NativeViewHierarchyChangedTestHost)
242   //         +-- child1 (Widget, attached to host1)
243
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);
249
250   // To each child view, add a child widget.
251   ViewHierarchyChangedTestHost* host0 = new ViewHierarchyChangedTestHost;
252   scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(),
253                                                view0,
254                                                new View,
255                                                host0));
256   ViewHierarchyChangedTestHost* host1 = new ViewHierarchyChangedTestHost;
257   scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
258                                                view1,
259                                                new View,
260                                                host1));
261
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());
267
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());
273
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());
283
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());
296 }
297
298 }  // namespace views