chromium/ui/views/controls/textfield/textfield_unittest.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/textfield/textfield_unittest.h"

#include <stddef.h>
#include <stdint.h>

#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/format_macros.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/pickle.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/clipboard/test/test_clipboard.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/ime/constants.h"
#include "ui/base/ime/ime_key_event_dispatcher.h"
#include "ui/base/ime/init/input_method_factory.h"
#include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/base/ui_base_switches_util.h"
#include "ui/events/event.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/test/keyboard_layout.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/render_text_test_api.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/touch_selection/touch_selection_metrics.h"
#include "ui/views/accessibility/atomic_view_ax_tree_manager.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
#include "ui/views/border.h"
#include "ui/views/controls/textfield/textfield_model.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/views_features.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_utils.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_WIN)
#include "base/win/windows_version.h"
#endif

#if BUILDFLAG(IS_LINUX)
#include "ui/linux/fake_linux_ui.h"
#include "ui/linux/linux_ui.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ui/aura/window.h"
#include "ui/wm/core/ime_util_chromeos.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "ui/base/cocoa/secure_password_input.h"
#include "ui/base/cocoa/text_services_context_menu.h"
#endif

#if BUILDFLAG(IS_OZONE)
#include "ui/events/ozone/layout/keyboard_layout_engine_test_utils.h"
#endif

namespace views::test {

const char16_t kHebrewLetterSamekh =;

// Convenience to make constructing a GestureEvent simpler.
ui::GestureEvent
CreateTestGestureEvent(int x, int y, const ui::GestureEventDetails& details) {}

// This controller will happily destroy the target field passed on
// construction when a key event is triggered.
class TextfieldDestroyerController : public TextfieldController {};

// Class that focuses a textfield when it sees a KeyDown event.
class TextfieldFocuser : public View {};

BEGIN_METADATA()

class MockInputMethod : public ui::InputMethodBase {};

MockInputMethod::MockInputMethod() :{}

MockInputMethod::~MockInputMethod() = default;

ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) {}

void MockInputMethod::OnTextInputTypeChanged(ui::TextInputClient* client) {}

void MockInputMethod::CancelComposition(const ui::TextInputClient* client) {}

bool MockInputMethod::IsCandidatePopupOpen() const {}

void MockInputMethod::OnWillChangeFocusedClient(
    ui::TextInputClient* focused_before,
    ui::TextInputClient* focused) {}

void MockInputMethod::Clear() {}

void MockInputMethod::SetCompositionTextForNextKey(
    const ui::CompositionText& composition) {}

void MockInputMethod::SetResultTextForNextKey(const std::u16string& result) {}

void MockInputMethod::ClearStates() {}

bool MockInputMethod::HasComposition() {}

void MockInputMethod::ClearComposition() {}

// A Textfield wrapper to intercept OnKey[Pressed|Released]() results.
class TestTextfield : public views::Textfield {};

BEGIN_METADATA()

TextfieldTest::TextfieldTest() {}

TextfieldTest::~TextfieldTest() = default;

void TextfieldTest::SetUp() {}

void TextfieldTest::TearDown() {}

ui::ClipboardBuffer TextfieldTest::GetAndResetCopiedToClipboard() {}

std::u16string TextfieldTest::GetClipboardText(
    ui::ClipboardBuffer clipboard_buffer) {}

void TextfieldTest::SetClipboardText(ui::ClipboardBuffer clipboard_buffer,
                                     const std::u16string& text) {}

void TextfieldTest::ContentsChanged(Textfield* sender,
                                    const std::u16string& new_contents) {}

void TextfieldTest::OnBeforeUserAction(Textfield* sender) {}

void TextfieldTest::OnAfterUserAction(Textfield* sender) {}

void TextfieldTest::OnAfterCutOrCopy(ui::ClipboardBuffer clipboard_type) {}

void TextfieldTest::InitTextfield(int count) {}

void TextfieldTest::PrepareTextfieldsInternal(int count,
                                              Textfield* textfield,
                                              View* container,
                                              gfx::Rect bounds) {}

ui::MenuModel* TextfieldTest::GetContextMenuModel() {}

bool TextfieldTest::TestingNativeMac() const {}

bool TextfieldTest::TestingNativeCrOs() const {}

void TextfieldTest::SendKeyPress(ui::KeyboardCode key_code, int flags) {}

void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code,
                                 bool alt,
                                 bool shift,
                                 bool control_or_command,
                                 bool caps_lock) {}

void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code,
                                 bool shift,
                                 bool control_or_command) {}

void TextfieldTest::SendKeyEvent(ui::KeyboardCode key_code) {}

void TextfieldTest::SendKeyEvent(char16_t ch) {}

void TextfieldTest::SendKeyEvent(char16_t ch, int flags) {}

void TextfieldTest::SendKeyEvent(char16_t ch, int flags, bool from_vk) {}

void TextfieldTest::DispatchMockInputMethodKeyEvent() {}

// Sends a platform-specific move (and select) to the logical start of line.
// Eg. this should move (and select) to the right end of line for RTL text.
void TextfieldTest::SendHomeEvent(bool shift) {}

// Sends a platform-specific move (and select) to the logical end of line.
void TextfieldTest::SendEndEvent(bool shift) {}

// Sends {delete, move, select} word {forward, backward}.
void TextfieldTest::SendWordEvent(ui::KeyboardCode key, bool shift) {}

// Sends Shift+Delete if supported, otherwise Cmd+X again.
void TextfieldTest::SendAlternateCut() {}

// Sends Ctrl+Insert if supported, otherwise Cmd+C again.
void TextfieldTest::SendAlternateCopy() {}

// Sends Shift+Insert if supported, otherwise Cmd+V again.
void TextfieldTest::SendAlternatePaste() {}

View* TextfieldTest::GetFocusedView() {}

int TextfieldTest::GetCursorPositionX(int cursor_pos) {}

int TextfieldTest::GetCursorYForTesting() {}

gfx::Rect TextfieldTest::GetCursorBounds() {}

// Gets the cursor bounds of |sel|.
gfx::Rect TextfieldTest::GetCursorBounds(const gfx::SelectionModel& sel) {}

gfx::Rect TextfieldTest::GetDisplayRect() {}

gfx::Rect TextfieldTest::GetCursorViewRect() {}

// Performs a mouse click on the point whose x-axis is |bound|'s x plus
// |x_offset| and y-axis is in the middle of |bound|'s vertical range.
void TextfieldTest::MouseClick(const gfx::Rect bound, int x_offset) {}

// This is to avoid double/triple click.
void TextfieldTest::NonClientMouseClick() {}

void TextfieldTest::VerifyTextfieldContextMenuContents(
    bool textfield_has_selection,
    bool can_undo,
    ui::MenuModel* menu) {}

void TextfieldTest::PressMouseButton(ui::EventFlags mouse_button_flags) {}

void TextfieldTest::ReleaseMouseButton(ui::EventFlags mouse_button_flags) {}

void TextfieldTest::PressLeftMouseButton() {}

void TextfieldTest::ReleaseLeftMouseButton() {}

void TextfieldTest::ClickLeftMouseButton() {}

void TextfieldTest::ClickRightMouseButton() {}

void TextfieldTest::DragMouseTo(const gfx::Point& where) {}

void TextfieldTest::MoveMouseTo(const gfx::Point& where) {}

// Taps on the textfield.
void TextfieldTest::TapAtCursor(ui::EventPointerType pointer_type) {}

TextfieldTestApi TextfieldTest::GetTextfieldTestApi() {}

MockInputMethod* TextfieldTest::input_method() {}

TextfieldModel* TextfieldTest::model() {}

TEST_F(TextfieldTest, ModelChangesTest) {}

TEST_F(TextfieldTest, Scroll) {}

TEST_F(TextfieldTest, ScrollUpdatesScrollXAccessibilityAttribute) {}

TEST_F(TextfieldTest,
       SetTextWithoutCaretBoundsChangeNotification_ModelEditHistory) {}

TEST_F(TextfieldTest, KeyTest) {}

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// Control key shouldn't generate a printable character on Linux.
TEST_F(TextfieldTest, KeyTestControlModifier) {}
#endif

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_KeysWithModifiersTest
#else
// TODO(crbug.com/41274325): Implement keyboard layout changing for other
//                         platforms.
#define MAYBE_KeysWithModifiersTest
#endif

TEST_F(TextfieldTest, MAYBE_KeysWithModifiersTest) {}

TEST_F(TextfieldTest, ControlAndSelectTest) {}

TEST_F(TextfieldTest, WordSelection) {}

TEST_F(TextfieldTest, LineSelection) {}

TEST_F(TextfieldTest, MoveUpDownAndModifySelection) {}

TEST_F(TextfieldTest, MovePageUpDownAndModifySelection) {}

TEST_F(TextfieldTest, MoveParagraphForwardBackwardAndModifySelection) {}

TEST_F(TextfieldTest, ModifySelectionWithMultipleSelections) {}

TEST_F(TextfieldTest, InsertionDeletionTest) {}

// Test that deletion operations behave correctly with an active selection.
TEST_F(TextfieldTest, DeletionWithSelection) {}

// Test that deletion operations behave correctly with multiple selections.
TEST_F(TextfieldTest, DeletionWithMultipleSelections) {}

// Test deletions not covered by other tests with key events.
TEST_F(TextfieldTest, DeletionWithEditCommands) {}

TEST_F(TextfieldTest, PasswordTest) {}

TEST_F(TextfieldTest, PasswordSelectWordTest) {}

// Check that text insertion works appropriately for password and read-only
// textfields.
TEST_F(TextfieldTest, TextInputType_InsertionTest) {}

TEST_F(TextfieldTest, ShouldDoLearning) {}

TEST_F(TextfieldTest, TextInputType) {}

TEST_F(TextfieldTest, OnKeyPress) {}

// Tests that default key bindings are handled even with a delegate installed.
TEST_F(TextfieldTest, OnKeyPressBinding) {}

TEST_F(TextfieldTest, CursorMovement) {}

TEST_F(TextfieldTest, CursorMovementWithMultipleSelections) {}

TEST_F(TextfieldTest, ShouldShowCursor) {}

#if BUILDFLAG(IS_MAC)
TEST_F(TextfieldTest, MacCursorAlphaTest) {
  InitTextfield();

  const int cursor_y = GetCursorYForTesting();
  MoveMouseTo(gfx::Point(GetCursorPositionX(0), cursor_y));
  ClickRightMouseButton();
  EXPECT_TRUE(textfield_->HasFocus());

  const float kOpaque = 1.0;
  EXPECT_FLOAT_EQ(kOpaque, GetTextfieldTestApi().CursorLayerOpacity());

  GetTextfieldTestApi().FlashCursor();

  const float kAlmostTransparent = 1.0 / 255.0;
  EXPECT_FLOAT_EQ(kAlmostTransparent,
                  GetTextfieldTestApi().CursorLayerOpacity());

  GetTextfieldTestApi().FlashCursor();

  EXPECT_FLOAT_EQ(kOpaque, GetTextfieldTestApi().CursorLayerOpacity());

  const float kTransparent = 0.0;
  GetTextfieldTestApi().SetCursorLayerOpacity(kTransparent);
  ASSERT_FLOAT_EQ(kTransparent, GetTextfieldTestApi().CursorLayerOpacity());

  GetTextfieldTestApi().UpdateCursorVisibility();
  EXPECT_FLOAT_EQ(kOpaque, GetTextfieldTestApi().CursorLayerOpacity());
}
#endif

TEST_F(TextfieldTest, FocusTraversalTest) {}

TEST_F(TextfieldTest, ContextMenuDisplayTest) {}

TEST_F(TextfieldTest, DoubleAndTripleClickTest) {}

// Tests text selection behavior on a right click.
TEST_F(TextfieldTest, SelectionOnRightClick) {}

TEST_F(TextfieldTest, DragToSelect) {}

// Ensures dragging above or below the textfield extends a selection to either
// end, depending on the relative x offsets of the text and mouse cursors.
TEST_F(TextfieldTest, DragUpOrDownSelectsToEnd) {}

#if BUILDFLAG(IS_WIN)
TEST_F(TextfieldTest, DragAndDrop_AcceptDrop) {
  InitTextfield();
  textfield_->SetText(u"hello world");

  ui::OSExchangeData data;
  std::u16string string(u"string ");
  data.SetString(string);
  int formats = 0;
  std::set<ui::ClipboardFormatType> format_types;

  // Ensure that disabled textfields do not accept drops.
  textfield_->SetEnabled(false);
  EXPECT_FALSE(textfield_->GetDropFormats(&formats, &format_types));
  EXPECT_EQ(0, formats);
  EXPECT_TRUE(format_types.empty());
  EXPECT_FALSE(textfield_->CanDrop(data));
  textfield_->SetEnabled(true);

  // Ensure that read-only textfields do not accept drops.
  textfield_->SetReadOnly(true);
  EXPECT_FALSE(textfield_->GetDropFormats(&formats, &format_types));
  EXPECT_EQ(0, formats);
  EXPECT_TRUE(format_types.empty());
  EXPECT_FALSE(textfield_->CanDrop(data));
  textfield_->SetReadOnly(false);

  // Ensure that enabled and editable textfields do accept drops.
  EXPECT_TRUE(textfield_->GetDropFormats(&formats, &format_types));
  EXPECT_EQ(ui::OSExchangeData::STRING, formats);
  EXPECT_TRUE(format_types.empty());
  EXPECT_TRUE(textfield_->CanDrop(data));
  gfx::PointF drop_point(GetCursorPositionX(6), 0);
  ui::DropTargetEvent drop(
      data, drop_point, drop_point,
      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE);
  EXPECT_EQ(ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE,
            textfield_->OnDragUpdated(drop));
  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
  auto cb = textfield_->GetDropCallback(drop);
  std::move(cb).Run(drop, output_drag_op, /*drag_image_layer_owner=*/nullptr);
  EXPECT_EQ(ui::mojom::DragOperation::kCopy, output_drag_op);
  EXPECT_EQ(u"hello string world", textfield_->GetText());

  // Ensure that textfields do not accept non-OSExchangeData::STRING types.
  ui::OSExchangeData bad_data;
  bad_data.SetFilename(base::FilePath(FILE_PATH_LITERAL("x")));
  ui::ClipboardFormatType fmt = ui::ClipboardFormatType::BitmapType();
  bad_data.SetPickledData(fmt, base::Pickle());
  bad_data.SetFileContents(base::FilePath(L"x"), "x");
  bad_data.SetHtml(std::u16string(u"x"), GURL("x.org"));
  ui::DownloadFileInfo download(base::FilePath(), nullptr);
  bad_data.provider().SetDownloadFileInfo(&download);
  EXPECT_FALSE(textfield_->CanDrop(bad_data));
}
#endif

TEST_F(TextfieldTest, DragAndDrop_InitiateDrag) {}

TEST_F(TextfieldTest, DragAndDrop_ToTheRight) {}

TEST_F(TextfieldTest, DragAndDrop_ToTheLeft) {}

TEST_F(TextfieldTest, DropCallbackCancelled) {}

TEST_F(TextfieldTest, DragAndDrop_Canceled) {}

TEST_F(TextfieldTest, ReadOnlyTest) {}

TEST_F(TextfieldTest, TextInputClientTest) {}

TEST_F(TextfieldTest, UndoRedoTest) {}

// Most platforms support Ctrl+Y as an alternative to Ctrl+Shift+Z, but on Mac
// Ctrl+Y is bound to "Yank" and Cmd+Y is bound to "Show full history". So, on
// Mac, Cmd+Shift+Z is sent for the tests above and the Ctrl+Y test below is
// skipped.
#if !BUILDFLAG(IS_MAC)

// Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z.
TEST_F(TextfieldTest, RedoWithCtrlY) {}

#endif  // !BUILDFLAG(IS_MAC)

// Non-Mac platforms don't have a key binding for Yank. Since this test is only
// run on Mac, it uses some Mac specific key bindings.
#if BUILDFLAG(IS_MAC)

TEST_F(TextfieldTest, Yank) {
  InitTextfield(2);
  textfield_->SetText(u"abcdef");
  textfield_->SetSelectedRange(gfx::Range(2, 4));

  // Press Ctrl+Y to yank.
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);

  // Initially the kill buffer should be empty. Hence yanking should delete the
  // selected text.
  EXPECT_EQ(u"abef", textfield_->GetText());
  EXPECT_EQ(gfx::Range(2), textfield_->GetSelectedRange());

  // Press Ctrl+K to delete to end of paragraph. This should place the deleted
  // text in the kill buffer.
  SendKeyPress(ui::VKEY_K, ui::EF_CONTROL_DOWN);

  EXPECT_EQ(u"ab", textfield_->GetText());
  EXPECT_EQ(gfx::Range(2), textfield_->GetSelectedRange());

  // Yank twice.
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);
  EXPECT_EQ(u"abefef", textfield_->GetText());
  EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange());

  // Verify pressing backspace does not modify the kill buffer.
  SendKeyEvent(ui::VKEY_BACK);
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);
  EXPECT_EQ(u"abefeef", textfield_->GetText());
  EXPECT_EQ(gfx::Range(7), textfield_->GetSelectedRange());

  // Move focus to next textfield.
  widget_->GetFocusManager()->AdvanceFocus(false);
  EXPECT_EQ(2, GetFocusedView()->GetID());
  Textfield* textfield2 = static_cast<Textfield*>(GetFocusedView());
  EXPECT_TRUE(textfield2->GetText().empty());

  // Verify yanked text persists across multiple textfields and that yanking
  // into a password textfield works.
  textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);
  EXPECT_EQ(u"ef", textfield2->GetText());
  EXPECT_EQ(gfx::Range(2), textfield2->GetSelectedRange());

  // Verify deletion in a password textfield does not modify the kill buffer.
  textfield2->SetText(u"hello");
  textfield2->SetSelectedRange(gfx::Range(0));
  SendKeyPress(ui::VKEY_K, ui::EF_CONTROL_DOWN);
  EXPECT_TRUE(textfield2->GetText().empty());

  textfield_->RequestFocus();
  textfield_->SetSelectedRange(gfx::Range(0));
  SendKeyPress(ui::VKEY_Y, ui::EF_CONTROL_DOWN);
  EXPECT_EQ(u"efabefeef", textfield_->GetText());
}

#endif  // BUILDFLAG(IS_MAC)

TEST_F(TextfieldTest, CutCopyPaste) {}

TEST_F(TextfieldTest, CutCopyPasteWithEditCommand) {}

TEST_F(TextfieldTest, SelectWordFromEmptySelection) {}

TEST_F(TextfieldTest, SelectWordFromNonEmptySelection) {}

TEST_F(TextfieldTest, SelectWordFromNonAlphaNumericFragment) {}

TEST_F(TextfieldTest, SelectWordFromWhitespaceFragment) {}

TEST_F(TextfieldTest, SelectCommands) {}

// No touch on desktop Mac.
#if !BUILDFLAG(IS_MAC)
TEST_F(TextfieldTest, SelectCommandsFromTouchEvent) {}
#endif

TEST_F(TextfieldTest, OvertypeMode) {}

TEST_F(TextfieldTest, TextCursorDisplayTest) {}

TEST_F(TextfieldTest, TextCursorDisplayInRTLTest) {}

TEST_F(TextfieldTest, TextCursorPositionInRTLTest) {}

TEST_F(TextfieldTest, TextCursorPositionInLTRTest) {}

TEST_F(TextfieldTest, HitInsideTextAreaTest) {}

TEST_F(TextfieldTest, HitOutsideTextAreaTest) {}

TEST_F(TextfieldTest, HitOutsideTextAreaInRTLTest) {}

TEST_F(TextfieldTest, OverflowTest) {}

TEST_F(TextfieldTest, OverflowInRTLTest) {}

TEST_F(TextfieldTest, PasswordProtected) {}

TEST_F(TextfieldTest, CommitComposingTextTest) {}

TEST_F(TextfieldTest, CommitEmptyComposingTextTest) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// SetCompositionFromExistingText is only available on Windows and Chrome OS.
TEST_F(TextfieldTest, SetCompositionFromExistingTextTest) {}
#endif

TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest) {}

TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) {}

// The word we select by double clicking should remain selected regardless of
// where we drag the mouse afterwards without releasing the left button.
TEST_F(TextfieldTest, KeepInitiallySelectedWord) {}

// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_F(TextfieldTest, SelectionClipboard) {}

// Verify that the selection clipboard is not updated for selections on a
// password textfield.
TEST_F(TextfieldTest, SelectionClipboard_Password) {}
#endif

// Long_Press gesture in Textfield can initiate a drag and drop now.
TEST_F(TextfieldTest, TestLongPressInitiatesDragDrop) {}

// No touch on desktop Mac.
#if !BUILDFLAG(IS_MAC)
TEST_F(TextfieldTest, ScrollToAdjustDisplayOffset) {}

TEST_F(TextfieldTest, TwoFingerScroll) {}

TEST_F(TextfieldTest, ScrollToPlaceCursor) {}

TEST_F(TextfieldTest, ScrollToPlaceCursorAdjustsDisplayOffset) {}

TEST_F(TextfieldTest, TwoFingerScrollUpdate) {}

// TODO(crbug.com/40276114): Rewrite these long press tests when EventGenerator
// can generate long press gestures.
TEST_F(TextfieldTest, LongPressSelection) {}

TEST_F(TextfieldTest, LongPressDragSelectionLTRForward) {}

TEST_F(TextfieldTest, LongPressDragSelectionLTRBackward) {}

TEST_F(TextfieldTest, LongPressDragSelectionRTLForward) {}

TEST_F(TextfieldTest, LongPressDragSelectionRTLBackward) {}

TEST_F(TextfieldTest, DoubleTapSelection) {}

TEST_F(TextfieldTest, TripleTapSelection) {}

TEST_F(TextfieldTest, DoublePressDragSelection) {}

TEST_F(TextfieldTest, TouchSelectionDraggingMetrics) {}
#endif  // !BUILDFLAG(IS_MAC)

TEST_F(TextfieldTest, GetTextfieldBaseline_FontFallbackTest) {}

// Tests that a textfield view can be destroyed from OnKeyEvent() on its
// controller and it does not crash.
TEST_F(TextfieldTest, DestroyingTextfieldFromOnKeyEvent) {}

TEST_F(TextfieldTest, CursorBlinkRestartsOnInsertOrReplace) {}

TEST_F(TextfieldTest, InitialAccessibilityProperties) {}

// Verifies setting the accessible name will call NotifyAccessibilityEvent.
TEST_F(TextfieldTest, SetAccessibleNameNotifiesAccessibilityEvent) {}

#if BUILDFLAG(IS_WIN)
TEST_F(TextfieldTest, AccessibilityAttributes) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);
  InitTextfield();

  ViewAXPlatformNodeDelegate* delegate =
      static_cast<ViewAXPlatformNodeDelegate*>(
          &textfield_->GetViewAccessibility());

  textfield_->GetViewAccessibility().EnsureAtomicViewAXTreeManager();
  textfield_->SetText(u"this is the textfield");
  textfield_->SetBounds(1, 2, 3, 4);

  ui::AXNodeData actual =
      delegate->GetAtomicViewAXTreeManagerForTesting()->GetRoot()->data();

  EXPECT_EQ(ax::mojom::Role::kTextField, actual.role);
  EXPECT_TRUE(actual.HasState(ax::mojom::State::kEditable) &&
              actual.HasState(ax::mojom::State::kFocusable));
  EXPECT_EQ(textfield_->GetAccessibleName(),
            actual.GetString16Attribute(ax::mojom::StringAttribute::kName));
  EXPECT_EQ(textfield_->GetText(),
            actual.GetString16Attribute(ax::mojom::StringAttribute::kValue));
  EXPECT_EQ(
      textfield_->GetPlaceholderText(),
      actual.GetString16Attribute(ax::mojom::StringAttribute::kPlaceholder));

  EXPECT_EQ(static_cast<const int>(textfield_->GetSelectedRange().start()),
            actual.GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart));
  EXPECT_EQ(static_cast<const int>(textfield_->GetSelectedRange().end()),
            actual.GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd));

  EXPECT_EQ(gfx::Rect(1, 2, 3, 4),
            gfx::ToEnclosingRect(actual.relative_bounds.bounds));

  EXPECT_EQ(textfield_->GetBoundsInScreen(),
            delegate->GetBoundsRect(ui::AXCoordinateSystem::kScreenDIPs,
                                    ui::AXClippingBehavior::kUnclipped, nullptr));
}
#endif

TEST_F(TextfieldTest, AccessiblePlaceholderTest) {}

TEST_F(TextfieldTest, AccessibleTextSelectBound) {}

TEST_F(TextfieldTest, AccessibleNameFromLabel) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Check that when accessibility virtual keyboard is enabled, windows are
// shifted up when focused and restored when focus is lost.
TEST_F(TextfieldTest, VirtualKeyboardFocusEnsureCaretNotInRect) {
  InitTextfield();

  aura::Window* root_window = GetRootWindow(widget_.get());
  int keyboard_height = 200;
  gfx::Rect root_bounds = root_window->bounds();
  gfx::Rect orig_widget_bounds = gfx::Rect(0, 300, 400, 200);
  gfx::Rect shifted_widget_bounds = gfx::Rect(0, 200, 400, 200);
  gfx::Rect keyboard_view_bounds =
      gfx::Rect(0, root_bounds.height() - keyboard_height, root_bounds.width(),
                keyboard_height);

  // Focus the window.
  widget_->SetBounds(orig_widget_bounds);
  input_method()->SetFocusedTextInputClient(textfield_);
  EXPECT_EQ(widget_->GetNativeView()->bounds(), orig_widget_bounds);

  // Simulate virtual keyboard.
  input_method()->SetVirtualKeyboardBounds(keyboard_view_bounds);

  // Window should be shifted.
  EXPECT_EQ(widget_->GetNativeView()->bounds(), shifted_widget_bounds);

  // Detach the textfield from the IME
  input_method()->DetachTextInputClient(textfield_);
  wm::RestoreWindowBoundsOnClientFocusLost(
      widget_->GetNativeView()->GetToplevelWindow());

  // Window should be restored.
  EXPECT_EQ(widget_->GetNativeView()->bounds(), orig_widget_bounds);
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// No touch on desktop Mac. Tracked in http://crbug.com/445520.
#if !BUILDFLAG(IS_MAC)
TEST_F(TextfieldTest, TapActivatesTouchSelection) {}

TEST_F(TextfieldTest, ClearingFocusDeactivatesTouchSelection) {}

TEST_F(TextfieldTest, TapOnSelection) {}

// When touch drag drop is enabled, long pressing on selected text initiates
// drag-drop behaviour. So, long pressing on selected text should preserve the
// selection rather than selecting the nearest word and activating touch
// selection.
TEST_F(TextfieldTest, LongPressOnSelection) {}

TEST_F(TextfieldTest, TouchSelectionInUnfocusableTextfield) {}
#endif

TEST_F(TextfieldTest, MoveCaret) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtent) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentToTextEnd) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentByCharacter) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentExpandByWord) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentShrinkByCharacter) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentOffset) {}

TEST_F(TextfieldTest, MoveRangeSelectionExtentNonEmptySelection) {}

TEST_F(TextfieldTest, SelectBetweenCoordinates) {}

TEST_F(TextfieldTest, AccessiblePasswordTest) {}

TEST_F(TextfieldTest, AccessibleRole) {}

TEST_F(TextfieldTest, AccessibleReadOnly) {}

// Verify that cursor visibility is controlled by SetCursorEnabled.
TEST_F(TextfieldTest, CursorVisibility) {}

TEST_F(TextfieldTest, AccessibleValue) {}

// Tests that Textfield::FitToLocalBounds() sets the RenderText's display rect
// to the view's bounds, taking the border into account.
TEST_F(TextfieldTest, FitToLocalBounds) {}

// Verify that cursor view height does not exceed the textfield height.
TEST_F(TextfieldTest, CursorViewHeight) {}

// Verify that cursor view height is independent of its parent view height.
TEST_F(TextfieldTest, CursorViewHeightAtDiffDSF) {}

// Check if the text cursor is always at the end of the textfield after the
// text overflows from the textfield. If the textfield size changes, check if
// the text cursor's location is updated accordingly.
TEST_F(TextfieldTest, TextfieldBoundsChangeTest) {}

// Verify that after creating a new Textfield, the Textfield doesn't
// automatically receive focus and the text cursor is not visible.
TEST_F(TextfieldTest, TextfieldInitialization) {}

// Verify that if a textfield gains focus during key dispatch that an edit
// command only results when the event is not consumed.
TEST_F(TextfieldTest, SwitchFocusInKeyDown) {}

TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) {}

TEST_F(TextfieldTest, EmojiItem_EmptyField) {}

TEST_F(TextfieldTest, EmojiItem_ReadonlyField) {}

TEST_F(TextfieldTest, EmojiItem_FieldWithText) {}

#if BUILDFLAG(IS_MAC)
// Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed.
TEST_F(TextfieldTest, TextServicesContextMenuTextDirectionTest) {
  InitTextfield();
  EXPECT_TRUE(textfield_->context_menu_controller());

  EXPECT_TRUE(GetContextMenuModel());

  textfield_->ChangeTextDirectionAndLayoutAlignment(
      base::i18n::TextDirection::LEFT_TO_RIGHT);
  GetTextfieldTestApi().UpdateContextMenu();

  EXPECT_FALSE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionDefault));
  EXPECT_TRUE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionLtr));
  EXPECT_FALSE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionRtl));

  textfield_->ChangeTextDirectionAndLayoutAlignment(
      base::i18n::TextDirection::RIGHT_TO_LEFT);
  GetTextfieldTestApi().UpdateContextMenu();

  EXPECT_FALSE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionDefault));
  EXPECT_FALSE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionLtr));
  EXPECT_TRUE(textfield_->IsCommandIdChecked(
      ui::TextServicesContextMenu::kWritingDirectionRtl));
}

// Tests to see if the look up item is hidden for password fields.
TEST_F(TextfieldTest, LookUpPassword) {
  InitTextfield();
  textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);

  const std::u16string kText = u"Willie Wagtail";

  textfield_->SetText(kText);
  textfield_->SelectAll(false);

  ui::MenuModel* context_menu = GetContextMenuModel();
  EXPECT_TRUE(context_menu);
  EXPECT_GT(context_menu->GetItemCount(), 0u);
  EXPECT_NE(context_menu->GetCommandIdAt(0), IDS_CONTENT_CONTEXT_LOOK_UP);
  EXPECT_NE(context_menu->GetLabelAt(0),
            l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kText));
}

TEST_F(TextfieldTest, SecurePasswordInput) {
  InitTextfield();
  ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());

  // Shouldn't enable secure input if it's not a password textfield.
  textfield_->OnFocus();
  EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());

  textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);

  // Single matched calls immediately update IsPasswordInputEnabled().
  textfield_->OnFocus();
  EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());

  textfield_->OnBlur();
  EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
}
#endif  // BUILDFLAG(IS_MAC)

TEST_F(TextfieldTest, AccessibilitySelectionEvents) {}

TEST_F(TextfieldTest, AccessibilitySelectionEventsOnInitialFocus) {}

TEST_F(TextfieldTest, FocusReasonMouse) {}

TEST_F(TextfieldTest, FocusReasonTouchTap) {}

TEST_F(TextfieldTest, FocusReasonPenTap) {}

TEST_F(TextfieldTest, FocusReasonMultipleEvents) {}

TEST_F(TextfieldTest, FocusReasonFocusBlurFocus) {}

TEST_F(TextfieldTest, KeyboardObserverForPenInput) {}

TEST_F(TextfieldTest, ChangeTextDirectionAndLayoutAlignmentTest) {}

TEST_F(TextfieldTest, AccessibilityTextDirection) {}

TEST_F(TextfieldTest, TextChangedCallbackTest) {}

// Tests that invalid characters like non-displayable characters are filtered
// out when inserted into the text field.
TEST_F(TextfieldTest, InsertInvalidCharsTest) {}

TEST_F(TextfieldTest, ScrollCommands) {}

TEST_F(TextfieldTest, AccessibleTextDirectionRTL) {}

TEST_F(TextfieldTest, AccessibleDefaultActionVerb) {}

#if BUILDFLAG(SUPPORTS_AX_TEXT_OFFSETS)
TEST_F(TextfieldTest, WordOffsets) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);
  InitTextfield();
  ui::AXNodeData node_data;
  textfield_->SetText(u"abc 12 34 def hij :' $*() ");
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  std::vector<int32_t> expected_starts = {0, 4, 7, 10, 14};
  std::vector<int32_t> expected_ends = {3, 6, 9, 13, 17};
  EXPECT_EQ(
      node_data.GetIntListAttribute(ax::mojom::IntListAttribute::kWordStarts),
      expected_starts);
  EXPECT_EQ(
      node_data.GetIntListAttribute(ax::mojom::IntListAttribute::kWordEnds),
      expected_ends);
}

TEST_F(TextfieldTest, AccessibleGraphemeOffsets) {
  struct TestCase {
    std::u16string text;
    std::vector<int32_t> expected_offsets;
  };

  const auto kTestCases = std::to_array<TestCase>({
      {std::u16string(), {}},
      // LTR.
      {u"asdfghkl:/", {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}},
      // RTL: should render left-to-right as "<space>43210 \n cba9876".
      // Note this used to say "Arabic language", in Arabic, but the last
      // character in the string (\u0629) got fancy in an updated Mac font, so
      // now the penultimate character repeats.
      //
      // TODO(accessibility): This is not the correct order of grapheme offsets.
      // Blink returns the offsets from the right boundary when in RTL and so
      // should we for Views.
      {u"اللغة العربيي",
       {120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, 10}},
      // LTR कि (DEVANAGARI KA with VOWEL I) (2-char grapheme), LTR abc, and LTR
      // कि.
      {u"\u0915\u093fabc\u0915\u093f", {0, 20, 30, 40, 50, 70}},
      // LTR ab, LTR कि (DEVANAGARI KA with VOWEL I) (2-char grapheme), LTR cd.
      {u"ab\u0915\u093fcd", {0, 10, 20, 40, 50, 60}},
      // LTR ab, 𝄞 'MUSICAL SYMBOL G CLEF' U+1D11E (surrogate pair), LTR cd.
      // Windows requires wide strings for \Unnnnnnnn universal character names.
      {u"ab\U0001D11Ecd", {0, 10, 20, 30, 40, 50}},
  });

  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);

  InitTextfield();

  // Set the glyph width to a fixed value to avoid flakiness and dependency on
  // each platform's default font size.
  constexpr int kGlyphWidth = 10;
  gfx::test::RenderTextTestApi(GetTextfieldTestApi().GetRenderText())
      .SetGlyphWidth(kGlyphWidth);
  GetTextfieldTestApi().GetRenderText()->SetDisplayRect(
      gfx::Rect(0, 0, 20 * kGlyphWidth, 100));

  for (size_t i = 0; i < kTestCases.size(); i++) {
    SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i));
    textfield_->SetText(kTestCases[i].text);

    ui::AXNodeData node_data;
    textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
    EXPECT_EQ(node_data.GetIntListAttribute(
                  ax::mojom::IntListAttribute::kCharacterOffsets),
              kTestCases[i].expected_offsets);
  }
}

TEST_F(TextfieldTest, AccessibleGraphemeOffsetsObscured) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);
  InitTextfield();
  textfield_->SetText(u"abcdef");

  ASSERT_FALSE(GetTextfieldTestApi().GetRenderText()->obscured());

  ui::AXNodeData node_data;
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  std::vector<int32_t> non_obscured_offsets = node_data.GetIntListAttribute(
      ax::mojom::IntListAttribute::kCharacterOffsets);

  textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  EXPECT_NE(node_data.GetIntListAttribute(
                ax::mojom::IntListAttribute::kCharacterOffsets),
            non_obscured_offsets);
}

TEST_F(TextfieldTest, AccessibleGraphemeOffsetsElidedTail) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);
  InitTextfield();

  constexpr int kGlyphWidth = 10;

  GetTextfieldTestApi().GetRenderText()->SetDisplayRect(
      gfx::Rect(0, 0, 5 * kGlyphWidth, 100));
  GetTextfieldTestApi().GetRenderText()->SetElideBehavior(gfx::ELIDE_TAIL);
  gfx::test::RenderTextTestApi(GetTextfieldTestApi().GetRenderText())
      .SetGlyphWidth(kGlyphWidth);

  textfield_->SetText(u"abcdef");

  ui::AXNodeData node_data;
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  std::vector<int32_t> expected_offsets = {0, 10, 20, 30, 40, 40, 40};
  EXPECT_EQ(node_data.GetIntListAttribute(
                ax::mojom::IntListAttribute::kCharacterOffsets),
            expected_offsets);
}

TEST_F(TextfieldTest, AccessibleGraphemeOffsetsIndependentOfDisplayOffset) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(::features::kUiaProvider);
  InitTextfield();

  // Size the textfield wide enough to hold 10 characters.
  gfx::test::RenderTextTestApi render_text_test_api(
      GetTextfieldTestApi().GetRenderText());
  constexpr int kGlyphWidth = 10;
  render_text_test_api.SetGlyphWidth(kGlyphWidth);
  GetTextfieldTestApi().GetRenderText()->SetDisplayRect(
      gfx::Rect(kGlyphWidth * 10, 20));
  textfield_->SetTextWithoutCaretBoundsChangeNotification(
      u"3.141592653589793238462", 0);
  GetTextfieldTestApi().SetDisplayOffsetX(0);

  ui::AXNodeData node_data;
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  std::vector<int32_t> expected_offsets = {
      0,   10,  20,  30,  40,  50,  60,  70,  80,  90,  100, 110,
      120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230};
  EXPECT_EQ(node_data.GetIntListAttribute(
                ax::mojom::IntListAttribute::kCharacterOffsets),
            expected_offsets);
  GetTextfieldTestApi().SetDisplayOffsetX(-100);
  EXPECT_EQ(GetTextfieldTestApi().GetDisplayOffsetX(), -100);

  ui::AXNodeData node_data_2;
  textfield_->GetViewAccessibility().GetAccessibleNodeData(&node_data_2);
  // The offsets should be the same.
  EXPECT_EQ(node_data_2.GetIntListAttribute(
                ax::mojom::IntListAttribute::kCharacterOffsets),
            expected_offsets);
}
#endif  // BUILDFLAG(SUPPORTS_AX_TEXT_OFFSETS)

}  // namespace views::test