| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h" |
| |
| #include <memory> |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/html/forms/html_input_element.h" |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/page/scoped_page_pauser.h" |
| #include "third_party/blink/renderer/core/page/validation_message_client.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| |
| namespace blink { |
| |
| namespace { |
| class MockFormValidationMessageClient |
| : public GarbageCollectedFinalized<MockFormValidationMessageClient>, |
| public ValidationMessageClient { |
| USING_GARBAGE_COLLECTED_MIXIN(MockFormValidationMessageClient); |
| |
| public: |
| void ShowValidationMessage(const Element& anchor, |
| const String&, |
| TextDirection, |
| const String&, |
| TextDirection) override { |
| anchor_ = anchor; |
| ++operation_count_; |
| } |
| |
| void HideValidationMessage(const Element& anchor) override { |
| if (anchor_ == &anchor) |
| anchor_ = nullptr; |
| ++operation_count_; |
| } |
| |
| bool IsValidationMessageVisible(const Element& anchor) override { |
| return anchor_ == &anchor; |
| } |
| |
| void DocumentDetached(const Document&) override {} |
| void WillBeDestroyed() override {} |
| void Trace(blink::Visitor* visitor) override { |
| visitor->Trace(anchor_); |
| ValidationMessageClient::Trace(visitor); |
| } |
| |
| // The number of calls of ShowValidationMessage() and HideValidationMessage(). |
| int OperationCount() const { return operation_count_; } |
| |
| private: |
| Member<const Element> anchor_; |
| int operation_count_ = 0; |
| }; |
| } // namespace |
| |
| class HTMLFormControlElementTest : public PageTestBase { |
| protected: |
| void SetUp() override; |
| }; |
| |
| void HTMLFormControlElementTest::SetUp() { |
| PageTestBase::SetUp(); |
| GetDocument().SetMimeType("text/html"); |
| } |
| |
| TEST_F(HTMLFormControlElementTest, customValidationMessageTextDirection) { |
| SetHtmlInnerHTML("<body><input pattern='abc' value='def' id=input></body>"); |
| |
| HTMLInputElement* input = ToHTMLInputElement(GetElementById("input")); |
| input->setCustomValidity( |
| String::FromUTF8("\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89")); |
| input->setAttribute( |
| html_names::kTitleAttr, |
| AtomicString::FromUTF8("\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89")); |
| |
| String message = input->validationMessage().StripWhiteSpace(); |
| String sub_message = input->ValidationSubMessage().StripWhiteSpace(); |
| TextDirection message_dir = TextDirection::kRtl; |
| TextDirection sub_message_dir = TextDirection::kLtr; |
| |
| input->FindCustomValidationMessageTextDirection(message, message_dir, |
| sub_message, sub_message_dir); |
| EXPECT_EQ(TextDirection::kRtl, message_dir); |
| EXPECT_EQ(TextDirection::kLtr, sub_message_dir); |
| |
| scoped_refptr<ComputedStyle> rtl_style = |
| ComputedStyle::Clone(input->GetLayoutObject()->StyleRef()); |
| rtl_style->SetDirection(TextDirection::kRtl); |
| input->GetLayoutObject()->SetStyle(std::move(rtl_style)); |
| input->FindCustomValidationMessageTextDirection(message, message_dir, |
| sub_message, sub_message_dir); |
| EXPECT_EQ(TextDirection::kRtl, message_dir); |
| EXPECT_EQ(TextDirection::kLtr, sub_message_dir); |
| |
| input->setCustomValidity(String::FromUTF8("Main message.")); |
| message = input->validationMessage().StripWhiteSpace(); |
| sub_message = input->ValidationSubMessage().StripWhiteSpace(); |
| input->FindCustomValidationMessageTextDirection(message, message_dir, |
| sub_message, sub_message_dir); |
| EXPECT_EQ(TextDirection::kLtr, message_dir); |
| EXPECT_EQ(TextDirection::kLtr, sub_message_dir); |
| |
| input->setCustomValidity(String()); |
| message = input->validationMessage().StripWhiteSpace(); |
| sub_message = input->ValidationSubMessage().StripWhiteSpace(); |
| input->FindCustomValidationMessageTextDirection(message, message_dir, |
| sub_message, sub_message_dir); |
| EXPECT_EQ(TextDirection::kLtr, message_dir); |
| EXPECT_EQ(TextDirection::kRtl, sub_message_dir); |
| } |
| |
| TEST_F(HTMLFormControlElementTest, UpdateValidationMessageSkippedIfPrinting) { |
| SetHtmlInnerHTML("<body><input required id=input></body>"); |
| ValidationMessageClient* validation_message_client = |
| MakeGarbageCollected<MockFormValidationMessageClient>(); |
| GetPage().SetValidationMessageClientForTesting(validation_message_client); |
| Page::OrdinaryPages().insert(&GetPage()); |
| |
| HTMLInputElement* input = ToHTMLInputElement(GetElementById("input")); |
| ScopedPagePauser pauser; // print() pauses the page. |
| input->reportValidity(); |
| EXPECT_FALSE(validation_message_client->IsValidationMessageVisible(*input)); |
| } |
| |
| TEST_F(HTMLFormControlElementTest, DoNotUpdateLayoutDuringDOMMutation) { |
| // The real ValidationMessageClient has UpdateStyleAndLayout*() in |
| // ShowValidationMessage(). So calling it during DOM mutation is |
| // dangerous. This test ensures ShowValidationMessage() is NOT called in |
| // appendChild(). crbug.com/756408 |
| GetDocument().documentElement()->SetInnerHTMLFromString("<select></select>"); |
| HTMLFormControlElement* const select = |
| ToHTMLFormControlElement(GetDocument().QuerySelector("select")); |
| auto* const optgroup = |
| GetDocument().CreateRawElement(html_names::kOptgroupTag); |
| auto* validation_client = |
| MakeGarbageCollected<MockFormValidationMessageClient>(); |
| GetDocument().GetPage()->SetValidationMessageClientForTesting( |
| validation_client); |
| |
| select->setCustomValidity("foobar"); |
| select->reportValidity(); |
| int start_operation_count = validation_client->OperationCount(); |
| select->appendChild(optgroup); |
| EXPECT_EQ(start_operation_count, validation_client->OperationCount()) |
| << "DOM mutation should not handle validation message UI in it."; |
| } |
| |
| TEST_F(HTMLFormControlElementTest, UniqueRendererFormControlId) { |
| SetHtmlInnerHTML("<body><input id=input1><input id=input2></body>"); |
| auto* form_control1 = ToHTMLFormControlElement(GetElementById("input1")); |
| unsigned first_id = form_control1->UniqueRendererFormControlId(); |
| auto* form_control2 = ToHTMLFormControlElement(GetElementById("input2")); |
| EXPECT_EQ(first_id + 1, form_control2->UniqueRendererFormControlId()); |
| SetHtmlInnerHTML("<body><select id=select1></body>"); |
| auto* form_control3 = ToHTMLFormControlElement(GetElementById("select1")); |
| EXPECT_EQ(first_id + 2, form_control3->UniqueRendererFormControlId()); |
| } |
| |
| } // namespace blink |