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/html_form_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/frame/local_frame_view.h"
11 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
12 #include "third_party/blink/renderer/core/layout/layout_object.h"
13 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
14 #include "third_party/blink/renderer/core/page/validation_message_client.h"
15 #include "third_party/blink/renderer/core/testing/page_test_base.h"
16
17 namespace blink {
18
19 namespace {
20 class MockFormValidationMessageClient
21 : public GarbageCollected<MockFormValidationMessageClient>,
22 public ValidationMessageClient {
23 public:
ShowValidationMessage(const Element & anchor,const String &,TextDirection,const String &,TextDirection)24 void ShowValidationMessage(const Element& anchor,
25 const String&,
26 TextDirection,
27 const String&,
28 TextDirection) override {
29 anchor_ = anchor;
30 ++operation_count_;
31 }
32
HideValidationMessage(const Element & anchor)33 void HideValidationMessage(const Element& anchor) override {
34 if (anchor_ == &anchor)
35 anchor_ = nullptr;
36 ++operation_count_;
37 }
38
IsValidationMessageVisible(const Element & anchor)39 bool IsValidationMessageVisible(const Element& anchor) override {
40 return anchor_ == &anchor;
41 }
42
DocumentDetached(const Document &)43 void DocumentDetached(const Document&) override {}
DidChangeFocusTo(const Element *)44 void DidChangeFocusTo(const Element*) override {}
WillBeDestroyed()45 void WillBeDestroyed() override {}
Trace(Visitor * visitor) const46 void Trace(Visitor* visitor) const override {
47 visitor->Trace(anchor_);
48 ValidationMessageClient::Trace(visitor);
49 }
50
51 // The number of calls of ShowValidationMessage() and HideValidationMessage().
OperationCount() const52 int OperationCount() const { return operation_count_; }
53
54 private:
55 Member<const Element> anchor_;
56 int operation_count_ = 0;
57 };
58 } // namespace
59
60 class HTMLFormControlElementTest : public PageTestBase {
61 protected:
62 void SetUp() override;
63 };
64
SetUp()65 void HTMLFormControlElementTest::SetUp() {
66 PageTestBase::SetUp();
67 GetDocument().SetMimeType("text/html");
68 }
69
TEST_F(HTMLFormControlElementTest,customValidationMessageTextDirection)70 TEST_F(HTMLFormControlElementTest, customValidationMessageTextDirection) {
71 SetHtmlInnerHTML("<body><input pattern='abc' value='def' id=input></body>");
72
73 auto* input = To<HTMLInputElement>(GetElementById("input"));
74 input->setCustomValidity(
75 String::FromUTF8("\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89"));
76 input->setAttribute(
77 html_names::kTitleAttr,
78 AtomicString::FromUTF8("\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89"));
79
80 String message = input->validationMessage().StripWhiteSpace();
81 String sub_message = input->ValidationSubMessage().StripWhiteSpace();
82 TextDirection message_dir = TextDirection::kRtl;
83 TextDirection sub_message_dir = TextDirection::kLtr;
84
85 input->FindCustomValidationMessageTextDirection(message, message_dir,
86 sub_message, sub_message_dir);
87 EXPECT_EQ(TextDirection::kRtl, message_dir);
88 EXPECT_EQ(TextDirection::kLtr, sub_message_dir);
89
90 scoped_refptr<ComputedStyle> rtl_style =
91 ComputedStyle::Clone(input->GetLayoutObject()->StyleRef());
92 rtl_style->SetDirection(TextDirection::kRtl);
93 input->GetLayoutObject()->SetStyle(std::move(rtl_style));
94 input->FindCustomValidationMessageTextDirection(message, message_dir,
95 sub_message, sub_message_dir);
96 EXPECT_EQ(TextDirection::kRtl, message_dir);
97 EXPECT_EQ(TextDirection::kLtr, sub_message_dir);
98
99 input->setCustomValidity(String::FromUTF8("Main message."));
100 message = input->validationMessage().StripWhiteSpace();
101 sub_message = input->ValidationSubMessage().StripWhiteSpace();
102 input->FindCustomValidationMessageTextDirection(message, message_dir,
103 sub_message, sub_message_dir);
104 EXPECT_EQ(TextDirection::kLtr, message_dir);
105 EXPECT_EQ(TextDirection::kLtr, sub_message_dir);
106
107 input->setCustomValidity(String());
108 message = input->validationMessage().StripWhiteSpace();
109 sub_message = input->ValidationSubMessage().StripWhiteSpace();
110 input->FindCustomValidationMessageTextDirection(message, message_dir,
111 sub_message, sub_message_dir);
112 EXPECT_EQ(TextDirection::kLtr, message_dir);
113 EXPECT_EQ(TextDirection::kRtl, sub_message_dir);
114 }
115
TEST_F(HTMLFormControlElementTest,UpdateValidationMessageSkippedIfPrinting)116 TEST_F(HTMLFormControlElementTest, UpdateValidationMessageSkippedIfPrinting) {
117 SetHtmlInnerHTML("<body><input required id=input></body>");
118 ValidationMessageClient* validation_message_client =
119 MakeGarbageCollected<MockFormValidationMessageClient>();
120 GetPage().SetValidationMessageClientForTesting(validation_message_client);
121 Page::OrdinaryPages().insert(&GetPage());
122
123 auto* input = To<HTMLInputElement>(GetElementById("input"));
124 ScopedPagePauser pauser; // print() pauses the page.
125 input->reportValidity();
126 EXPECT_FALSE(validation_message_client->IsValidationMessageVisible(*input));
127 }
128
TEST_F(HTMLFormControlElementTest,DoNotUpdateLayoutDuringDOMMutation)129 TEST_F(HTMLFormControlElementTest, DoNotUpdateLayoutDuringDOMMutation) {
130 // The real ValidationMessageClient has UpdateStyleAndLayout*() in
131 // ShowValidationMessage(). So calling it during DOM mutation is
132 // dangerous. This test ensures ShowValidationMessage() is NOT called in
133 // appendChild(). crbug.com/756408
134 GetDocument().documentElement()->setInnerHTML("<select></select>");
135 auto* const select =
136 To<HTMLFormControlElement>(GetDocument().QuerySelector("select"));
137 auto* const optgroup =
138 GetDocument().CreateRawElement(html_names::kOptgroupTag);
139 auto* validation_client =
140 MakeGarbageCollected<MockFormValidationMessageClient>();
141 GetDocument().GetPage()->SetValidationMessageClientForTesting(
142 validation_client);
143
144 select->setCustomValidity("foobar");
145 select->reportValidity();
146 int start_operation_count = validation_client->OperationCount();
147 select->appendChild(optgroup);
148 EXPECT_EQ(start_operation_count, validation_client->OperationCount())
149 << "DOM mutation should not handle validation message UI in it.";
150 }
151
TEST_F(HTMLFormControlElementTest,UniqueRendererFormControlId)152 TEST_F(HTMLFormControlElementTest, UniqueRendererFormControlId) {
153 SetHtmlInnerHTML("<body><input id=input1><input id=input2></body>");
154 auto* form_control1 = To<HTMLFormControlElement>(GetElementById("input1"));
155 unsigned first_id = form_control1->UniqueRendererFormControlId();
156 auto* form_control2 = To<HTMLFormControlElement>(GetElementById("input2"));
157 EXPECT_EQ(first_id + 1, form_control2->UniqueRendererFormControlId());
158 SetHtmlInnerHTML("<body><select id=select1></body>");
159 auto* form_control3 = To<HTMLFormControlElement>(GetElementById("select1"));
160 EXPECT_EQ(first_id + 2, form_control3->UniqueRendererFormControlId());
161 }
162
163 } // namespace blink
164