Commit 0fe0c2f7 authored by Kent Tamura's avatar Kent Tamura Committed by Commit Bot

TextControl NG: Support LayoutNG in TextControlElement::ValueWithHardLineBreaks()

The function iterates over RootInlineBoxes, which are not used in
LayoutNG. This CL adds LayoutNG FragmentItem support to
ValueWithHardLineBreaks().

The new code in ValueWithHardLineBreaks() is almost equivalent to the
existing NodeTraversal loop, and this CL introduces GetNextSoftBreak()
function for FragmentItem.

This CL fixes textarea-wrap-submission-value.html test with
LayoutNGTextArea flag, but this doesn't change any user-visible
behavior yet.

Bug: 1040826
Change-Id: Ice5e3954df7cd0f8ccb4f1e94d9057be5cbcc3cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454893Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815074}
parent 7ca911f1
......@@ -5,11 +5,29 @@
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
TEST(HTMLTextAreaElementTest, SanitizeUserInputValue) {
class HTMLTextAreaElementTest : public testing::WithParamInterface<bool>,
private ScopedLayoutNGTextAreaForTest,
public RenderingTest {
public:
HTMLTextAreaElementTest() : ScopedLayoutNGTextAreaForTest(GetParam()) {}
protected:
HTMLTextAreaElement& TestElement() {
Element* element = GetDocument().getElementById("test");
DCHECK(element);
return To<HTMLTextAreaElement>(*element);
}
};
INSTANTIATE_TEST_SUITE_P(All, HTMLTextAreaElementTest, testing::Bool());
TEST_P(HTMLTextAreaElementTest, SanitizeUserInputValue) {
UChar kLeadSurrogate = 0xD800;
EXPECT_EQ("", HTMLTextAreaElement::SanitizeUserInputValue("", 0));
EXPECT_EQ("", HTMLTextAreaElement::SanitizeUserInputValue("a", 0));
......@@ -33,4 +51,64 @@ TEST(HTMLTextAreaElementTest, SanitizeUserInputValue) {
HTMLTextAreaElement::SanitizeUserInputValue("a\r\ncdef", 4));
}
TEST_P(HTMLTextAreaElementTest, ValueWithHardLineBreaks) {
LoadAhem();
// The textarea can contain four letters in each of lines.
SetBodyContent(R"HTML(
<textarea id=test wrap=hard
style="font:10px Ahem; width:40px; height:200px;"></textarea>
)HTML");
HTMLTextAreaElement& textarea = TestElement();
RunDocumentLifecycle();
EXPECT_TRUE(textarea.ValueWithHardLineBreaks().IsEmpty());
textarea.setValue("12345678");
RunDocumentLifecycle();
EXPECT_EQ("1234\n5678", textarea.ValueWithHardLineBreaks());
textarea.setValue("1234567890\n");
RunDocumentLifecycle();
EXPECT_EQ("1234\n5678\n90\n", textarea.ValueWithHardLineBreaks());
Document& doc = GetDocument();
auto* inner_editor = textarea.InnerEditorElement();
inner_editor->setTextContent("");
// We set the value same as the previous one, but the value consists of four
// Text nodes.
inner_editor->appendChild(Text::Create(doc, "12"));
inner_editor->appendChild(Text::Create(doc, "34"));
inner_editor->appendChild(Text::Create(doc, "5678"));
inner_editor->appendChild(Text::Create(doc, "90"));
inner_editor->appendChild(doc.CreateRawElement(html_names::kBrTag));
RunDocumentLifecycle();
// Should be "1234\n5678\n90". The legacy behavior is wrong.
EXPECT_EQ(GetParam() ? "1234\n5678\n90" : "1234567890",
textarea.ValueWithHardLineBreaks());
}
TEST_P(HTMLTextAreaElementTest, ValueWithHardLineBreaksRtl) {
LoadAhem();
SetBodyContent(R"HTML(
<textarea id=test wrap=hard style="font:10px Ahem; width:160px;"></textarea>
)HTML");
HTMLTextAreaElement& textarea = TestElement();
#define LTO "\xE2\x80\xAD"
#define RTO "\xE2\x80\xAE"
textarea.setValue(
String::FromUTF8(RTO "Hebrew" LTO " English " RTO "Arabic" LTO));
// This textarea is rendered as:
// -----------------
// | EnglishwerbeH |
// |cibarA |
// ----------------
RunDocumentLifecycle();
EXPECT_EQ(String::FromUTF8(RTO "Hebrew" LTO " English \n" RTO "Arabic" LTO),
textarea.ValueWithHardLineBreaks());
#undef LTO
#undef RTO
}
} // namespace blink
......@@ -55,6 +55,9 @@
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
......@@ -66,6 +69,26 @@
namespace blink {
namespace {
Position GetNextSoftBreak(const NGOffsetMapping& mapping,
NGInlineCursor& cursor) {
while (cursor) {
DCHECK(cursor.Current().IsLineBox()) << cursor.Current();
const auto* break_token = cursor.Current().InlineBreakToken();
DCHECK(break_token);
cursor.MoveToNextLine();
// We don't need to emit a LF for the last line.
if (!cursor)
return Position();
if (!break_token->IsForcedBreak())
return mapping.GetFirstPosition(break_token->TextOffset());
}
return Position();
}
} // namespace
TextControlElement::TextControlElement(const QualifiedName& tag_name,
Document& doc)
: HTMLFormControlElementWithState(tag_name, doc),
......@@ -916,6 +939,44 @@ String TextControlElement::ValueWithHardLineBreaks() const {
if (!layout_object)
return value();
if (layout_object->IsLayoutNGObject()) {
const auto* items = layout_object->FragmentItems();
if (!items)
return value();
const auto* mapping = NGInlineNode::GetOffsetMapping(layout_object);
if (!mapping)
return value();
NGInlineCursor cursor(*items);
Position break_position = GetNextSoftBreak(*mapping, cursor);
StringBuilder result;
for (Node& node : NodeTraversal::DescendantsOf(*inner_text)) {
if (IsA<HTMLBRElement>(node)) {
DCHECK_EQ(&node, inner_text->lastChild());
if (&node != inner_text->lastChild())
result.Append(kNewlineCharacter);
} else if (auto* text_node = DynamicTo<Text>(node)) {
String data = text_node->data();
unsigned length = data.length();
unsigned position = 0;
while (break_position.AnchorNode() == node &&
static_cast<unsigned>(break_position.OffsetInContainerNode()) <=
length) {
unsigned break_offset = break_position.OffsetInContainerNode();
if (break_offset > position) {
result.Append(data, position, break_offset - position);
position = break_offset;
result.Append(kNewlineCharacter);
}
break_position = GetNextSoftBreak(*mapping, cursor);
}
result.Append(data, position, length - position);
}
while (break_position.AnchorNode() == node)
break_position = GetNextSoftBreak(*mapping, cursor);
}
return result.ToString();
}
Node* break_node;
unsigned break_offset;
RootInlineBox* line = layout_object->FirstRootBox();
......
......@@ -233,6 +233,8 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState {
String value_before_set_suggested_value_;
FRIEND_TEST_ALL_PREFIXES(TextControlElementTest, IndexForPosition);
FRIEND_TEST_ALL_PREFIXES(HTMLTextAreaElementTest, ValueWithHardLineBreaks);
FRIEND_TEST_ALL_PREFIXES(HTMLTextAreaElementTest, ValueWithHardLineBreaksRtl);
};
inline bool IsTextControl(const Node& node) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment