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 18namespace 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. 23class 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 77class 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. 108TEST_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. 119TEST_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. 145TEST_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. 161TEST_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. 172TEST_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. 207TEST_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. 250TEST_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. 265TEST_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. 295TEST_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. 331TEST_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 350