Commit 0d249616 authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

Fix encoding of the string returned from Node::ToString().

https://chromium-review.googlesource.com/c/chromium/src/+/920776 caused
regressions. Strings dumped from LayoutObject::DumpLayoutObject() used
to escape newlines, non-ASCII characters and other special characters,
but not after this CL. The code depended on a special std::ostream&
operator<<(std::ostream& out, const String& string) overload, which took
care of this string conversion. Since we no longer use std::ostream,
introduce add String::EncodeForDebugging() and have Node::ToString()
(and the aforementioned operator<< overload) use that.

Added a unit test so that we don't accidentally break this again.

Change-Id: I62172bf1ebd12da1fdb71f78768e17aa937ea5cb
Reviewed-on: https://chromium-review.googlesource.com/925271Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Reviewed-by: default avatarXiaocheng Hu <xiaochengh@chromium.org>
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#537719}
parent 8c0fea48
......@@ -1700,9 +1700,8 @@ static void DumpAttributeDesc(const Node& node,
return;
builder.Append(' ');
builder.Append(name.ToString());
builder.Append("=\"");
builder.Append(value);
builder.Append("\"");
builder.Append("=");
builder.Append(String(value).EncodeForDebugging());
}
std::ostream& operator<<(std::ostream& ostream, const Node& node) {
......@@ -1732,9 +1731,8 @@ String Node::ToString() const {
StringBuilder builder;
builder.Append(nodeName());
if (IsTextNode()) {
builder.Append(" \"");
builder.Append(nodeValue());
builder.Append("\"");
builder.Append(" ");
builder.Append(nodeValue().EncodeForDebugging());
return builder.ToString();
}
DumpAttributeDesc(*this, HTMLNames::idAttr, builder);
......
......@@ -675,4 +675,34 @@ TEST_F(LayoutObjectTest, DisplayContentsWrapperInTableCell) {
EXPECT_EQ(cell->GetLayoutObject(), none->GetLayoutObject()->Parent());
}
TEST_F(LayoutObjectTest, DumpLayoutObject) {
// Test dumping for debugging, in particular that newlines and non-ASCII
// characters are escaped as expected.
SetBodyInnerHTML(String::FromUTF8(R"HTML(
<div id='block' style='background:
lime'>
testing Среќен роденден
</div>
)HTML"));
LayoutObject* block = GetLayoutObjectByElementId("block");
ASSERT_TRUE(block);
LayoutObject* text = block->SlowFirstChild();
ASSERT_TRUE(text);
StringBuilder result;
block->DumpLayoutObject(result, false, 0);
EXPECT_EQ(
result.ToString(),
String("LayoutBlockFlow\tDIV id=\"block\" style=\"background:\\nlime\""));
result.Clear();
text->DumpLayoutObject(result, false, 0);
EXPECT_EQ(
result.ToString(),
String("LayoutText\t#text \"\\n testing "
"\\u0421\\u0440\\u0435\\u045C\\u0435\\u043D "
"\\u0440\\u043E\\u0434\\u0435\\u043D\\u0434\\u0435\\u043D\\n\""));
}
} // namespace blink
......@@ -374,6 +374,52 @@ String String::Format(const char* format, ...) {
return String(reinterpret_cast<const LChar*>(buffer.data()), length);
}
String String::EncodeForDebugging() const {
if (IsNull())
return "<null>";
String str;
str.append('"');
for (unsigned index = 0; index < length(); ++index) {
// Print shorthands for select cases.
UChar character = (*impl_)[index];
switch (character) {
case '\t':
str.append("\\t");
break;
case '\n':
str.append("\\n");
break;
case '\r':
str.append("\\r");
break;
case '"':
str.append("\\\"");
break;
case '\\':
str.append("\\\\");
break;
default:
if (IsASCIIPrintable(character)) {
str.append(static_cast<char>(character));
} else {
// Print "\uXXXX" for control or non-ASCII characters.
str.append("\\u");
std::stringstream out;
out.width(4);
out.fill('0');
out.setf(std::ios_base::hex, std::ios_base::basefield);
out.setf(std::ios::uppercase);
out << character;
str.append(out.str().c_str());
}
break;
}
}
str.append('"');
return str;
}
template <typename IntegerType>
static String IntegerToString(IntegerType input) {
IntegerToStringConverter<IntegerType> converter(input);
......@@ -774,45 +820,7 @@ String String::FromUTF8WithLatin1Fallback(const LChar* string, size_t size) {
}
std::ostream& operator<<(std::ostream& out, const String& string) {
if (string.IsNull())
return out << "<null>";
out << '"';
for (unsigned index = 0; index < string.length(); ++index) {
// Print shorthands for select cases.
UChar character = string[index];
switch (character) {
case '\t':
out << "\\t";
break;
case '\n':
out << "\\n";
break;
case '\r':
out << "\\r";
break;
case '"':
out << "\\\"";
break;
case '\\':
out << "\\\\";
break;
default:
if (IsASCIIPrintable(character)) {
out << static_cast<char>(character);
} else {
// Print "\uXXXX" for control or non-ASCII characters.
out << "\\u";
out.width(4);
out.fill('0');
out.setf(std::ios_base::hex, std::ios_base::basefield);
out.setf(std::ios::uppercase);
out << character;
}
break;
}
}
return out << '"';
return out << string.EncodeForDebugging().Utf8().data();
}
#ifndef NDEBUG
......
......@@ -335,6 +335,10 @@ class WTF_EXPORT String {
// This function supports Latin-1 characters only.
PRINTF_FORMAT(1, 2) static String Format(const char* format, ...);
// Returns a version suitable for gtest and base/logging.*. It prepends and
// appends double-quotes, and escapes chracters other than ASCII printables.
String EncodeForDebugging() const;
// Returns an uninitialized string. The characters needs to be written
// into the buffer returned in data before the returned string is used.
// Failure to do this will have unpredictable results.
......
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