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/corewm/tooltip_controller.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "ui/aura/client/cursor_client.h" 9 #include "ui/aura/client/screen_position_client.h" 10 #include "ui/aura/env.h" 11 #include "ui/aura/test/aura_test_base.h" 12 #include "ui/aura/test/test_screen.h" 13 #include "ui/aura/test/test_window_delegate.h" 14 #include "ui/aura/window.h" 15 #include "ui/aura/window_event_dispatcher.h" 16 #include "ui/events/test/event_generator.h" 17 #include "ui/gfx/font.h" 18 #include "ui/gfx/point.h" 19 #include "ui/gfx/screen.h" 20 #include "ui/gfx/screen_type_delegate.h" 21 #include "ui/gfx/text_elider.h" 22 #include "ui/views/corewm/tooltip_aura.h" 23 #include "ui/views/corewm/tooltip_controller_test_helper.h" 24 #include "ui/views/test/desktop_test_views_delegate.h" 25 #include "ui/views/test/test_views_delegate.h" 26 #include "ui/views/view.h" 27 #include "ui/views/widget/tooltip_manager.h" 28 #include "ui/views/widget/widget.h" 29 #include "ui/wm/core/default_activation_client.h" 30 #include "ui/wm/core/wm_state.h" 31 #include "ui/wm/public/tooltip_client.h" 32 #include "ui/wm/public/window_types.h" 33 34 #if defined(OS_WIN) 35 #include "ui/base/win/scoped_ole_initializer.h" 36 #endif 37 38 #if !defined(OS_CHROMEOS) 39 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 40 #include "ui/views/widget/desktop_aura/desktop_screen.h" 41 #endif 42 43 using base::ASCIIToUTF16; 44 45 namespace views { 46 namespace corewm { 47 namespace test { 48 namespace { 49 50 views::Widget* CreateWidget(aura::Window* root) { 51 views::Widget* widget = new views::Widget; 52 views::Widget::InitParams params; 53 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; 54 params.accept_events = true; 55 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 56 #if defined(OS_CHROMEOS) 57 params.parent = root; 58 #else 59 params.native_widget = new DesktopNativeWidgetAura(widget); 60 #endif 61 params.bounds = gfx::Rect(0, 0, 200, 100); 62 widget->Init(params); 63 widget->Show(); 64 return widget; 65 } 66 67 TooltipController* GetController(Widget* widget) { 68 return static_cast<TooltipController*>( 69 aura::client::GetTooltipClient( 70 widget->GetNativeWindow()->GetRootWindow())); 71 } 72 73 } // namespace 74 75 class TooltipControllerTest : public aura::test::AuraTestBase { 76 public: 77 TooltipControllerTest() : view_(NULL) {} 78 virtual ~TooltipControllerTest() {} 79 80 virtual void SetUp() OVERRIDE { 81 #if defined(OS_CHROMEOS) 82 views_delegate_.reset(new TestViewsDelegate); 83 #else 84 views_delegate_.reset(new DesktopTestViewsDelegate); 85 #endif 86 87 aura::test::AuraTestBase::SetUp(); 88 new wm::DefaultActivationClient(root_window()); 89 #if defined(OS_CHROMEOS) 90 controller_.reset(new TooltipController( 91 scoped_ptr<views::corewm::Tooltip>( 92 new views::corewm::TooltipAura(gfx::SCREEN_TYPE_ALTERNATE)))); 93 root_window()->AddPreTargetHandler(controller_.get()); 94 SetTooltipClient(root_window(), controller_.get()); 95 #endif 96 widget_.reset(CreateWidget(root_window())); 97 widget_->SetContentsView(new View); 98 view_ = new TooltipTestView; 99 widget_->GetContentsView()->AddChildView(view_); 100 view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds()); 101 helper_.reset(new TooltipControllerTestHelper( 102 GetController(widget_.get()))); 103 generator_.reset(new ui::test::EventGenerator(GetRootWindow())); 104 } 105 106 virtual void TearDown() OVERRIDE { 107 #if defined(OS_CHROMEOS) 108 root_window()->RemovePreTargetHandler(controller_.get()); 109 aura::client::SetTooltipClient(root_window(), NULL); 110 controller_.reset(); 111 #endif 112 generator_.reset(); 113 helper_.reset(); 114 widget_.reset(); 115 aura::test::AuraTestBase::TearDown(); 116 views_delegate_.reset(); 117 } 118 119 protected: 120 aura::Window* GetWindow() { 121 return widget_->GetNativeWindow(); 122 } 123 124 aura::Window* GetRootWindow() { 125 return GetWindow()->GetRootWindow(); 126 } 127 128 TooltipTestView* PrepareSecondView() { 129 TooltipTestView* view2 = new TooltipTestView; 130 widget_->GetContentsView()->AddChildView(view2); 131 view_->SetBounds(0, 0, 100, 100); 132 view2->SetBounds(100, 0, 100, 100); 133 return view2; 134 } 135 136 scoped_ptr<views::Widget> widget_; 137 TooltipTestView* view_; 138 scoped_ptr<TooltipControllerTestHelper> helper_; 139 scoped_ptr<ui::test::EventGenerator> generator_; 140 141 private: 142 scoped_ptr<TooltipController> controller_; 143 144 scoped_ptr<views::TestViewsDelegate> views_delegate_; 145 146 #if defined(OS_WIN) 147 ui::ScopedOleInitializer ole_initializer_; 148 #endif 149 150 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest); 151 }; 152 153 TEST_F(TooltipControllerTest, ViewTooltip) { 154 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 155 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 156 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 157 generator_->MoveMouseToCenterOf(GetWindow()); 158 159 EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint( 160 generator_->current_location())); 161 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 162 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 163 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 164 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 165 166 // Fire tooltip timer so tooltip becomes visible. 167 helper_->FireTooltipTimer(); 168 169 EXPECT_TRUE(helper_->IsTooltipVisible()); 170 generator_->MoveMouseBy(1, 0); 171 172 EXPECT_TRUE(helper_->IsTooltipVisible()); 173 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 174 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 175 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 176 } 177 178 TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { 179 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 180 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 181 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 182 183 PrepareSecondView(); 184 aura::Window* window = GetWindow(); 185 aura::Window* root_window = GetRootWindow(); 186 187 // Fire tooltip timer so tooltip becomes visible. 188 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 189 helper_->FireTooltipTimer(); 190 EXPECT_TRUE(helper_->IsTooltipVisible()); 191 for (int i = 0; i < 49; ++i) { 192 generator_->MoveMouseBy(1, 0); 193 EXPECT_TRUE(helper_->IsTooltipVisible()); 194 EXPECT_EQ(window, root_window->GetEventHandlerForPoint( 195 generator_->current_location())); 196 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 197 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 198 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 199 EXPECT_EQ(window, helper_->GetTooltipWindow()); 200 } 201 for (int i = 0; i < 49; ++i) { 202 generator_->MoveMouseBy(1, 0); 203 EXPECT_FALSE(helper_->IsTooltipVisible()); 204 EXPECT_EQ(window, root_window->GetEventHandlerForPoint( 205 generator_->current_location())); 206 base::string16 expected_tooltip; // = "" 207 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 208 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 209 EXPECT_EQ(window, helper_->GetTooltipWindow()); 210 } 211 } 212 213 TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { 214 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 215 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 216 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 217 218 generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); 219 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 220 221 // Fire tooltip timer so tooltip becomes visible. 222 helper_->FireTooltipTimer(); 223 EXPECT_TRUE(helper_->IsTooltipVisible()); 224 225 // Disable tooltips and check again. 226 helper_->controller()->SetTooltipsEnabled(false); 227 EXPECT_FALSE(helper_->IsTooltipVisible()); 228 helper_->FireTooltipTimer(); 229 EXPECT_FALSE(helper_->IsTooltipVisible()); 230 231 // Enable tooltips back and check again. 232 helper_->controller()->SetTooltipsEnabled(true); 233 EXPECT_FALSE(helper_->IsTooltipVisible()); 234 helper_->FireTooltipTimer(); 235 EXPECT_TRUE(helper_->IsTooltipVisible()); 236 } 237 238 // Verifies tooltip isn't shown if tooltip text consists entirely of whitespace. 239 TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { 240 view_->set_tooltip_text(ASCIIToUTF16(" ")); 241 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 242 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 243 244 generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); 245 246 helper_->FireTooltipTimer(); 247 EXPECT_FALSE(helper_->IsTooltipVisible()); 248 } 249 250 TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { 251 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); 252 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 253 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 254 255 TooltipTestView* view2 = PrepareSecondView(); 256 view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2")); 257 258 aura::Window* window = GetWindow(); 259 260 // Fire tooltip timer so tooltip becomes visible. 261 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 262 helper_->FireTooltipTimer(); 263 EXPECT_TRUE(helper_->IsTooltipVisible()); 264 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 265 266 generator_->PressKey(ui::VKEY_1, 0); 267 EXPECT_FALSE(helper_->IsTooltipVisible()); 268 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 269 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 270 271 // Moving the mouse inside |view1| should not change the state of the tooltip 272 // or the timers. 273 for (int i = 0; i < 49; i++) { 274 generator_->MoveMouseBy(1, 0); 275 EXPECT_FALSE(helper_->IsTooltipVisible()); 276 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 277 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 278 EXPECT_EQ(window, 279 GetRootWindow()->GetEventHandlerForPoint( 280 generator_->current_location())); 281 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); 282 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 283 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 284 EXPECT_EQ(window, helper_->GetTooltipWindow()); 285 } 286 287 // Now we move the mouse on to |view2|. It should re-start the tooltip timer. 288 generator_->MoveMouseBy(1, 0); 289 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 290 helper_->FireTooltipTimer(); 291 EXPECT_TRUE(helper_->IsTooltipVisible()); 292 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 293 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); 294 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 295 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 296 EXPECT_EQ(window, helper_->GetTooltipWindow()); 297 } 298 299 TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { 300 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); 301 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 302 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 303 304 TooltipTestView* view2 = PrepareSecondView(); 305 view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2")); 306 307 aura::Window* window = GetWindow(); 308 309 // Fire tooltip timer so tooltip becomes visible. 310 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 311 helper_->FireTooltipTimer(); 312 EXPECT_TRUE(helper_->IsTooltipVisible()); 313 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 314 315 helper_->FireTooltipShownTimer(); 316 EXPECT_FALSE(helper_->IsTooltipVisible()); 317 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 318 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 319 320 // Moving the mouse inside |view1| should not change the state of the tooltip 321 // or the timers. 322 for (int i = 0; i < 49; ++i) { 323 generator_->MoveMouseBy(1, 0); 324 EXPECT_FALSE(helper_->IsTooltipVisible()); 325 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 326 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 327 EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint( 328 generator_->current_location())); 329 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); 330 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 331 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 332 EXPECT_EQ(window, helper_->GetTooltipWindow()); 333 } 334 335 // Now we move the mouse on to |view2|. It should re-start the tooltip timer. 336 generator_->MoveMouseBy(1, 0); 337 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 338 helper_->FireTooltipTimer(); 339 EXPECT_TRUE(helper_->IsTooltipVisible()); 340 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 341 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); 342 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 343 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 344 EXPECT_EQ(window, helper_->GetTooltipWindow()); 345 } 346 347 // Verifies a mouse exit event hides the tooltips. 348 TEST_F(TooltipControllerTest, HideOnExit) { 349 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 350 generator_->MoveMouseToCenterOf(GetWindow()); 351 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 352 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 353 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 354 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 355 356 // Fire tooltip timer so tooltip becomes visible. 357 helper_->FireTooltipTimer(); 358 359 EXPECT_TRUE(helper_->IsTooltipVisible()); 360 generator_->SendMouseExit(); 361 EXPECT_FALSE(helper_->IsTooltipVisible()); 362 } 363 364 TEST_F(TooltipControllerTest, ReshowOnClickAfterEnterExit) { 365 // Owned by |view_|. 366 TooltipTestView* v1 = new TooltipTestView; 367 TooltipTestView* v2 = new TooltipTestView; 368 view_->AddChildView(v1); 369 view_->AddChildView(v2); 370 gfx::Rect view_bounds(view_->GetLocalBounds()); 371 view_bounds.set_height(view_bounds.height() / 2); 372 v1->SetBoundsRect(view_bounds); 373 view_bounds.set_y(view_bounds.height()); 374 v2->SetBoundsRect(view_bounds); 375 const base::string16 v1_tt(ASCIIToUTF16("v1")); 376 const base::string16 v2_tt(ASCIIToUTF16("v2")); 377 v1->set_tooltip_text(v1_tt); 378 v2->set_tooltip_text(v2_tt); 379 380 gfx::Point v1_point(1, 1); 381 View::ConvertPointToWidget(v1, &v1_point); 382 generator_->MoveMouseRelativeTo(GetWindow(), v1_point); 383 384 // Fire tooltip timer so tooltip becomes visible. 385 helper_->FireTooltipTimer(); 386 EXPECT_TRUE(helper_->IsTooltipVisible()); 387 EXPECT_EQ(v1_tt, helper_->GetTooltipText()); 388 389 // Press the mouse, move to v2 and back to v1. 390 generator_->ClickLeftButton(); 391 392 gfx::Point v2_point(1, 1); 393 View::ConvertPointToWidget(v2, &v2_point); 394 generator_->MoveMouseRelativeTo(GetWindow(), v2_point); 395 generator_->MoveMouseRelativeTo(GetWindow(), v1_point); 396 397 helper_->FireTooltipTimer(); 398 EXPECT_TRUE(helper_->IsTooltipVisible()); 399 EXPECT_EQ(v1_tt, helper_->GetTooltipText()); 400 } 401 402 namespace { 403 404 // Returns the index of |window| in its parent's children. 405 int IndexInParent(const aura::Window* window) { 406 aura::Window::Windows::const_iterator i = 407 std::find(window->parent()->children().begin(), 408 window->parent()->children().end(), 409 window); 410 return i == window->parent()->children().end() ? -1 : 411 static_cast<int>(i - window->parent()->children().begin()); 412 } 413 414 class TestScreenPositionClient : public aura::client::ScreenPositionClient { 415 public: 416 TestScreenPositionClient() {} 417 virtual ~TestScreenPositionClient() {} 418 419 // ScreenPositionClient overrides: 420 virtual void ConvertPointToScreen(const aura::Window* window, 421 gfx::Point* point) OVERRIDE { 422 } 423 virtual void ConvertPointFromScreen(const aura::Window* window, 424 gfx::Point* point) OVERRIDE { 425 } 426 virtual void ConvertHostPointToScreen(aura::Window* root_gwindow, 427 gfx::Point* point) OVERRIDE { 428 NOTREACHED(); 429 } 430 virtual void SetBounds(aura::Window* window, 431 const gfx::Rect& bounds, 432 const gfx::Display& display) OVERRIDE { 433 window->SetBounds(bounds); 434 } 435 436 private: 437 DISALLOW_COPY_AND_ASSIGN(TestScreenPositionClient); 438 }; 439 440 } // namespace 441 442 class TooltipControllerCaptureTest : public TooltipControllerTest { 443 public: 444 TooltipControllerCaptureTest() {} 445 virtual ~TooltipControllerCaptureTest() {} 446 447 virtual void SetUp() OVERRIDE { 448 TooltipControllerTest::SetUp(); 449 aura::client::SetScreenPositionClient(GetRootWindow(), 450 &screen_position_client_); 451 #if !defined(OS_CHROMEOS) 452 desktop_screen_.reset(CreateDesktopScreen()); 453 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, 454 desktop_screen_.get()); 455 #endif 456 } 457 458 virtual void TearDown() OVERRIDE { 459 #if !defined(OS_CHROMEOS) 460 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen()); 461 desktop_screen_.reset(); 462 #endif 463 aura::client::SetScreenPositionClient(GetRootWindow(), NULL); 464 TooltipControllerTest::TearDown(); 465 } 466 467 private: 468 TestScreenPositionClient screen_position_client_; 469 scoped_ptr<gfx::Screen> desktop_screen_; 470 471 DISALLOW_COPY_AND_ASSIGN(TooltipControllerCaptureTest); 472 }; 473 474 // Verifies when capture is released the TooltipController resets state. 475 // Flaky on all builders. http://crbug.com/388268 476 TEST_F(TooltipControllerCaptureTest, DISABLED_CloseOnCaptureLost) { 477 view_->GetWidget()->SetCapture(view_); 478 RunAllPendingInMessageLoop(); 479 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 480 generator_->MoveMouseToCenterOf(GetWindow()); 481 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 482 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 483 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 484 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 485 486 // Fire tooltip timer so tooltip becomes visible. 487 helper_->FireTooltipTimer(); 488 489 EXPECT_TRUE(helper_->IsTooltipVisible()); 490 view_->GetWidget()->ReleaseCapture(); 491 EXPECT_FALSE(helper_->IsTooltipVisible()); 492 EXPECT_TRUE(helper_->GetTooltipWindow() == NULL); 493 } 494 495 // Disabled on linux as DesktopScreenX11::GetWindowAtScreenPoint() doesn't 496 // consider z-order. 497 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 498 #define MAYBE_Capture DISABLED_Capture 499 #else 500 #define MAYBE_Capture Capture 501 #endif 502 // Verifies the correct window is found for tooltips when there is a capture. 503 TEST_F(TooltipControllerCaptureTest, MAYBE_Capture) { 504 const base::string16 tooltip_text(ASCIIToUTF16("1")); 505 const base::string16 tooltip_text2(ASCIIToUTF16("2")); 506 507 widget_->SetBounds(gfx::Rect(0, 0, 200, 200)); 508 view_->set_tooltip_text(tooltip_text); 509 510 scoped_ptr<views::Widget> widget2(CreateWidget(root_window())); 511 widget2->SetContentsView(new View); 512 TooltipTestView* view2 = new TooltipTestView; 513 widget2->GetContentsView()->AddChildView(view2); 514 view2->set_tooltip_text(tooltip_text2); 515 widget2->SetBounds(gfx::Rect(0, 0, 200, 200)); 516 view2->SetBoundsRect(widget2->GetContentsView()->GetLocalBounds()); 517 518 widget_->SetCapture(view_); 519 EXPECT_TRUE(widget_->HasCapture()); 520 widget2->Show(); 521 EXPECT_GE(IndexInParent(widget2->GetNativeWindow()), 522 IndexInParent(widget_->GetNativeWindow())); 523 524 generator_->MoveMouseRelativeTo(widget_->GetNativeWindow(), 525 view_->bounds().CenterPoint()); 526 527 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 528 helper_->FireTooltipTimer(); 529 // Even though the mouse is over a window with a tooltip it shouldn't be 530 // picked up because the windows don't have the same value for 531 // |TooltipManager::kGroupingPropertyKey|. 532 EXPECT_TRUE(helper_->GetTooltipText().empty()); 533 534 // Now make both the windows have same transient value for 535 // kGroupingPropertyKey. In this case the tooltip should be picked up from 536 // |widget2| (because the mouse is over it). 537 const int grouping_key = 1; 538 widget_->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey, 539 reinterpret_cast<void*>(grouping_key)); 540 widget2->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey, 541 reinterpret_cast<void*>(grouping_key)); 542 generator_->MoveMouseBy(1, 10); 543 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 544 helper_->FireTooltipTimer(); 545 EXPECT_EQ(tooltip_text2, helper_->GetTooltipText()); 546 547 widget2.reset(); 548 } 549 550 namespace { 551 552 class TestTooltip : public Tooltip { 553 public: 554 TestTooltip() : is_visible_(false) {} 555 virtual ~TestTooltip() {} 556 557 const base::string16& tooltip_text() const { return tooltip_text_; } 558 559 // Tooltip: 560 virtual void SetText(aura::Window* window, 561 const base::string16& tooltip_text, 562 const gfx::Point& location) OVERRIDE { 563 tooltip_text_ = tooltip_text; 564 location_ = location; 565 } 566 virtual void Show() OVERRIDE { 567 is_visible_ = true; 568 } 569 virtual void Hide() OVERRIDE { 570 is_visible_ = false; 571 } 572 virtual bool IsVisible() OVERRIDE { 573 return is_visible_; 574 } 575 const gfx::Point& location() { return location_; } 576 577 private: 578 bool is_visible_; 579 base::string16 tooltip_text_; 580 gfx::Point location_; 581 582 DISALLOW_COPY_AND_ASSIGN(TestTooltip); 583 }; 584 585 } // namespace 586 587 // Use for tests that don't depend upon views. 588 class TooltipControllerTest2 : public aura::test::AuraTestBase { 589 public: 590 TooltipControllerTest2() : test_tooltip_(new TestTooltip) {} 591 virtual ~TooltipControllerTest2() {} 592 593 virtual void SetUp() OVERRIDE { 594 wm_state_.reset(new wm::WMState); 595 aura::test::AuraTestBase::SetUp(); 596 new wm::DefaultActivationClient(root_window()); 597 controller_.reset(new TooltipController( 598 scoped_ptr<corewm::Tooltip>(test_tooltip_))); 599 root_window()->AddPreTargetHandler(controller_.get()); 600 SetTooltipClient(root_window(), controller_.get()); 601 helper_.reset(new TooltipControllerTestHelper(controller_.get())); 602 generator_.reset(new ui::test::EventGenerator(root_window())); 603 } 604 605 virtual void TearDown() OVERRIDE { 606 root_window()->RemovePreTargetHandler(controller_.get()); 607 aura::client::SetTooltipClient(root_window(), NULL); 608 controller_.reset(); 609 generator_.reset(); 610 helper_.reset(); 611 aura::test::AuraTestBase::TearDown(); 612 wm_state_.reset(); 613 } 614 615 protected: 616 // Owned by |controller_|. 617 TestTooltip* test_tooltip_; 618 scoped_ptr<TooltipControllerTestHelper> helper_; 619 scoped_ptr<ui::test::EventGenerator> generator_; 620 621 private: 622 scoped_ptr<TooltipController> controller_; 623 scoped_ptr<wm::WMState> wm_state_; 624 625 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest2); 626 }; 627 628 TEST_F(TooltipControllerTest2, VerifyLeadingTrailingWhitespaceStripped) { 629 aura::test::TestWindowDelegate test_delegate; 630 scoped_ptr<aura::Window> window( 631 CreateNormalWindow(100, root_window(), &test_delegate)); 632 window->SetBounds(gfx::Rect(0, 0, 300, 300)); 633 base::string16 tooltip_text(ASCIIToUTF16(" \nx ")); 634 aura::client::SetTooltipText(window.get(), &tooltip_text); 635 generator_->MoveMouseToCenterOf(window.get()); 636 helper_->FireTooltipTimer(); 637 EXPECT_EQ(ASCIIToUTF16("x"), test_tooltip_->tooltip_text()); 638 } 639 640 // Verifies that tooltip is hidden and tooltip window closed upon cancel mode. 641 TEST_F(TooltipControllerTest2, CloseOnCancelMode) { 642 aura::test::TestWindowDelegate test_delegate; 643 scoped_ptr<aura::Window> window( 644 CreateNormalWindow(100, root_window(), &test_delegate)); 645 window->SetBounds(gfx::Rect(0, 0, 300, 300)); 646 base::string16 tooltip_text(ASCIIToUTF16("Tooltip Text")); 647 aura::client::SetTooltipText(window.get(), &tooltip_text); 648 generator_->MoveMouseToCenterOf(window.get()); 649 650 // Fire tooltip timer so tooltip becomes visible. 651 helper_->FireTooltipTimer(); 652 EXPECT_TRUE(helper_->IsTooltipVisible()); 653 654 // Send OnCancelMode event and verify that tooltip becomes invisible and 655 // the tooltip window is closed. 656 ui::CancelModeEvent event; 657 helper_->controller()->OnCancelMode(&event); 658 EXPECT_FALSE(helper_->IsTooltipVisible()); 659 EXPECT_TRUE(helper_->GetTooltipWindow() == NULL); 660 } 661 662 // Use for tests that need both views and a TestTooltip. 663 class TooltipControllerTest3 : public aura::test::AuraTestBase { 664 public: 665 TooltipControllerTest3() : test_tooltip_(new TestTooltip) {} 666 virtual ~TooltipControllerTest3() {} 667 668 virtual void SetUp() OVERRIDE { 669 wm_state_.reset(new wm::WMState); 670 aura::test::AuraTestBase::SetUp(); 671 new wm::DefaultActivationClient(root_window()); 672 673 widget_.reset(CreateWidget(root_window())); 674 widget_->SetContentsView(new View); 675 view_ = new TooltipTestView; 676 widget_->GetContentsView()->AddChildView(view_); 677 view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds()); 678 679 generator_.reset(new ui::test::EventGenerator(GetRootWindow())); 680 controller_.reset(new TooltipController( 681 scoped_ptr<views::corewm::Tooltip>(test_tooltip_))); 682 GetRootWindow()->RemovePreTargetHandler( 683 static_cast<TooltipController*>(aura::client::GetTooltipClient( 684 widget_->GetNativeWindow()->GetRootWindow()))); 685 GetRootWindow()->AddPreTargetHandler(controller_.get()); 686 helper_.reset(new TooltipControllerTestHelper(controller_.get())); 687 SetTooltipClient(GetRootWindow(), controller_.get()); 688 } 689 690 virtual void TearDown() OVERRIDE { 691 GetRootWindow()->RemovePreTargetHandler(controller_.get()); 692 aura::client::SetTooltipClient(GetRootWindow(), NULL); 693 694 controller_.reset(); 695 generator_.reset(); 696 helper_.reset(); 697 widget_.reset(); 698 aura::test::AuraTestBase::TearDown(); 699 wm_state_.reset(); 700 } 701 702 aura::Window* GetWindow() { return widget_->GetNativeWindow(); } 703 704 protected: 705 // Owned by |controller_|. 706 TestTooltip* test_tooltip_; 707 scoped_ptr<TooltipControllerTestHelper> helper_; 708 scoped_ptr<ui::test::EventGenerator> generator_; 709 scoped_ptr<views::Widget> widget_; 710 TooltipTestView* view_; 711 712 private: 713 scoped_ptr<TooltipController> controller_; 714 scoped_ptr<wm::WMState> wm_state_; 715 716 #if defined(OS_WIN) 717 ui::ScopedOleInitializer ole_initializer_; 718 #endif 719 720 aura::Window* GetRootWindow() { return GetWindow()->GetRootWindow(); } 721 722 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest3); 723 }; 724 725 TEST_F(TooltipControllerTest3, TooltipPositionChangesOnTwoViewsWithSameLabel) { 726 // Owned by |view_|. 727 // These two views have the same tooltip text 728 TooltipTestView* v1 = new TooltipTestView; 729 TooltipTestView* v2 = new TooltipTestView; 730 // v1_1 is a view inside v1 that has an identical tooltip text to that of v1 731 // and v2 732 TooltipTestView* v1_1 = new TooltipTestView; 733 // v2_1 is a view inside v2 that has an identical tooltip text to that of v1 734 // and v2 735 TooltipTestView* v2_1 = new TooltipTestView; 736 // v2_2 is a view inside v2 with the tooltip text different from all the 737 // others 738 TooltipTestView* v2_2 = new TooltipTestView; 739 740 // Setup all the views' relations 741 view_->AddChildView(v1); 742 view_->AddChildView(v2); 743 v1->AddChildView(v1_1); 744 v2->AddChildView(v2_1); 745 v2->AddChildView(v2_2); 746 const base::string16 reference_string( 747 base::ASCIIToUTF16("Identical Tooltip Text")); 748 const base::string16 alternative_string( 749 base::ASCIIToUTF16("Another Shrubbery")); 750 v1->set_tooltip_text(reference_string); 751 v2->set_tooltip_text(reference_string); 752 v1_1->set_tooltip_text(reference_string); 753 v2_1->set_tooltip_text(reference_string); 754 v2_2->set_tooltip_text(alternative_string); 755 756 // Set views' bounds 757 gfx::Rect view_bounds(view_->GetLocalBounds()); 758 view_bounds.set_height(view_bounds.height() / 2); 759 v1->SetBoundsRect(view_bounds); 760 v1_1->SetBounds(0, 0, 3, 3); 761 view_bounds.set_y(view_bounds.height()); 762 v2->SetBoundsRect(view_bounds); 763 v2_2->SetBounds(view_bounds.width() - 3, view_bounds.height() - 3, 3, 3); 764 v2_1->SetBounds(0, 0, 3, 3); 765 766 // Test whether a toolbar appears on v1 767 gfx::Point center = v1->bounds().CenterPoint(); 768 generator_->MoveMouseRelativeTo(GetWindow(), center); 769 helper_->FireTooltipTimer(); 770 EXPECT_TRUE(helper_->IsTooltipVisible()); 771 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 772 gfx::Point tooltip_bounds1 = test_tooltip_->location(); 773 774 // Test whether the toolbar changes position on mouse over v2 775 center = v2->bounds().CenterPoint(); 776 generator_->MoveMouseRelativeTo(GetWindow(), center); 777 helper_->FireTooltipTimer(); 778 EXPECT_TRUE(helper_->IsTooltipVisible()); 779 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 780 gfx::Point tooltip_bounds2 = test_tooltip_->location(); 781 782 EXPECT_NE(tooltip_bounds1, gfx::Point()); 783 EXPECT_NE(tooltip_bounds2, gfx::Point()); 784 EXPECT_NE(tooltip_bounds1, tooltip_bounds2); 785 786 // Test if the toolbar does not change position on encountering a contained 787 // view with the same tooltip text 788 center = v2_1->GetLocalBounds().CenterPoint(); 789 views::View::ConvertPointToTarget(v2_1, view_, ¢er); 790 generator_->MoveMouseRelativeTo(GetWindow(), center); 791 helper_->FireTooltipTimer(); 792 gfx::Point tooltip_bounds2_1 = test_tooltip_->location(); 793 794 EXPECT_NE(tooltip_bounds2, tooltip_bounds2_1); 795 EXPECT_TRUE(helper_->IsTooltipVisible()); 796 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 797 798 // Test if the toolbar changes position on encountering a contained 799 // view with a different tooltip text 800 center = v2_2->GetLocalBounds().CenterPoint(); 801 views::View::ConvertPointToTarget(v2_2, view_, ¢er); 802 generator_->MoveMouseRelativeTo(GetWindow(), center); 803 helper_->FireTooltipTimer(); 804 gfx::Point tooltip_bounds2_2 = test_tooltip_->location(); 805 806 EXPECT_NE(tooltip_bounds2_1, tooltip_bounds2_2); 807 EXPECT_TRUE(helper_->IsTooltipVisible()); 808 EXPECT_EQ(alternative_string, helper_->GetTooltipText()); 809 810 // Test if moving from a view that is contained by a larger view, both with 811 // the same tooltip text, does not change tooltip's position. 812 center = v1_1->GetLocalBounds().CenterPoint(); 813 views::View::ConvertPointToTarget(v1_1, view_, ¢er); 814 generator_->MoveMouseRelativeTo(GetWindow(), center); 815 helper_->FireTooltipTimer(); 816 gfx::Point tooltip_bounds1_1 = test_tooltip_->location(); 817 818 EXPECT_TRUE(helper_->IsTooltipVisible()); 819 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 820 821 center = v1->bounds().CenterPoint(); 822 generator_->MoveMouseRelativeTo(GetWindow(), center); 823 helper_->FireTooltipTimer(); 824 tooltip_bounds1 = test_tooltip_->location(); 825 826 EXPECT_NE(tooltip_bounds1_1, tooltip_bounds1); 827 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 828 } 829 830 } // namespace test 831 } // namespace corewm 832 } // namespace views 833