selection_controller_unittest.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2018 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. #include "ui/views/selection_controller.h"
  5. #include <memory>
  6. #include <string>
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/strings/utf_string_conversions.h"
  9. #include "base/test/task_environment.h"
  10. #include "base/time/time.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. #include "ui/events/event.h"
  13. #include "ui/events/event_constants.h"
  14. #include "ui/events/types/event_type.h"
  15. #include "ui/gfx/render_text.h"
  16. #include "ui/views/metrics.h"
  17. #include "ui/views/selection_controller_delegate.h"
  18. #include "ui/views/style/platform_style.h"
  19. namespace views {
  20. namespace {
  21. const gfx::Point CenterLeft(const gfx::Rect& rect) {
  22. return gfx::Point(rect.x(), rect.CenterPoint().y());
  23. }
  24. const gfx::Point CenterRight(const gfx::Rect& rect) {
  25. return gfx::Point(rect.right(), rect.CenterPoint().y());
  26. }
  27. class TestSelectionControllerDelegate : public SelectionControllerDelegate {
  28. public:
  29. explicit TestSelectionControllerDelegate(gfx::RenderText* render_text)
  30. : render_text_(render_text) {}
  31. TestSelectionControllerDelegate(const TestSelectionControllerDelegate&) =
  32. delete;
  33. TestSelectionControllerDelegate& operator=(
  34. const TestSelectionControllerDelegate&) = delete;
  35. ~TestSelectionControllerDelegate() override = default;
  36. gfx::RenderText* GetRenderTextForSelectionController() override {
  37. return render_text_;
  38. }
  39. bool IsReadOnly() const override { return true; }
  40. bool SupportsDrag() const override { return true; }
  41. bool HasTextBeingDragged() const override { return false; }
  42. void SetTextBeingDragged(bool value) override {}
  43. int GetViewHeight() const override {
  44. return render_text_->GetStringSize().height();
  45. }
  46. int GetViewWidth() const override {
  47. return render_text_->GetStringSize().width();
  48. }
  49. int GetDragSelectionDelay() const override { return 0; }
  50. void OnBeforePointerAction() override {}
  51. void OnAfterPointerAction(bool text_changed,
  52. bool selection_changed) override {}
  53. bool PasteSelectionClipboard() override { return false; }
  54. void UpdateSelectionClipboard() override {}
  55. private:
  56. raw_ptr<gfx::RenderText> render_text_;
  57. };
  58. class SelectionControllerTest : public ::testing::Test {
  59. public:
  60. void SetUp() override {
  61. render_text_ = gfx::RenderText::CreateRenderText();
  62. delegate_ =
  63. std::make_unique<TestSelectionControllerDelegate>(render_text_.get());
  64. controller_ = std::make_unique<SelectionController>(delegate_.get());
  65. }
  66. SelectionControllerTest()
  67. : task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {}
  68. SelectionControllerTest(const SelectionControllerTest&) = delete;
  69. SelectionControllerTest& operator=(const SelectionControllerTest&) = delete;
  70. ~SelectionControllerTest() override = default;
  71. void SetText(const std::string& text) {
  72. render_text_->SetText(base::ASCIIToUTF16(text));
  73. }
  74. std::string GetSelectedText() {
  75. return base::UTF16ToASCII(
  76. render_text_->GetTextFromRange(render_text_->selection()));
  77. }
  78. void LeftMouseDown(const gfx::Point& location, bool focused = false) {
  79. PressMouseButton(location, ui::EF_LEFT_MOUSE_BUTTON, focused);
  80. }
  81. void LeftMouseUp() { ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON); }
  82. void DragMouse(const gfx::Point& location) {
  83. mouse_location_ = location;
  84. controller_->OnMouseDragged(ui::MouseEvent(ui::ET_MOUSE_DRAGGED, location,
  85. location, last_event_time_,
  86. mouse_flags_, 0));
  87. }
  88. void RightMouseDown(const gfx::Point& location, bool focused = false) {
  89. PressMouseButton(location, ui::EF_RIGHT_MOUSE_BUTTON, focused);
  90. }
  91. void RightMouseUp() { ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON); }
  92. const gfx::Rect BoundsOfChar(int index) {
  93. return render_text_->GetSubstringBounds(gfx::Range(index, index + 1))[0];
  94. }
  95. gfx::Point TranslatePointX(const gfx::Point& point, int delta) {
  96. return point + gfx::Vector2d(delta, 0);
  97. }
  98. private:
  99. void PressMouseButton(const gfx::Point& location, int button, bool focused) {
  100. DCHECK(!(mouse_flags_ & button));
  101. mouse_flags_ |= button;
  102. mouse_location_ = location;
  103. // Ensure that mouse presses are spaced apart by at least the double-click
  104. // interval to avoid triggering a double-click.
  105. last_event_time_ += base::Milliseconds(views::GetDoubleClickInterval() + 1);
  106. controller_->OnMousePressed(
  107. ui::MouseEvent(ui::ET_MOUSE_PRESSED, location, location,
  108. last_event_time_, mouse_flags_, button),
  109. false,
  110. focused
  111. ? SelectionController::InitialFocusStateOnMousePress::kFocused
  112. : SelectionController::InitialFocusStateOnMousePress::kUnFocused);
  113. }
  114. void ReleaseMouseButton(int button) {
  115. DCHECK(mouse_flags_ & button);
  116. mouse_flags_ &= ~button;
  117. controller_->OnMouseReleased(
  118. ui::MouseEvent(ui::ET_MOUSE_RELEASED, mouse_location_, mouse_location_,
  119. last_event_time_, mouse_flags_, button));
  120. }
  121. base::test::TaskEnvironment task_environment_;
  122. std::unique_ptr<gfx::RenderText> render_text_;
  123. std::unique_ptr<TestSelectionControllerDelegate> delegate_;
  124. std::unique_ptr<SelectionController> controller_;
  125. int mouse_flags_ = 0;
  126. gfx::Point mouse_location_;
  127. base::TimeTicks last_event_time_;
  128. };
  129. TEST_F(SelectionControllerTest, ClickAndDragToSelect) {
  130. SetText("abc def");
  131. EXPECT_EQ("", GetSelectedText());
  132. LeftMouseDown(CenterLeft(BoundsOfChar(0)));
  133. DragMouse(CenterRight(BoundsOfChar(0)));
  134. EXPECT_EQ("a", GetSelectedText());
  135. DragMouse(CenterRight(BoundsOfChar(2)));
  136. EXPECT_EQ("abc", GetSelectedText());
  137. LeftMouseUp();
  138. EXPECT_EQ("abc", GetSelectedText());
  139. LeftMouseDown(CenterRight(BoundsOfChar(3)));
  140. EXPECT_EQ("", GetSelectedText());
  141. DragMouse(CenterRight(BoundsOfChar(4)));
  142. EXPECT_EQ("d", GetSelectedText());
  143. }
  144. TEST_F(SelectionControllerTest, RightClickWhenUnfocused) {
  145. SetText("abc def");
  146. RightMouseDown(CenterRight(BoundsOfChar(0)));
  147. if (PlatformStyle::kSelectAllOnRightClickWhenUnfocused)
  148. EXPECT_EQ("abc def", GetSelectedText());
  149. else
  150. EXPECT_EQ("", GetSelectedText());
  151. }
  152. TEST_F(SelectionControllerTest, RightClickSelectsWord) {
  153. SetText("abc def");
  154. RightMouseDown(CenterRight(BoundsOfChar(5)), true);
  155. if (PlatformStyle::kSelectWordOnRightClick)
  156. EXPECT_EQ("def", GetSelectedText());
  157. else
  158. EXPECT_EQ("", GetSelectedText());
  159. }
  160. // Regression test for https://crbug.com/856609
  161. TEST_F(SelectionControllerTest, RightClickPastEndDoesntSelectLastWord) {
  162. SetText("abc def");
  163. RightMouseDown(CenterRight(BoundsOfChar(6)), true);
  164. EXPECT_EQ("", GetSelectedText());
  165. }
  166. // Regression test for https://crbug.com/731252
  167. // This test validates that drags which are:
  168. // a) Above or below the text, and
  169. // b) Past one end of the text
  170. // behave properly with regard to RenderText::kDragToEndIfOutsideVerticalBounds.
  171. // When that option is true, drags outside the text that are horizontally
  172. // "towards" the text should select all of it; when that option is false, those
  173. // drags should have no effect.
  174. TEST_F(SelectionControllerTest, DragPastEndUsesProperOrigin) {
  175. SetText("abc def");
  176. gfx::Point point = BoundsOfChar(6).top_right() + gfx::Vector2d(100, -10);
  177. LeftMouseDown(point);
  178. EXPECT_EQ("", GetSelectedText());
  179. DragMouse(TranslatePointX(point, -1));
  180. if (gfx::RenderText::kDragToEndIfOutsideVerticalBounds)
  181. EXPECT_EQ("abc def", GetSelectedText());
  182. else
  183. EXPECT_EQ("", GetSelectedText());
  184. DragMouse(TranslatePointX(point, 1));
  185. EXPECT_EQ("", GetSelectedText());
  186. }
  187. } // namespace
  188. } // namespace views