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
43using base::ASCIIToUTF16;
44
45namespace views {
46namespace corewm {
47namespace test {
48namespace {
49
50views::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
67TooltipController* GetController(Widget* widget) {
68  return static_cast<TooltipController*>(
69      aura::client::GetTooltipClient(
70          widget->GetNativeWindow()->GetRootWindow()));
71}
72
73}  // namespace
74
75class 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
153TEST_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
178TEST_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
213TEST_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.
239TEST_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
250TEST_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
299TEST_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.
348TEST_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
364TEST_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
402namespace {
403
404// Returns the index of |window| in its parent's children.
405int 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
414class 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
442class 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
476TEST_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.
503TEST_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
550namespace {
551
552class 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.
588class 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
628TEST_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.
641TEST_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.
663class 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
725TEST_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_, &center);
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_, &center);
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_, &center);
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