| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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_aura.h" | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "base/memory/scoped_ptr.h" | |
| 9 #include "ui/aura/client/aura_constants.h" | |
| 10 #include "ui/aura/window.h" | |
| 11 #include "ui/base/cursor/cursor.h" | |
| 12 #include "ui/views/controls/native/native_view_host.h" | |
| 13 #include "ui/views/controls/native/native_view_host_test_base.h" | |
| 14 #include "ui/views/view.h" | |
| 15 #include "ui/views/view_constants_aura.h" | |
| 16 #include "ui/views/widget/widget.h" | |
| 17 | |
| 18 namespace views { | |
| 19 | |
| 20 // Observer watching for window visibility and bounds change events. This is | |
| 21 // used to verify that the child and clipping window operations are done in the | |
| 22 // right order. | |
| 23 class NativeViewHostWindowObserver : public aura::WindowObserver { | |
| 24 public: | |
| 25 enum EventType { | |
| 26 EVENT_NONE, | |
| 27 EVENT_SHOWN, | |
| 28 EVENT_HIDDEN, | |
| 29 EVENT_BOUNDS_CHANGED, | |
| 30 }; | |
| 31 | |
| 32 struct EventDetails { | |
| 33 EventType type; | |
| 34 aura::Window* window; | |
| 35 gfx::Rect bounds; | |
| 36 bool operator!=(const EventDetails& rhs) { | |
| 37 return type != rhs.type || window != rhs.window || bounds != rhs.bounds; | |
| 38 } | |
| 39 }; | |
| 40 | |
| 41 NativeViewHostWindowObserver() {} | |
| 42 virtual ~NativeViewHostWindowObserver() {} | |
| 43 | |
| 44 const std::vector<EventDetails>& events() const { return events_; } | |
| 45 | |
| 46 // aura::WindowObserver overrides | |
| 47 virtual void OnWindowVisibilityChanged(aura::Window* window, | |
| 48 bool visible) override { | |
| 49 EventDetails event; | |
| 50 event.type = visible ? EVENT_SHOWN : EVENT_HIDDEN; | |
| 51 event.window = window; | |
| 52 event.bounds = window->GetBoundsInRootWindow(); | |
| 53 | |
| 54 // Dedupe events as a single Hide() call can result in several | |
| 55 // notifications. | |
| 56 if (events_.size() == 0u || events_.back() != event) | |
| 57 events_.push_back(event); | |
| 58 } | |
| 59 | |
| 60 virtual void OnWindowBoundsChanged(aura::Window* window, | |
| 61 const gfx::Rect& old_bounds, | |
| 62 const gfx::Rect& new_bounds) override { | |
| 63 EventDetails event; | |
| 64 event.type = EVENT_BOUNDS_CHANGED; | |
| 65 event.window = window; | |
| 66 event.bounds = window->GetBoundsInRootWindow(); | |
| 67 events_.push_back(event); | |
| 68 } | |
| 69 | |
| 70 private: | |
| 71 std::vector<EventDetails> events_; | |
| 72 gfx::Rect bounds_at_visibility_changed_; | |
| 73 | |
| 74 DISALLOW_COPY_AND_ASSIGN(NativeViewHostWindowObserver); | |
| 75 }; | |
| 76 | |
| 77 class NativeViewHostAuraTest : public test::NativeViewHostTestBase { | |
| 78 public: | |
| 79 NativeViewHostAuraTest() { | |
| 80 } | |
| 81 | |
| 82 NativeViewHostAura* native_host() { | |
| 83 return static_cast<NativeViewHostAura*>(GetNativeWrapper()); | |
| 84 } | |
| 85 | |
| 86 Widget* child() { | |
| 87 return child_.get(); | |
| 88 } | |
| 89 | |
| 90 aura::Window* clipping_window() { return &(native_host()->clipping_window_); } | |
| 91 | |
| 92 void CreateHost() { | |
| 93 CreateTopLevel(); | |
| 94 CreateTestingHost(); | |
| 95 child_.reset(CreateChildForHost(toplevel()->GetNativeView(), | |
| 96 toplevel()->GetRootView(), | |
| 97 new View, | |
| 98 host())); | |
| 99 } | |
| 100 | |
| 101 private: | |
| 102 scoped_ptr<Widget> child_; | |
| 103 | |
| 104 DISALLOW_COPY_AND_ASSIGN(NativeViewHostAuraTest); | |
| 105 }; | |
| 106 | |
| 107 // Verifies NativeViewHostAura stops observing native view on destruction. | |
| 108 TEST_F(NativeViewHostAuraTest, StopObservingNativeViewOnDestruct) { | |
| 109 CreateHost(); | |
| 110 aura::Window* child_win = child()->GetNativeView(); | |
| 111 NativeViewHostAura* aura_host = native_host(); | |
| 112 | |
| 113 EXPECT_TRUE(child_win->HasObserver(aura_host)); | |
| 114 DestroyHost(); | |
| 115 EXPECT_FALSE(child_win->HasObserver(aura_host)); | |
| 116 } | |
| 117 | |
| 118 // Tests that the kHostViewKey is correctly set and cleared. | |
| 119 TEST_F(NativeViewHostAuraTest, HostViewPropertyKey) { | |
| 120 // Create the NativeViewHost and attach a NativeView. | |
| 121 CreateHost(); | |
| 122 aura::Window* child_win = child()->GetNativeView(); | |
| 123 EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey)); | |
| 124 EXPECT_EQ(host()->GetWidget()->GetNativeView(), | |
| 125 child_win->GetProperty(aura::client::kHostWindowKey)); | |
| 126 EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey)); | |
| 127 | |
| 128 host()->Detach(); | |
| 129 EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey)); | |
| 130 EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey)); | |
| 131 EXPECT_TRUE(clipping_window()->GetProperty(views::kHostViewKey)); | |
| 132 | |
| 133 host()->Attach(child_win); | |
| 134 EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey)); | |
| 135 EXPECT_EQ(host()->GetWidget()->GetNativeView(), | |
| 136 child_win->GetProperty(aura::client::kHostWindowKey)); | |
| 137 EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey)); | |
| 138 | |
| 139 DestroyHost(); | |
| 140 EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey)); | |
| 141 EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey)); | |
| 142 } | |
| 143 | |
| 144 // Tests that the NativeViewHost reports the cursor set on its native view. | |
| 145 TEST_F(NativeViewHostAuraTest, CursorForNativeView) { | |
| 146 CreateHost(); | |
| 147 | |
| 148 toplevel()->SetCursor(ui::kCursorHand); | |
| 149 child()->SetCursor(ui::kCursorWait); | |
| 150 ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), | |
| 151 gfx::Point(0, 0), 0, 0); | |
| 152 | |
| 153 EXPECT_EQ(ui::kCursorWait, host()->GetCursor(move_event).native_type()); | |
| 154 | |
| 155 DestroyHost(); | |
| 156 } | |
| 157 | |
| 158 // Test that destroying the top level widget before destroying the attached | |
| 159 // NativeViewHost works correctly. Specifically the associated NVH should be | |
| 160 // destroyed and there shouldn't be any errors. | |
| 161 TEST_F(NativeViewHostAuraTest, DestroyWidget) { | |
| 162 ResetHostDestroyedCount(); | |
| 163 CreateHost(); | |
| 164 ReleaseHost(); | |
| 165 EXPECT_EQ(0, host_destroyed_count()); | |
| 166 DestroyTopLevel(); | |
| 167 EXPECT_EQ(1, host_destroyed_count()); | |
| 168 } | |
| 169 | |
| 170 // Test that the fast resize path places the clipping and content windows were | |
| 171 // they are supposed to be. | |
| 172 TEST_F(NativeViewHostAuraTest, FastResizePath) { | |
| 173 CreateHost(); | |
| 174 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100)); | |
| 175 | |
| 176 // Without fast resize, the clipping window should size to the native view | |
| 177 // with the native view positioned at the origin of the clipping window and | |
| 178 // the clipping window positioned where the native view was requested. | |
| 179 host()->set_fast_resize(false); | |
| 180 native_host()->ShowWidget(5, 10, 100, 100); | |
| 181 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), | |
| 182 host()->native_view()->bounds().ToString()); | |
| 183 EXPECT_EQ(gfx::Rect(5, 10, 100, 100).ToString(), | |
| 184 clipping_window()->bounds().ToString()); | |
| 185 | |
| 186 // With fast resize, the native view should remain the same size but be | |
| 187 // clipped the requested size. | |
| 188 host()->set_fast_resize(true); | |
| 189 native_host()->ShowWidget(10, 25, 50, 50); | |
| 190 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), | |
| 191 host()->native_view()->bounds().ToString()); | |
| 192 EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(), | |
| 193 clipping_window()->bounds().ToString()); | |
| 194 | |
| 195 // Turning off fast resize should make the native view start resizing again. | |
| 196 host()->set_fast_resize(false); | |
| 197 native_host()->ShowWidget(10, 25, 50, 50); | |
| 198 EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(), | |
| 199 host()->native_view()->bounds().ToString()); | |
| 200 EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(), | |
| 201 clipping_window()->bounds().ToString()); | |
| 202 | |
| 203 DestroyHost(); | |
| 204 } | |
| 205 | |
| 206 // Test installing and uninstalling a clip. | |
| 207 TEST_F(NativeViewHostAuraTest, InstallClip) { | |
| 208 CreateHost(); | |
| 209 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100)); | |
| 210 | |
| 211 // Without a clip, the clipping window should always be positioned at the | |
| 212 // requested coordinates with the native view positioned at the origin of the | |
| 213 // clipping window. | |
| 214 native_host()->ShowWidget(10, 20, 100, 100); | |
| 215 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), | |
| 216 host()->native_view()->bounds().ToString()); | |
| 217 EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(), | |
| 218 clipping_window()->bounds().ToString()); | |
| 219 | |
| 220 // Clip to the bottom right quarter of the native view. | |
| 221 native_host()->InstallClip(60, 70, 50, 50); | |
| 222 native_host()->ShowWidget(10, 20, 100, 100); | |
| 223 EXPECT_EQ(gfx::Rect(-50, -50, 100, 100).ToString(), | |
| 224 host()->native_view()->bounds().ToString()); | |
| 225 EXPECT_EQ(gfx::Rect(60, 70, 50, 50).ToString(), | |
| 226 clipping_window()->bounds().ToString()); | |
| 227 | |
| 228 // Clip to the center of the native view. | |
| 229 native_host()->InstallClip(35, 45, 50, 50); | |
| 230 native_host()->ShowWidget(10, 20, 100, 100); | |
| 231 EXPECT_EQ(gfx::Rect(-25, -25, 100, 100).ToString(), | |
| 232 host()->native_view()->bounds().ToString()); | |
| 233 EXPECT_EQ(gfx::Rect(35, 45, 50, 50).ToString(), | |
| 234 clipping_window()->bounds().ToString()); | |
| 235 | |
| 236 // Uninstalling the clip should make the clipping window match the native view | |
| 237 // again. | |
| 238 native_host()->UninstallClip(); | |
| 239 native_host()->ShowWidget(10, 20, 100, 100); | |
| 240 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), | |
| 241 host()->native_view()->bounds().ToString()); | |
| 242 EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(), | |
| 243 clipping_window()->bounds().ToString()); | |
| 244 | |
| 245 DestroyHost(); | |
| 246 } | |
| 247 | |
| 248 // Ensure native view is parented to the root window after detaching. This is | |
| 249 // a regression test for http://crbug.com/389261. | |
| 250 TEST_F(NativeViewHostAuraTest, ParentAfterDetach) { | |
| 251 CreateHost(); | |
| 252 aura::Window* child_win = child()->GetNativeView(); | |
| 253 aura::Window* root_window = child_win->GetRootWindow(); | |
| 254 aura::WindowTreeHost* child_win_tree_host = child_win->GetHost(); | |
| 255 | |
| 256 host()->Detach(); | |
| 257 EXPECT_EQ(root_window, child_win->GetRootWindow()); | |
| 258 EXPECT_EQ(child_win_tree_host, child_win->GetHost()); | |
| 259 | |
| 260 DestroyHost(); | |
| 261 } | |
| 262 | |
| 263 // Ensure the clipping window is hidden before setting the native view's bounds. | |
| 264 // This is a regression test for http://crbug.com/388699. | |
| 265 TEST_F(NativeViewHostAuraTest, RemoveClippingWindowOrder) { | |
| 266 CreateHost(); | |
| 267 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100)); | |
| 268 native_host()->ShowWidget(10, 20, 100, 100); | |
| 269 | |
| 270 NativeViewHostWindowObserver test_observer; | |
| 271 clipping_window()->AddObserver(&test_observer); | |
| 272 child()->GetNativeView()->AddObserver(&test_observer); | |
| 273 | |
| 274 host()->Detach(); | |
| 275 | |
| 276 ASSERT_EQ(3u, test_observer.events().size()); | |
| 277 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN, | |
| 278 test_observer.events()[0].type); | |
| 279 EXPECT_EQ(clipping_window(), test_observer.events()[0].window); | |
| 280 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED, | |
| 281 test_observer.events()[1].type); | |
| 282 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window); | |
| 283 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN, | |
| 284 test_observer.events()[2].type); | |
| 285 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[2].window); | |
| 286 | |
| 287 clipping_window()->RemoveObserver(&test_observer); | |
| 288 child()->GetNativeView()->RemoveObserver(&test_observer); | |
| 289 | |
| 290 DestroyHost(); | |
| 291 } | |
| 292 | |
| 293 // Ensure the native view receives the correct bounds notification when it is | |
| 294 // attached. This is a regression test for https://crbug.com/399420. | |
| 295 TEST_F(NativeViewHostAuraTest, Attach) { | |
| 296 CreateHost(); | |
| 297 host()->Detach(); | |
| 298 | |
| 299 child()->GetNativeView()->SetBounds(gfx::Rect(0, 0, 0, 0)); | |
| 300 toplevel()->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
| 301 host()->SetBounds(10, 10, 80, 80); | |
| 302 | |
| 303 NativeViewHostWindowObserver test_observer; | |
| 304 child()->GetNativeView()->AddObserver(&test_observer); | |
| 305 | |
| 306 host()->Attach(child()->GetNativeView()); | |
| 307 | |
| 308 ASSERT_EQ(3u, test_observer.events().size()); | |
| 309 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED, | |
| 310 test_observer.events()[0].type); | |
| 311 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[0].window); | |
| 312 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(), | |
| 313 test_observer.events()[0].bounds.ToString()); | |
| 314 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN, | |
| 315 test_observer.events()[1].type); | |
| 316 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window); | |
| 317 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(), | |
| 318 test_observer.events()[1].bounds.ToString()); | |
| 319 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN, | |
| 320 test_observer.events()[2].type); | |
| 321 EXPECT_EQ(clipping_window(), test_observer.events()[2].window); | |
| 322 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(), | |
| 323 test_observer.events()[2].bounds.ToString()); | |
| 324 | |
| 325 child()->GetNativeView()->RemoveObserver(&test_observer); | |
| 326 DestroyHost(); | |
| 327 } | |
| 328 | |
| 329 // Ensure the clipping window is hidden with the native view. This is a | |
| 330 // regression test for https://crbug.com/408877. | |
| 331 TEST_F(NativeViewHostAuraTest, SimpleShowAndHide) { | |
| 332 CreateHost(); | |
| 333 | |
| 334 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100)); | |
| 335 toplevel()->Show(); | |
| 336 | |
| 337 host()->SetBounds(10, 10, 80, 80); | |
| 338 EXPECT_TRUE(clipping_window()->IsVisible()); | |
| 339 EXPECT_TRUE(child()->IsVisible()); | |
| 340 | |
| 341 host()->SetVisible(false); | |
| 342 EXPECT_FALSE(clipping_window()->IsVisible()); | |
| 343 EXPECT_FALSE(child()->IsVisible()); | |
| 344 | |
| 345 DestroyHost(); | |
| 346 DestroyTopLevel(); | |
| 347 } | |
| 348 | |
| 349 } // namespace views | |
| OLD | NEW |