 Chromium Code Reviews
 Chromium Code Reviews Issue 1403643002:
  Rework list marker spacing for better web compatibility.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@472084_use_list_item_painter
    
  
    Issue 1403643002:
  Rework list marker spacing for better web compatibility.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@472084_use_list_item_painter| Index: third_party/WebKit/Source/core/layout/LayoutListMarker.cpp | 
| diff --git a/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp b/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp | 
| index e44011989136eb710c69f07f7d3fbbd11ed657bc..fe1550abdd90d77779d3fae9782bee4b69ef99cd 100644 | 
| --- a/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp | 
| +++ b/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp | 
| @@ -36,6 +36,9 @@ | 
| namespace blink { | 
| +// TODO(wkorman): A seemingly arbitrary number tacked on following any image | 
| +// and included as part of margin for any text. Look into history, other | 
| +// browser behavior, and update or remove as appropriate. | 
| const int cMarkerPadding = 7; | 
| 
eae
2015/10/12 06:02:01
cMarkerPaddingPx (assuming it is in pixels).
 
wkorman
2015/10/14 01:55:30
Done.
 | 
| enum SequenceType { NumericSequence, AlphabeticSequence }; | 
| @@ -1034,8 +1037,11 @@ LayoutRect LayoutListMarker::localSelectionRect() const | 
| if (!box) | 
| return LayoutRect(LayoutPoint(), size()); | 
| RootInlineBox& root = inlineBoxWrapper()->root(); | 
| - LayoutUnit newLogicalTop = root.block().style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root.selectionBottom() : root.selectionTop() - inlineBoxWrapper()->logicalTop(); | 
| - if (root.block().style()->isHorizontalWritingMode()) | 
| + const ComputedStyle* blockStyle = root.block().style(); | 
| + LayoutUnit newLogicalTop = blockStyle->isFlippedBlocksWritingMode() | 
| + ? inlineBoxWrapper()->logicalBottom() - root.selectionBottom() | 
| + : root.selectionTop() - inlineBoxWrapper()->logicalTop(); | 
| + if (blockStyle->isHorizontalWritingMode()) | 
| return LayoutRect(0, newLogicalTop, size().width(), root.selectionHeight()); | 
| return LayoutRect(newLogicalTop, 0, root.selectionHeight(), size().height()); | 
| } | 
| @@ -1090,34 +1096,15 @@ void LayoutListMarker::updateMarginsAndContent() | 
| updateMargins(); | 
| } | 
| -void LayoutListMarker::updateContent() | 
| +LayoutListMarker::SimplifiedListStyle LayoutListMarker::simplifiedListStyle() const | 
| { | 
| - // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this. | 
| - // It's unclear if this is a premature optimization. | 
| - if (!preferredLogicalWidthsDirty()) | 
| - return; | 
| - | 
| - m_text = ""; | 
| - | 
| - if (isImage()) { | 
| - // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful | 
| - // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box. | 
| - int bulletWidth = style()->fontMetrics().ascent() / 2; | 
| - IntSize defaultBulletSize(bulletWidth, bulletWidth); | 
| - IntSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom); | 
| - m_image->setContainerSizeForLayoutObject(this, imageSize, style()->effectiveZoom()); | 
| - return; | 
| - } | 
| - | 
| - EListStyleType type = style()->listStyleType(); | 
| - switch (type) { | 
| + switch (style()->listStyleType()) { | 
| case NoneListStyle: | 
| - break; | 
| - case Circle: | 
| + return NoneSimpleStyle; | 
| case Disc: | 
| + case Circle: | 
| case Square: | 
| - m_text = listMarkerText(type, 0); // value is ignored for these types | 
| - break; | 
| + return SymbolSimpleStyle; | 
| case ArabicIndic: | 
| case Armenian: | 
| case Bengali: | 
| @@ -1135,12 +1122,12 @@ void LayoutListMarker::updateContent() | 
| case Georgian: | 
| case Gujarati: | 
| case Gurmukhi: | 
| + case Hebrew: | 
| case Hangul: | 
| case HangulConsonant: | 
| case KoreanHangulFormal: | 
| case KoreanHanjaFormal: | 
| case KoreanHanjaInformal: | 
| - case Hebrew: | 
| case Hiragana: | 
| case HiraganaIroha: | 
| case Kannada: | 
| @@ -1170,11 +1157,55 @@ void LayoutListMarker::updateContent() | 
| case UpperLatin: | 
| case UpperRoman: | 
| case Urdu: | 
| - m_text = listMarkerText(type, m_listItem->value()); | 
| + return LanguageSimpleStyle; | 
| + default: | 
| + return NoneSimpleStyle; | 
| + } | 
| +} | 
| + | 
| +void LayoutListMarker::updateContent() | 
| +{ | 
| + // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this. | 
| + // It's unclear if this is a premature optimization. | 
| + if (!preferredLogicalWidthsDirty()) | 
| + return; | 
| + | 
| + m_text = ""; | 
| + | 
| + if (isImage()) { | 
| + // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful | 
| + // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box. | 
| + int bulletWidth = style()->fontMetrics().ascent() / 2; | 
| 
eae
2015/10/12 06:02:01
Is flooring the result the tight thing to do here?
 
wkorman
2015/10/14 01:55:30
Likely to revise this in subsequent changes when I
 | 
| + IntSize defaultBulletSize(bulletWidth, bulletWidth); | 
| + IntSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom); | 
| + m_image->setContainerSizeForLayoutObject(this, imageSize, style()->effectiveZoom()); | 
| + return; | 
| + } | 
| + | 
| + switch (simplifiedListStyle()) { | 
| + case NoneSimpleStyle: | 
| + break; | 
| + case SymbolSimpleStyle: | 
| + m_text = listMarkerText(style()->listStyleType(), 0); // value is ignored for these types | 
| + break; | 
| + case LanguageSimpleStyle: | 
| + m_text = listMarkerText(style()->listStyleType(), m_listItem->value()); | 
| break; | 
| } | 
| } | 
| +LayoutUnit LayoutListMarker::getWidthOfTextWithSuffix(UChar* suffix, int suffixLength) const | 
| +{ | 
| + if (m_text.isEmpty()) | 
| + return 0; | 
| + | 
| + const Font& font = style()->font(); | 
| + LayoutUnit itemWidth = font.width(m_text); | 
| + TextRun run = constructTextRun(font, suffix, suffixLength, styleRef(), style()->direction()); | 
| 
eae
2015/10/12 06:02:01
How about constructing a text run with both the te
 
wkorman
2015/10/14 01:55:30
Added TODO to look at this, I agree there's redund
 | 
| + LayoutUnit suffixSpaceWidth = font.width(run); | 
| + return itemWidth + suffixSpaceWidth; | 
| +} | 
| + | 
| void LayoutListMarker::computePreferredLogicalWidths() | 
| { | 
| ASSERT(preferredLogicalWidthsDirty()); | 
| @@ -1188,77 +1219,24 @@ void LayoutListMarker::computePreferredLogicalWidths() | 
| return; | 
| } | 
| - const Font& font = style()->font(); | 
| - | 
| LayoutUnit logicalWidth = 0; | 
| - EListStyleType type = style()->listStyleType(); | 
| - switch (type) { | 
| - case NoneListStyle: | 
| + switch (simplifiedListStyle()) { | 
| + case NoneSimpleStyle: | 
| break; | 
| - case Circle: | 
| - case Disc: | 
| - case Square: | 
| - logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2; | 
| + case SymbolSimpleStyle: { | 
| + // For these styles, the text of the marker will be the single character itself, | 
| + // and so the only suffix will be a single space character to provide sufficient | 
| + // separation between the list marker text and the associated list item text. | 
| + UChar suffix[1] = { listMarkerSuffix(style()->listStyleType(), m_listItem->value()) }; | 
| + logicalWidth = getWidthOfTextWithSuffix(suffix, 1); | 
| + } | 
| break; | 
| - case ArabicIndic: | 
| - case Armenian: | 
| - case Bengali: | 
| - case Cambodian: | 
| - case CJKIdeographic: | 
| - case CjkEarthlyBranch: | 
| - case CjkHeavenlyStem: | 
| - case DecimalLeadingZero: | 
| - case DecimalListStyle: | 
| - case Devanagari: | 
| - case EthiopicHalehame: | 
| - case EthiopicHalehameAm: | 
| - case EthiopicHalehameTiEr: | 
| - case EthiopicHalehameTiEt: | 
| - case Georgian: | 
| - case Gujarati: | 
| - case Gurmukhi: | 
| - case Hangul: | 
| - case HangulConsonant: | 
| - case KoreanHangulFormal: | 
| - case KoreanHanjaFormal: | 
| - case KoreanHanjaInformal: | 
| - case Hebrew: | 
| - case Hiragana: | 
| - case HiraganaIroha: | 
| - case Kannada: | 
| - case Katakana: | 
| - case KatakanaIroha: | 
| - case Khmer: | 
| - case Lao: | 
| - case LowerAlpha: | 
| - case LowerArmenian: | 
| - case LowerGreek: | 
| - case LowerLatin: | 
| - case LowerRoman: | 
| - case Malayalam: | 
| - case Mongolian: | 
| - case Myanmar: | 
| - case Oriya: | 
| - case Persian: | 
| - case SimpChineseFormal: | 
| - case SimpChineseInformal: | 
| - case Telugu: | 
| - case Thai: | 
| - case Tibetan: | 
| - case TradChineseFormal: | 
| - case TradChineseInformal: | 
| - case UpperAlpha: | 
| - case UpperArmenian: | 
| - case UpperLatin: | 
| - case UpperRoman: | 
| - case Urdu: | 
| - if (m_text.isEmpty()) { | 
| - logicalWidth = 0; | 
| - } else { | 
| - LayoutUnit itemWidth = font.width(m_text); | 
| - UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' }; | 
| - LayoutUnit suffixSpaceWidth = font.width(constructTextRun(font, suffixSpace, 2, styleRef(), style()->direction())); | 
| - logicalWidth = itemWidth + suffixSpaceWidth; | 
| + case LanguageSimpleStyle: { | 
| + // For these styles, we have some kind of a language-specific separating | 
| + // character, followed by a space character to provide sufficient | 
| + // separation between the list marker text and the associated list item text. | 
| + UChar suffixSpace[2] = { listMarkerSuffix(style()->listStyleType(), m_listItem->value()), ' ' }; | 
| + logicalWidth = getWidthOfTextWithSuffix(suffixSpace, 2); | 
| } | 
| break; | 
| } | 
| @@ -1273,65 +1251,30 @@ void LayoutListMarker::computePreferredLogicalWidths() | 
| void LayoutListMarker::updateMargins() | 
| { | 
| - const FontMetrics& fontMetrics = style()->fontMetrics(); | 
| - | 
| LayoutUnit marginStart = 0; | 
| LayoutUnit marginEnd = 0; | 
| if (isInside()) { | 
| if (isImage()) { | 
| marginEnd = cMarkerPadding; | 
| - } else { | 
| - switch (style()->listStyleType()) { | 
| - case Disc: | 
| - case Circle: | 
| - case Square: | 
| - marginStart = -1; | 
| - marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1; | 
| - break; | 
| - default: | 
| - break; | 
| - } | 
| + } else if (simplifiedListStyle() == SymbolSimpleStyle) { | 
| + marginStart = minPreferredLogicalWidth(); | 
| + marginEnd = 0; | 
| } | 
| } else { | 
| if (style()->isLeftToRightDirection()) { | 
| - if (isImage()) { | 
| + if (isImage()) | 
| marginStart = -minPreferredLogicalWidth() - cMarkerPadding; | 
| - } else { | 
| - int offset = fontMetrics.ascent() * 2 / 3; | 
| - switch (style()->listStyleType()) { | 
| - case Disc: | 
| - case Circle: | 
| - case Square: | 
| - marginStart = -offset - cMarkerPadding - 1; | 
| - break; | 
| - case NoneListStyle: | 
| - break; | 
| - default: | 
| - marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2; | 
| - } | 
| - } | 
| + else if (style()->listStyleType() != NoneListStyle) | 
| + marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth(); | 
| marginEnd = -marginStart - minPreferredLogicalWidth(); | 
| } else { | 
| - if (isImage()) { | 
| + if (isImage()) | 
| marginEnd = cMarkerPadding; | 
| - } else { | 
| - int offset = fontMetrics.ascent() * 2 / 3; | 
| - switch (style()->listStyleType()) { | 
| - case Disc: | 
| - case Circle: | 
| - case Square: | 
| - marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth(); | 
| - break; | 
| - case NoneListStyle: | 
| - break; | 
| - default: | 
| - marginEnd = m_text.isEmpty() ? 0 : offset / 2; | 
| - } | 
| - } | 
| + else if (style()->listStyleType() != NoneListStyle) | 
| + marginEnd = 0; | 
| marginStart = -marginEnd - minPreferredLogicalWidth(); | 
| } | 
| - | 
| } | 
| mutableStyleRef().setMarginStart(Length(marginStart, Fixed)); | 
| @@ -1364,79 +1307,27 @@ IntRect LayoutListMarker::getRelativeMarkerRect() const | 
| return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height()); | 
| IntRect relativeRect; | 
| - EListStyleType type = style()->listStyleType(); | 
| - switch (type) { | 
| - case Disc: | 
| - case Circle: | 
| - case Square: { | 
| - // FIXME: Are these particular rounding rules necessary? | 
| - const FontMetrics& fontMetrics = style()->fontMetrics(); | 
| - int ascent = fontMetrics.ascent(); | 
| - int bulletWidth = (ascent * 2 / 3 + 1) / 2; | 
| - relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth); | 
| - break; | 
| - } | 
| - case NoneListStyle: | 
| + switch (simplifiedListStyle()) { | 
| + case NoneSimpleStyle: | 
| return IntRect(); | 
| - case ArabicIndic: | 
| - case Armenian: | 
| - case Bengali: | 
| - case Cambodian: | 
| - case CJKIdeographic: | 
| - case CjkEarthlyBranch: | 
| - case CjkHeavenlyStem: | 
| - case DecimalLeadingZero: | 
| - case DecimalListStyle: | 
| - case Devanagari: | 
| - case EthiopicHalehame: | 
| - case EthiopicHalehameAm: | 
| - case EthiopicHalehameTiEr: | 
| - case EthiopicHalehameTiEt: | 
| - case Georgian: | 
| - case Gujarati: | 
| - case Gurmukhi: | 
| - case Hangul: | 
| - case KoreanHangulFormal: | 
| - case KoreanHanjaFormal: | 
| - case KoreanHanjaInformal: | 
| - case HangulConsonant: | 
| - case Hebrew: | 
| - case Hiragana: | 
| - case HiraganaIroha: | 
| - case Kannada: | 
| - case Katakana: | 
| - case KatakanaIroha: | 
| - case Khmer: | 
| - case Lao: | 
| - case LowerAlpha: | 
| - case LowerArmenian: | 
| - case LowerGreek: | 
| - case LowerLatin: | 
| - case LowerRoman: | 
| - case Malayalam: | 
| - case Mongolian: | 
| - case Myanmar: | 
| - case Oriya: | 
| - case Persian: | 
| - case SimpChineseFormal: | 
| - case SimpChineseInformal: | 
| - case Telugu: | 
| - case Thai: | 
| - case Tibetan: | 
| - case TradChineseFormal: | 
| - case TradChineseInformal: | 
| - case UpperAlpha: | 
| - case UpperArmenian: | 
| - case UpperLatin: | 
| - case UpperRoman: | 
| - case Urdu: | 
| - if (m_text.isEmpty()) | 
| - return IntRect(); | 
| - const Font& font = style()->font(); | 
| - int itemWidth = font.width(m_text); | 
| - UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' }; | 
| - int suffixSpaceWidth = font.width(constructTextRun(font, suffixSpace, 2, styleRef(), style()->direction())); | 
| - relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height()); | 
| + case SymbolSimpleStyle: { | 
| + // For these styles, the text of the marker will be the single character itself, | 
| + // and so the only suffix will be a single space character to provide sufficient | 
| + // separation between the list marker text and the associated list item text. | 
| + UChar suffix[1] = { listMarkerSuffix(style()->listStyleType(), m_listItem->value()) }; | 
| + LayoutUnit itemWidth = getWidthOfTextWithSuffix(suffix, 1); | 
| + relativeRect = IntRect(0, 0, itemWidth, style()->font().fontMetrics().height()); | 
| + } | 
| + break; | 
| + case LanguageSimpleStyle: { | 
| + // For these styles, we have some kind of a language-specific separating | 
| + // character, followed by a space character to provide sufficient | 
| + // separation between the list marker text and the associated list item text. | 
| + UChar suffixSpace[2] = { listMarkerSuffix(style()->listStyleType(), m_listItem->value()), ' ' }; | 
| + LayoutUnit itemWidth = getWidthOfTextWithSuffix(suffixSpace, 2); | 
| + relativeRect = IntRect(0, 0, itemWidth, style()->font().fontMetrics().height()); | 
| + } | 
| + break; | 
| } | 
| if (!style()->isHorizontalWritingMode()) { | 
| @@ -1463,6 +1354,8 @@ LayoutRect LayoutListMarker::selectionRectForPaintInvalidation(const LayoutBoxMo | 
| if (selectionState() == SelectionNone || !inlineBoxWrapper()) | 
| return LayoutRect(); | 
| + // TODO(wkorman): Make sure the output rect from this method matches what we expect to see. | 
| + | 
| RootInlineBox& root = inlineBoxWrapper()->root(); | 
| LayoutRect rect(0, root.selectionTop() - location().y(), size().width(), root.selectionHeight()); | 
| mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0); |