Bug 1808906 - Make the style editor handle `<font>` at last when there are multiple preserved styles r=m_kato

Height of inline elements are considered with current `font-size` and
`line-height`.  Therefore, if content in inline elements are taller, the
`background-color` does not fill the bigger content background entirely.
For solving this issue, Chrome handles styles of `<font>` element as
outer-most style.  This is reasonable approach, let's follow this.

For solving this issue, we can change the order of `PreservedStyle`s at setting
the preserved styles.  Then, `SetInlinePropertiesAsSubAction` is called with
reversed order and apply later style first and applies newer styles to all
content in an element which is previously inserted.  Therefore, the `<font>`
element styles should be last elements of `PendingStyles::mPreservingStyles`.

When applying new style, our style editor does not reuse existing `<font>`
element, and this causes writing WPT harder.  Therefore, this patch also changes
the applying range of `<font>` style to wrapping existing `<font>` element if
and only if its content is entirely selected.

Unfortunately, this approach cannot get exactly same result as Chrome because we
insert outer-most `<font>` element first, then, try to apply `background-color`,
at this moment, our style editor applies the style to the previously inserted
`<font>` element instead of creating new `<span>` element.  This behavior is
required for compatibility in the other cases.  Additionally, changing only this
behavior requires a lot of method changes to specify how to handle it.  However,
this incompatible behavior may not cause any problems in web apps in the wild.
Therefore, this patch does not solve this incompatible issue.  I think that once
we get a bug report caused by this difference, we should redesign how to set
multiple inline styles once.

Depends on D166416

Differential Revision: https://phabricator.services.mozilla.com/D166617
This commit is contained in:
Masayuki Nakano 2023-01-16 23:59:45 +00:00
Родитель bf6597ccd4
Коммит d83e7a60f8
8 изменённых файлов: 265 добавлений и 11 удалений

Просмотреть файл

@ -994,6 +994,17 @@ struct MOZ_STACK_CLASS EditorInlineStyle : public EditorElementStyle {
mHTMLProperty == nsGkAtoms::s);
}
/**
* Returns true if the style can be represented with <font>.
*/
[[nodiscard]] bool IsStyleOfFontElement() const {
MOZ_ASSERT_IF(
mHTMLProperty == nsGkAtoms::font,
mAttribute == nsGkAtoms::bgcolor || mAttribute == nsGkAtoms::color ||
mAttribute == nsGkAtoms::face || mAttribute == nsGkAtoms::size);
return mHTMLProperty == nsGkAtoms::font && mAttribute != nsGkAtoms::bgcolor;
}
/**
* Returns true if the style is conflict with vertical-align even though
* they are not mapped to vertical-align in the CSS mode.

Просмотреть файл

@ -6172,6 +6172,9 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::CreateStyleForInsertText(
insertNewTextNodeResult.inspect().IgnoreCaretPointSuggestion();
pointToPutCaret.Set(newEmptyTextNode, 0u);
// FIXME: If the stylesToSet have background-color style, it may
// be applied shorter because outer <span> element height is not
// computed with inner element's height.
HTMLEditor::FontSize incrementOrDecrement =
relFontSize > 0 ? HTMLEditor::FontSize::incr
: HTMLEditor::FontSize::decr;

Просмотреть файл

@ -1601,6 +1601,8 @@ EditorRawDOMPoint HTMLEditor::AutoInlineStyleSetter::
return startPoint;
}
const bool useCSS = aHTMLEditor.IsCSSEnabled();
const bool isFontElementStyle = IsStyleOfFontElement();
Element* mostDistantStartParentHavingStyle = nullptr;
for (Element* parent :
startPoint.GetContainer()->InclusiveAncestorsOfType<Element>()) {
@ -1612,6 +1614,12 @@ EditorRawDOMPoint HTMLEditor::AutoInlineStyleSetter::
if (ContentIsElementSettingTheStyle(aHTMLEditor, *parent)) {
mostDistantStartParentHavingStyle = parent;
}
// If we're setting <font> element and there is a <font> element which is
// entirely selected, we should use it.
else if (!useCSS && isFontElementStyle &&
parent->IsHTMLElement(nsGkAtoms::font)) {
mostDistantStartParentHavingStyle = parent;
}
if (parent->GetPreviousSibling()) {
break; // The parent is not first element in its parent, stop climbing.
}
@ -1633,6 +1641,8 @@ EditorRawDOMPoint HTMLEditor::AutoInlineStyleSetter::
return endPoint;
}
const bool useCSS = aHTMLEditor.IsCSSEnabled();
const bool isFontElementStyle = IsStyleOfFontElement();
Element* mostDistantEndParentHavingStyle = nullptr;
for (Element* parent :
endPoint.GetContainer()->InclusiveAncestorsOfType<Element>()) {
@ -1644,6 +1654,12 @@ EditorRawDOMPoint HTMLEditor::AutoInlineStyleSetter::
if (ContentIsElementSettingTheStyle(aHTMLEditor, *parent)) {
mostDistantEndParentHavingStyle = parent;
}
// If we're setting <font> element and there is a <font> element which is
// entirely selected, we should use it.
else if (!useCSS && isFontElementStyle &&
parent->IsHTMLElement(nsGkAtoms::font)) {
mostDistantEndParentHavingStyle = parent;
}
if (parent->GetNextSibling()) {
break; // The parent is not last element in its parent, stop climbing.
}

Просмотреть файл

@ -373,8 +373,29 @@ void PendingStyles::PreserveStyle(nsStaticAtom& aHTMLProperty,
return;
}
mPreservingStyles.AppendElement(MakeUnique<PendingStyle>(
&aHTMLProperty, aAttribute, aAttributeValueOrCSSValue));
// font-size and font-family need to be applied outer-most because height of
// outer inline elements of them are computed without these styles. E.g.,
// background-color may be applied bottom-half of the text. Therefore, we
// need to apply the font styles first.
uint32_t fontStyleCount = 0;
for (const UniquePtr<PendingStyle>& style : Reversed(mPreservingStyles)) {
if (style->GetTag() != nsGkAtoms::font ||
style->GetAttribute() == nsGkAtoms::bgcolor) {
break;
}
MOZ_ASSERT(style->GetAttribute() == nsGkAtoms::color ||
style->GetAttribute() == nsGkAtoms::face ||
style->GetAttribute() == nsGkAtoms::size);
fontStyleCount++;
}
UniquePtr<PendingStyle> style = MakeUnique<PendingStyle>(
&aHTMLProperty, aAttribute, aAttributeValueOrCSSValue);
if (fontStyleCount) {
mPreservingStyles.InsertElementAt(
mPreservingStyles.Length() - fontStyleCount, std::move(style));
} else {
mPreservingStyles.AppendElement(std::move(style));
}
CancelClearingStyle(aHTMLProperty, aAttribute);
}

Просмотреть файл

@ -704,5 +704,50 @@ var browserTests = [
[["stylewithcss","false"],["fontname","sans-serif"]],
"<span style=\"font-family:monospace\">fo<font face=\"sans-serif\">[o</font></span><kbd><font face=\"sans-serif\">b]</font>ar</kbd>",
[true,true],
{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"monospace",false,false,"sans-serif"]}]
{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"monospace",false,false,"sans-serif"]}],
// If contents of <font> are entirely selected, it should be reused.
["<font size=7>[abc]</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
["<font face=\"monospace\" size=\"7\">[abc]</font>",
"<font size=\"7\" face=\"monospace\">[abc]</font>"],
[true,true],
{}],
["<font color=#ff0000>[abc]</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
["<font face=\"monospace\" color=\"#ff0000\">[abc]</font>",
"<font color=\"#ff0000\" face=\"monospace\">[abc]</font>"],
[true,true],
{}],
["<font size=\"7\" color=#ff0000>[abc]</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
["<font color=\"#ff0000\" face=\"monospace\" size=\"7\">[abc]</font>",
"<font color=\"#ff0000\" size=\"7\" face=\"monospace\">[abc]</font>",
"<font face=\"monospace\" color=\"#ff0000\" size=\"7\">[abc]</font>",
"<font face=\"monospace\" size=\"7\" color=\"#ff0000\">[abc]</font>",
"<font size=\"7\" color=\"#ff0000\" face=\"monospace\">[abc]</font>",
"<font size=\"7\" face=\"monospace\" color=\"#ff0000\">[abc]</font>"],
[true,true],
{}],
// but don't split existing <font> if partially selected.
["<font size=7>[a]bc</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
"<font size=\"7\"><font face=\"monospace\">[a]</font>bc</font>",
[true,true],
{}],
["<font size=7>ab[c]</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
"<font size=\"7\">ab<font face=\"monospace\">[c]</font></font>",
[true,true],
{}],
["<font color=#ff0000>[a]bc</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
"<font color=\"#ff0000\"><font face=\"monospace\">[a]</font>bc</font>",
[true,true],
{}],
["<font color=#ff0000>ab[c]</font>",
[["styleWithCSS","false"],["fontName","monospace"]],
"<font color=\"#ff0000\">ab<font face=\"monospace\">[c]</font></font>",
[true,true],
{}],
]

Просмотреть файл

@ -779,5 +779,50 @@ var browserTests = [
[["stylewithcss","false"],["fontsize","4"]],
"<font size=\"6\">fo<font size=\"4\">[o</font></font><span style=\"font-size:xx-large\"><font size=\"4\">b]</font>ar</span>",
[true,true],
{"stylewithcss":[false,true,"",false,false,""],"fontsize":[false,false,"6",false,false,"4"]}]
{"stylewithcss":[false,true,"",false,false,""],"fontsize":[false,false,"6",false,false,"4"]}],
// If contents of <font> are entirely selected, it should be reused.
["<font color=#ff0000>[abc]</font>",
[["styleWithCSS","false"],["fontSize","7"]],
["<font color=\"#ff0000\" size=\"7\">[abc]</font>",
"<font size=\"7\" color=\"#ff0000\">[abc]</font>"],
[true,true],
{}],
["<font face=monospace>[abc]</font>",
[["styleWithCSS","false"],["fontSize","7"]],
["<font face=\"monospace\" size=\"7\">[abc]</font>",
"<font size=\"7\" face=\"monospace\">[abc]</font>"],
[true,true],
{}],
["<font color=#ff0000 face=monospace>[abc]</font>",
[["styleWithCSS","false"],["fontSize","7"]],
["<font color=\"#ff0000\" face=\"monospace\" size=\"7\">[abc]</font>",
"<font color=\"#ff0000\" size=\"7\" face=\"monospace\">[abc]</font>",
"<font face=\"monospace\" color=\"#ff0000\" size=\"7\">[abc]</font>",
"<font face=\"monospace\" size=\"7\" color=\"#ff0000\">[abc]</font>",
"<font size=\"7\" color=\"#ff0000\" face=\"monospace\">[abc]</font>",
"<font size=\"7\" face=\"monospace\" color=\"#ff0000\">[abc]</font>"],
[true,true],
{}],
// but don't split existing <font> if partially selected.
["<font color=#ff0000>[a]bc</font>",
[["styleWithCSS","false"],["fontSize","7"]],
"<font color=\"#ff0000\"><font size=\"7\">[a]</font>bc</font>",
[true,true],
{}],
["<font color=#ff0000>ab[c]</font>",
[["styleWithCSS","false"],["fontSize","7"]],
"<font color=\"#ff0000\">ab<font size=\"7\">[c]</font></font>",
[true,true],
{}],
["<font face=monospace>[a]bc</font>",
[["styleWithCSS","false"],["fontSize","7"]],
"<font face=\"monospace\"><font size=\"7\">[a]</font>bc</font>",
[true,true],
{}],
["<font face=monospace>ab[c]</font>",
[["styleWithCSS","false"],["fontSize","7"]],
"<font face=\"monospace\">ab<font size=\"7\">[c]</font></font>",
[true,true],
{}],
]

Просмотреть файл

@ -734,5 +734,50 @@ var browserTests = [
[["stylewithcss","false"],["forecolor","#0000FF"]],
"<span style=\"color:rgb(165, 42, 42)\">fo<font color=\"#0000ff\">[o</font></span><span style=\"color:rgb(0, 0, 255)\">b]ar</span>",
[true,true],
{"stylewithcss":[false,true,"",false,false,""],"forecolor":[true,false,"rgb(165, 42, 42)",false,false,"rgb(0, 0, 255)"]}]
{"stylewithcss":[false,true,"",false,false,""],"forecolor":[true,false,"rgb(165, 42, 42)",false,false,"rgb(0, 0, 255)"]}],
// If contents of <font> are entirely selected, it should be reused.
["<font size=7>[abc]</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
["<font color=\"#ff0000\" size=\"7\">[abc]</font>",
"<font size=\"7\" color=\"#ff0000\">[abc]</font>"],
[true,true],
{}],
["<font face=monospace>[abc]</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
["<font face=\"monospace\" color=\"#ff0000\">[abc]</font>",
"<font color=\"#ff0000\" face=\"monospace\">[abc]</font>"],
[true,true],
{}],
["<font size=\"7\" face=monospace>[abc]</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
["<font color=\"#ff0000\" face=\"monospace\" size=\"7\">[abc]</font>",
"<font color=\"#ff0000\" size=\"7\" face=\"monospace\">[abc]</font>",
"<font face=\"monospace\" color=\"#ff0000\" size=\"7\">[abc]</font>",
"<font face=\"monospace\" size=\"7\" color=\"#ff0000\">[abc]</font>",
"<font size=\"7\" color=\"#ff0000\" face=\"monospace\">[abc]</font>",
"<font size=\"7\" face=\"monospace\" color=\"#ff0000\">[abc]</font>"],
[true,true],
{}],
// but don't split existing <font> if partially selected.
["<font size=7>[a]bc</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
"<font size=\"7\"><font color=\"#ff0000\">[a]</font>bc</font>",
[true,true],
{}],
["<font size=7>ab[c]</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
"<font size=\"7\">ab<font color=\"#ff0000\">[c]</font></font>",
[true,true],
{}],
["<font face=monospace>[a]bc</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
"<font face=\"monospace\"><font color=\"#ff0000\">[a]</font>bc</font>",
[true,true],
{}],
["<font face=monospace>ab[c]</font>",
[["styleWithCSS","false"],["foreColor","#ff0000"]],
"<font face=\"monospace\">ab<font color=\"#ff0000\">[c]</font></font>",
[true,true],
{}],
]

Просмотреть файл

@ -2062,12 +2062,12 @@ var browserTests = [
{"fontsize":[false,false,"3",false,false,"2"],"subscript":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
["foo[]bar",
[["subscript",""],["fontsize","3"],["inserttext","a"]],
"foo<sub><font size=\"3\">a[]</font></sub>bar",
"foo<font size=\"3\"><sub>a[]</sub></font>bar", // <font> should be outer-most element
[true,true,true],
{"subscript":[false,false,"",false,true,""],"fontsize":[false,false,"3",false,false,"3"],"inserttext":[false,false,"",false,false,""]}],
["foo[]bar",
[["fontsize","3"],["subscript",""],["inserttext","a"]],
"foo<sub><font size=\"3\">a[]</font></sub>bar",
"foo<font size=\"3\"><sub>a[]</sub></font>bar", // <font> should be outer-most element
[true,true,true],
{"fontsize":[false,false,"3",false,false,"3"],"subscript":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
["foo[]bar",
@ -2177,12 +2177,12 @@ var browserTests = [
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<font size=2><sub>[bar]</sub></font>baz",
[["delete",""],["inserttext","a"]],
"foo<sub><font size=\"1\">a[]</font></sub>baz",
"foo<font size=\"1\"><sub>a[]</sub></font>baz", // <font> should be outer-most element
[true,true],
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<sub><font size=3>[bar]</font></sub>baz",
[["delete",""],["inserttext","a"]],
"foo<sub><font size=\"3\">a[]</font></sub>baz",
"foo<font size=\"3\"><sub>a[]</sub></font>baz", // <font> should be outer-most element
[true,true],
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<font size=3><sub>[bar]</sub></font>baz",
@ -2397,12 +2397,12 @@ var browserTests = [
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<font size=2><sub>[bar</sub></font>baz]",
[["delete",""],["inserttext","a"]],
"foo<sub><font size=\"1\">a[]</font></sub>",
"foo<font size=\"1\"><sub>a[]</sub></font>", // <font> should be outer-most element
[true,true],
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<sub><font size=3>[bar</font></sub>baz]",
[["delete",""],["inserttext","a"]],
"foo<sub><font size=\"3\">a[]</font></sub>",
"foo<font size=\"3\"><sub>a[]</sub></font>", // <font> should be outer-most element
[true,true],
{"delete":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
["foo<font size=3><sub>[bar</sub></font>baz]",
@ -2866,4 +2866,72 @@ var browserTests = [
"<span style=\"font-weight:bold; font-style:italic\">a[]</span>"],
[true,true,true],
{}],
// <font> element may be taller than parent inline elements. Therefore, for
// applying background color to new text, <font> element should be put inner-
// most.
["abc[]ef",
[["stylewithcss","false"],["fontSize","4"],["backColor","#00dddd"],["insertText","d"]],
["abc<font size=\"4\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font size=\"4\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"4\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["foreColor","#ff0000"],["backColor","#00dddd"],["insertText","d"]],
["abc<font color=\"#ff0000\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font color=\"#ff0000\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" color=\"#ff0000\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["fontName","monospace"],["backColor","#00dddd"],["insertText","d"]],
["abc<font face=\"monospace\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font face=\"monospace\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" face=\"monospace\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["backColor","#00dddd"],["fontSize","4"],["insertText","d"]],
["abc<font size=\"4\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font size=\"4\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"4\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["backColor","#00dddd"],["foreColor","#ff0000"],["insertText","d"]],
["abc<font color=\"#ff0000\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font color=\"#ff0000\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" color=\"#ff0000\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["backColor","#00dddd"],["fontName","monospace"],["insertText","d"]],
["abc<font face=\"monospace\"><span style=\"background-color:rgb(0, 221, 221)\">d</span></font>ef",
"abc<font face=\"monospace\" style=\"background-color:rgb(0, 221, 221)\">d</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" face=\"monospace\">d</font>ef"],
[true,true,true,true],
{}],
["abc[]ef",
[["stylewithcss","false"],["fontName","monospace"],["foreColor","#ff0000"],["fontSize","7"],["backColor","#00dddd"],["insertText","d"]],
["abc<font color=\"#ff0000\" face=\"monospace\" size=\"7\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font color=\"#ff0000\" size=\"7\" face=\"monospace\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font face=\"monospace\" color=\"#ff0000\" size=\"7\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font face=\"monospace\" size=\"7\" color=\"#ff0000\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font size=\"7\" color=\"#ff0000\" face=\"monospace\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font size=\"7\" face=\"monospace\" color=\"#ff0000\"><span style=\"background-color:rgb(0, 221, 221)\">[d]</span></font>ef",
"abc<font color=\"#ff0000\" face=\"monospace\" size=\"7\" style=\"background-color:rgb(0, 221, 221)\">[d]</font>ef",
"abc<font color=\"#ff0000\" size=\"7\" face=\"monospace\" style=\"background-color:rgb(0, 221, 221)\">[d]</font>ef",
"abc<font face=\"monospace\" color=\"#ff0000\" size=\"7\" style=\"background-color:rgb(0, 221, 221)\">[d]</font>ef",
"abc<font face=\"monospace\" size=\"7\" color=\"#ff0000\" style=\"background-color:rgb(0, 221, 221)\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"7\" color=\"#ff0000\" face=\"monospace\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"7\" face=\"monospace\" color=\"#ff0000\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" color=\"#ff0000\" face=\"monospace\" size=\"7\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" color=\"#ff0000\" size=\"7\" face=\"monospace\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" face=\"monospace\" color=\"#ff0000\" size=\"7\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" face=\"monospace\" size=\"7\" color=\"#ff0000\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"7\" color=\"#ff0000\" face=\"monospace\">[d]</font>ef",
"abc<font style=\"background-color:rgb(0, 221, 221)\" size=\"7\" face=\"monospace\" color=\"#ff0000\">[d]</font>ef"],
[true,true,true,true,true,true],
{}],
]