1 // Copyright 2014 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 "third_party/blink/renderer/core/html/forms/text_control_element.h"
6 
7 #include <memory>
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/core/dom/document.h"
10 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
11 #include "third_party/blink/renderer/core/dom/text.h"
12 #include "third_party/blink/renderer/core/editing/frame_selection.h"
13 #include "third_party/blink/renderer/core/editing/position.h"
14 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
15 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
16 #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
17 #include "third_party/blink/renderer/core/loader/empty_clients.h"
18 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
19 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
20 
21 namespace blink {
22 
23 class TextControlElementTest : public testing::Test {
24  protected:
25   void SetUp() override;
26 
Page() const27   DummyPageHolder& Page() const { return *dummy_page_holder_; }
GetDocument() const28   Document& GetDocument() const { return *document_; }
TextControl() const29   TextControlElement& TextControl() const { return *text_control_; }
Input() const30   HTMLInputElement& Input() const { return *input_; }
31 
UpdateAllLifecyclePhases()32   void UpdateAllLifecyclePhases() {
33     GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
34   }
35 
36  private:
37   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
38 
39   Persistent<Document> document_;
40   Persistent<TextControlElement> text_control_;
41   Persistent<HTMLInputElement> input_;
42 };
43 
SetUp()44 void TextControlElementTest::SetUp() {
45   Page::PageClients page_clients;
46   FillWithEmptyClients(page_clients);
47   dummy_page_holder_ =
48       std::make_unique<DummyPageHolder>(IntSize(800, 600), &page_clients);
49 
50   document_ = &dummy_page_holder_->GetDocument();
51   document_->documentElement()->setInnerHTML(
52       "<body><textarea id=textarea></textarea><input id=input /></body>");
53   UpdateAllLifecyclePhases();
54   text_control_ = ToTextControl(document_->getElementById("textarea"));
55   text_control_->focus();
56   input_ = To<HTMLInputElement>(document_->getElementById("input"));
57 }
58 
TEST_F(TextControlElementTest,SetSelectionRange)59 TEST_F(TextControlElementTest, SetSelectionRange) {
60   EXPECT_EQ(0u, TextControl().selectionStart());
61   EXPECT_EQ(0u, TextControl().selectionEnd());
62 
63   TextControl().SetInnerEditorValue("Hello, text form.");
64   EXPECT_EQ(0u, TextControl().selectionStart());
65   EXPECT_EQ(0u, TextControl().selectionEnd());
66 
67   TextControl().SetSelectionRange(1, 3);
68   EXPECT_EQ(1u, TextControl().selectionStart());
69   EXPECT_EQ(3u, TextControl().selectionEnd());
70 }
71 
TEST_F(TextControlElementTest,SetSelectionRangeDoesNotCauseLayout)72 TEST_F(TextControlElementTest, SetSelectionRangeDoesNotCauseLayout) {
73   Input().focus();
74   Input().setValue("Hello, input form.");
75   Input().SetSelectionRange(1, 1);
76 
77   // Force layout if document().updateStyleAndLayoutIgnorePendingStylesheets()
78   // is called.
79   GetDocument().body()->AppendChild(GetDocument().createTextNode("foo"));
80   unsigned start_layout_count = Page().GetFrameView().LayoutCountForTesting();
81   EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
82   Input().SetSelectionRange(2, 2);
83   EXPECT_EQ(start_layout_count, Page().GetFrameView().LayoutCountForTesting());
84 }
85 
TEST_F(TextControlElementTest,IndexForPosition)86 TEST_F(TextControlElementTest, IndexForPosition) {
87   Input().setValue("Hello");
88   HTMLElement* inner_editor = Input().InnerEditorElement();
89   EXPECT_EQ(5u, TextControlElement::IndexForPosition(
90                     inner_editor,
91                     Position(inner_editor, PositionAnchorType::kAfterAnchor)));
92 }
93 
TEST_F(TextControlElementTest,ReadOnlyAttributeChangeEditability)94 TEST_F(TextControlElementTest, ReadOnlyAttributeChangeEditability) {
95   Input().setAttribute(html_names::kStyleAttr, "all:initial");
96   Input().setAttribute(html_names::kReadonlyAttr, "");
97   UpdateAllLifecyclePhases();
98   EXPECT_EQ(EUserModify::kReadOnly,
99             Input().InnerEditorElement()->GetComputedStyle()->UserModify());
100 
101   Input().removeAttribute(html_names::kReadonlyAttr);
102   UpdateAllLifecyclePhases();
103   EXPECT_EQ(EUserModify::kReadWritePlaintextOnly,
104             Input().InnerEditorElement()->GetComputedStyle()->UserModify());
105 }
106 
TEST_F(TextControlElementTest,DisabledAttributeChangeEditability)107 TEST_F(TextControlElementTest, DisabledAttributeChangeEditability) {
108   Input().setAttribute(html_names::kStyleAttr, "all:initial");
109   Input().setAttribute(html_names::kDisabledAttr, "");
110   UpdateAllLifecyclePhases();
111   EXPECT_EQ(EUserModify::kReadOnly,
112             Input().InnerEditorElement()->GetComputedStyle()->UserModify());
113 
114   Input().removeAttribute(html_names::kDisabledAttr);
115   UpdateAllLifecyclePhases();
116   EXPECT_EQ(EUserModify::kReadWritePlaintextOnly,
117             Input().InnerEditorElement()->GetComputedStyle()->UserModify());
118 }
119 
120 }  // namespace blink
121