| OLD | NEW |
| (Empty) |
| 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 #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" | |
| 13 | |
| 14 namespace views { | |
| 15 | |
| 16 class NativeViewHostTest : public test::NativeViewHostTestBase { | |
| 17 public: | |
| 18 NativeViewHostTest() { | |
| 19 } | |
| 20 | |
| 21 virtual void SetUp() override { | |
| 22 ViewsTestBase::SetUp(); | |
| 23 CreateTopLevel(); | |
| 24 } | |
| 25 | |
| 26 private: | |
| 27 DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest); | |
| 28 }; | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 // View implementation used by NativeViewHierarchyChanged to count number of | |
| 33 // times NativeViewHierarchyChanged() is invoked. | |
| 34 class NativeViewHierarchyChangedTestView : public View { | |
| 35 public: | |
| 36 NativeViewHierarchyChangedTestView() : notification_count_(0) { | |
| 37 } | |
| 38 | |
| 39 void ResetCount() { | |
| 40 notification_count_ = 0; | |
| 41 } | |
| 42 | |
| 43 int notification_count() const { return notification_count_; } | |
| 44 | |
| 45 // Overriden from View: | |
| 46 virtual void NativeViewHierarchyChanged() override { | |
| 47 ++notification_count_; | |
| 48 View::NativeViewHierarchyChanged(); | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 int notification_count_; | |
| 53 | |
| 54 DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView); | |
| 55 }; | |
| 56 | |
| 57 aura::Window* GetNativeParent(aura::Window* window) { | |
| 58 return window->parent(); | |
| 59 } | |
| 60 | |
| 61 class ViewHierarchyChangedTestHost : public NativeViewHost { | |
| 62 public: | |
| 63 ViewHierarchyChangedTestHost() | |
| 64 : num_parent_changes_(0) { | |
| 65 } | |
| 66 | |
| 67 void ResetParentChanges() { | |
| 68 num_parent_changes_ = 0; | |
| 69 } | |
| 70 | |
| 71 int num_parent_changes() const { | |
| 72 return num_parent_changes_; | |
| 73 } | |
| 74 | |
| 75 // Overriden from NativeViewHost: | |
| 76 virtual 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_; | |
| 85 } | |
| 86 | |
| 87 private: | |
| 88 int num_parent_changes_; | |
| 89 | |
| 90 DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost); | |
| 91 }; | |
| 92 | |
| 93 } // namespace | |
| 94 | |
| 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(), | |
| 103 test_view, | |
| 104 host)); | |
| 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 | |
| 108 // window. | |
| 109 EXPECT_EQ(2, test_view->notification_count()); | |
| 110 #else | |
| 111 EXPECT_EQ(0, test_view->notification_count()); | |
| 112 #endif | |
| 113 test_view->ResetCount(); | |
| 114 | |
| 115 // Detaching should send a NativeViewHierarchyChanged() notification and | |
| 116 // change the parent. | |
| 117 host->Detach(); | |
| 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()); | |
| 122 #else | |
| 123 EXPECT_EQ(1, test_view->notification_count()); | |
| 124 #endif | |
| 125 EXPECT_NE(toplevel()->GetNativeView(), | |
| 126 GetNativeParent(child->GetNativeView())); | |
| 127 test_view->ResetCount(); | |
| 128 | |
| 129 // Attaching should send a NativeViewHierarchyChanged() notification and | |
| 130 // reset the parent. | |
| 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()))); | |
| 138 #else | |
| 139 EXPECT_EQ(1, test_view->notification_count()); | |
| 140 EXPECT_EQ(toplevel()->GetNativeView(), | |
| 141 GetNativeParent(child->GetNativeView())); | |
| 142 #endif | |
| 143 } | |
| 144 | |
| 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) { | |
| 150 // Original tree: | |
| 151 // toplevel | |
| 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) | |
| 158 | |
| 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(), | |
| 164 new View, | |
| 165 host0)); | |
| 166 NativeViewHost* host1 = new NativeViewHost; | |
| 167 scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(), | |
| 168 toplevel()->GetRootView(), | |
| 169 new View, | |
| 170 host1)); | |
| 171 ViewHierarchyChangedTestHost* test_host = new ViewHierarchyChangedTestHost; | |
| 172 scoped_ptr<Widget> test_child(CreateChildForHost(host0->native_view(), | |
| 173 host0, | |
| 174 new View, | |
| 175 test_host)); | |
| 176 | |
| 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()); | |
| 182 | |
| 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()); | |
| 188 | |
| 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()); | |
| 195 | |
| 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()); | |
| 203 } | |
| 204 | |
| 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) { | |
| 210 // Original tree: | |
| 211 // toplevel | |
| 212 // +-- view0 (View) | |
| 213 // +-- host0 (NativeViewHierarchyChangedTestHost) | |
| 214 // +-- child0 (Widget, attached to host0) | |
| 215 // +-- view1 (View) | |
| 216 // +-- host1 (NativeViewHierarchyChangedTestHost) | |
| 217 // +-- child1 (Widget, attached to host1) | |
| 218 | |
| 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); | |
| 224 | |
| 225 // To each child view, add a child widget. | |
| 226 ViewHierarchyChangedTestHost* host0 = new ViewHierarchyChangedTestHost; | |
| 227 scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(), | |
| 228 view0, | |
| 229 new View, | |
| 230 host0)); | |
| 231 ViewHierarchyChangedTestHost* host1 = new ViewHierarchyChangedTestHost; | |
| 232 scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(), | |
| 233 view1, | |
| 234 new View, | |
| 235 host1)); | |
| 236 | |
| 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()); | |
| 242 | |
| 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()); | |
| 248 | |
| 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()); | |
| 258 | |
| 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()); | |
| 271 } | |
| 272 | |
| 273 } // namespace views | |
| OLD | NEW |