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