Diff
Modified: trunk/LayoutTests/ChangeLog (197463 => 197464)
--- trunk/LayoutTests/ChangeLog 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/LayoutTests/ChangeLog 2016-03-02 22:29:26 UTC (rev 197464)
@@ -1,3 +1,15 @@
+2016-03-02 Dave Hyatt <[email protected]>
+
+ Add support for the "first" value of the hanging-punctuation property.
+ https://bugs.webkit.org/show_bug.cgi?id=154919
+
+ Reviewed by Simon Fraser.
+
+ * fast/text/hanging-punctuation-first-expected.html: Added.
+ * fast/text/hanging-punctuation-first-rtl-expected.html: Added.
+ * fast/text/hanging-punctuation-first-rtl.html: Added.
+ * fast/text/hanging-punctuation-first.html: Added.
+
2016-03-01 Ryosuke Niwa <[email protected]>
Make HTML parser construct custom elements
Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html (0 => 197464)
--- trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-expected.html 2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+ body { font-family: 'Ahem'; color:green }
+ .hang { text-indent: -1em; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div style="margin-left:1em; text-align:justify; width:300px;"><span style="border-left:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:1em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file
Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html (0 => 197464)
--- trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl-expected.html 2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+ body { font-family: 'Ahem'; color:green }
+ .hang { text-indent: -1em; margin:1em }
+</style>
+</head>
+<body style="direction:rtl">
+<div style="float:right" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div style="margin-right:1em; text-align:justify; width:300px;"><span style="border-right:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:1em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file
Added: trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html (0 => 197464)
--- trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first-rtl.html 2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+ body { font-family: 'Ahem'; color:green }
+ .hang { hanging-punctuation: first; margin:1em }
+</style>
+</head>
+<body style="direction:rtl">
+<div style="float:right" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span style="border-right:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:2em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file
Added: trunk/LayoutTests/fast/text/hanging-punctuation-first.html (0 => 197464)
--- trunk/LayoutTests/fast/text/hanging-punctuation-first.html (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-first.html 2016-03-02 22:29:26 UTC (rev 197464)
@@ -0,0 +1,14 @@
+<head>
+<style>
+ body { font-family: 'Ahem'; color:green }
+ .hang { hanging-punctuation: first; margin:1em }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">(Hang test)</div>
+<div style="clear:both">
+<div class="hang">(This should hang.<br>(This should not.</div>
+<div class="hang" style="text-align:justify; width:300px;">(This should hang and justifybecause and now were fine.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span>(</span>This should hang.</div>
+<div class="hang" style="text-align:justify; width:300px;"><span style="border-left:1em solid blue">(</span>This should not hang.</div>
+<div class="hang" style="text-indent:2em; text-align:justify; width:300px;">(This should hang into the text-indent.</div>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (197463 => 197464)
--- trunk/Source/WebCore/ChangeLog 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/ChangeLog 2016-03-02 22:29:26 UTC (rev 197464)
@@ -1,3 +1,61 @@
+2016-03-02 Dave Hyatt <[email protected]>
+
+ Add support for the "first" value of the hanging-punctuation property.
+ https://bugs.webkit.org/show_bug.cgi?id=154919
+
+ Reviewed by Simon Fraser.
+
+ New tests added in fast/text.
+
+ Implement the "first" value for hanging-punctuation as described here:
+ https://drafts.csswg.org/css-text-3/#propdef-hanging-punctuation
+
+ * rendering/RenderBlockFlow.cpp:
+ (WebCore::RenderBlockFlow::computeInlinePreferredLogicalWidths):
+ Update the preferred logical width computation to factor in hanging punctuation.
+ This check is similar to the text-indent logic in that we only want to do it for
+ the first formatted line.
+
+ * rendering/RenderBlockFlow.h:
+ (WebCore::RenderBlockFlow::simpleLineLayout):
+ Make sure to turn off simple line layout when hanging punctuation is present. Eventually
+ it should be feasible to support this in simple line layout, but since the full line
+ layout model has to work with it anyway, we are starting there.
+
+ * rendering/RenderBlockLineLayout.cpp:
+ (WebCore::inlineAncestorHasStartBorderPaddingOrMargin):
+ (WebCore::isLastInFlowRun):
+ Helper functions that are needed to determine whether or not we're allowed to apply
+ hanging punctuation "first" to a text run.
+
+ (WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
+ This function manipulates logicalLeft and availableWidth when hanging punctuation
+ is present to shift the line as needed and to expand the availableWidth of the line.
+
+ * rendering/RenderText.cpp:
+ (WebCore::isHangablePunctuationAtLineStart):
+ (WebCore::isHangablePunctuationAtLineEnd):
+ (WebCore::RenderText::hangablePunctuationStartWidth):
+ (WebCore::RenderText::trimmedPrefWidths):
+ * rendering/RenderText.h:
+ RenderText has a helper function for handing back the hangable punctuation width. This
+ is used everywhere line layout wants to apply that offset. There are also helper functions
+ that detect whether the character is a hangable punctuation character.
+
+ * rendering/SimpleLineLayout.cpp:
+ (WebCore::SimpleLineLayout::canUseForWithReason):
+ (WebCore::SimpleLineLayout::printReason):
+ Turn off simple line layout when hanging punctuation is enabled.
+
+ * rendering/line/BreakingContext.h:
+ (WebCore::BreakingContext::handleText):
+ Modified to expand the available width when hanging punctuation is present so that we
+ know we have more room on the line.
+
+ * rendering/line/LineWidth.h:
+ (WebCore::LineWidth::isFirstLine):
+ Add an accessor for whether or not we're the first line.
+
2016-03-01 Ryosuke Niwa <[email protected]>
Make HTML parser construct custom elements
Modified: trunk/Source/WebCore/rendering/RenderBlockFlow.cpp (197463 => 197464)
--- trunk/Source/WebCore/rendering/RenderBlockFlow.cpp 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderBlockFlow.cpp 2016-03-02 22:29:26 UTC (rev 197464)
@@ -4064,6 +4064,9 @@
RenderObject* prevFloat = 0;
bool isPrevChildInlineFlow = false;
bool shouldBreakLineAfterText = false;
+ bool canHangPunctuationAtStart = styleToUse.hangingPunctuation() & FirstHangingPunctuation;
+ bool addedStartPunctuationHang = false;
+
while (RenderObject* child = childIterator.next()) {
bool autoWrap = child->isReplaced() ? child->parent()->style().autoWrap() :
child->style().autoWrap();
@@ -4179,6 +4182,9 @@
else
addedTextIndent = true;
}
+
+ if (canHangPunctuationAtStart && !addedStartPunctuationHang && !child->isFloating() && !isAnonymousInlineBlock)
+ addedStartPunctuationHang = true;
// Add our width to the max.
inlineMax += std::max<float>(0, childMax);
@@ -4267,7 +4273,17 @@
hasRemainingNegativeTextIndent = true;
}
}
-
+
+ // See if we have a hanging punctuation situation at the start.
+ if (canHangPunctuationAtStart && !addedStartPunctuationHang) {
+ float hangStartWidth = renderText.hangablePunctuationStartWidth();
+ childMin -= hangStartWidth;
+ beginMin -= hangStartWidth;
+ childMax -= hangStartWidth;
+ beginMax -= hangStartWidth;
+ addedStartPunctuationHang = true;
+ }
+
// If we have no breakable characters at all,
// then this is the easy case. We add ourselves to the current
// min and max and continue.
@@ -4305,6 +4321,7 @@
maxLogicalWidth = preferredWidth(maxLogicalWidth, childMax);
inlineMax = endMax;
addedTextIndent = true;
+ addedStartPunctuationHang = true;
} else
inlineMax += std::max<float>(0, childMax);
}
@@ -4319,6 +4336,7 @@
stripFrontSpaces = true;
trailingSpaceChild = 0;
addedTextIndent = true;
+ addedStartPunctuationHang = true;
}
if (!child->isText() && child->isRenderInline())
Modified: trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp (197463 => 197464)
--- trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp 2016-03-02 22:29:26 UTC (rev 197464)
@@ -806,11 +806,34 @@
}
}
+static bool inlineAncestorHasStartBorderPaddingOrMargin(const RenderBlockFlow& block, const InlineBox& box)
+{
+ bool isLTR = block.style().isLeftToRightDirection();
+ for (auto* currentBox = box.parent(); currentBox; currentBox = currentBox->parent()) {
+ if ((isLTR && currentBox->marginBorderPaddingLogicalLeft() > 0)
+ || (!isLTR && currentBox->marginBorderPaddingLogicalRight() > 0))
+ return true;
+ }
+ return false;
+}
+
+static bool isLastInFlowRun(BidiRun& runToCheck)
+{
+ for (auto* run = runToCheck.next(); run; run = run->next()) {
+ if (!run->box() || run->renderer().isOutOfFlowPositioned() || run->box()->isLineBreak())
+ continue;
+ return false;
+ }
+ return true;
+}
+
BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
WordMeasurements& wordMeasurements)
{
bool needsWordSpacing = false;
+ bool canHangPunctuationAtStart = style().hangingPunctuation() & FirstHangingPunctuation;
+ bool isLTR = style().isLeftToRightDirection();
float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
unsigned expansionOpportunityCount = 0;
bool isAfterExpansion = is<RenderRubyBase>(*this) ? downcast<RenderRubyBase>(*this).isAfterExpansion() : true;
@@ -827,6 +850,15 @@
if (is<RenderText>(run->renderer())) {
auto& renderText = downcast<RenderText>(run->renderer());
auto& textBox = downcast<InlineTextBox>(*run->box());
+ if (canHangPunctuationAtStart && lineInfo.isFirstLine() && (isLTR || isLastInFlowRun(*run))
+ && !inlineAncestorHasStartBorderPaddingOrMargin(*this, *run->box())) {
+ float hangStartWidth = renderText.hangablePunctuationStartWidth();
+ availableLogicalWidth += hangStartWidth;
+ if (style().isLeftToRightDirection())
+ logicalLeft -= hangStartWidth;
+ canHangPunctuationAtStart = false;
+ }
+
if (textAlign == JUSTIFY && run != trailingSpaceRun) {
ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*this, textBox, previousRun, run->next(), textAlign, isAfterExpansion);
applyExpansionBehavior(textBox, expansionBehavior);
@@ -844,6 +876,7 @@
setLogicalWidthForTextRun(lineBox, run, renderText, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
} else {
+ canHangPunctuationAtStart = false;
bool encounteredJustifiedRuby = false;
if (is<RenderRubyRun>(run->renderer()) && textAlign == JUSTIFY && run != trailingSpaceRun && downcast<RenderRubyRun>(run->renderer()).rubyBase()) {
auto* rubyBase = downcast<RenderRubyRun>(run->renderer()).rubyBase();
Modified: trunk/Source/WebCore/rendering/RenderText.cpp (197463 => 197464)
--- trunk/Source/WebCore/rendering/RenderText.cpp 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderText.cpp 2016-03-02 22:29:26 UTC (rev 197464)
@@ -502,6 +502,32 @@
return f.width(run, fallbackFonts, glyphOverflow);
}
+inline bool isHangablePunctuationAtLineStart(UChar c)
+{
+ return U_GET_GC_MASK(c) & (U_GC_PS_MASK | U_GC_PI_MASK | U_GC_PF_MASK);
+}
+
+inline bool isHangablePunctuationAtLineEnd(UChar c)
+{
+ return U_GET_GC_MASK(c) & (U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK);
+}
+
+float RenderText::hangablePunctuationStartWidth() const
+{
+ if (!textLength())
+ return 0;
+
+ ASSERT(m_text);
+ StringImpl& text = *m_text.impl();
+ if (!isHangablePunctuationAtLineStart(text[0]))
+ return 0;
+
+ const RenderStyle& style = this->style();
+ const FontCascade& font = style.fontCascade();
+
+ return widthFromCache(font, 0, 1, 0, 0, 0, style);
+}
+
void RenderText::trimmedPrefWidths(float leadWidth,
float& beginMinW, bool& beginWS,
float& endMinW, bool& endWS,
Modified: trunk/Source/WebCore/rendering/RenderText.h (197463 => 197464)
--- trunk/Source/WebCore/rendering/RenderText.h 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/RenderText.h 2016-03-02 22:29:26 UTC (rev 197464)
@@ -102,6 +102,7 @@
bool& hasBreakableChar, bool& hasBreak,
float& beginMaxW, float& endMaxW,
float& minW, float& maxW, bool& stripFrontSpaces);
+ float hangablePunctuationStartWidth() const;
WEBCORE_EXPORT virtual IntRect linesBoundingBox() const;
LayoutRect linesVisualOverflowBoundingBox() const;
Modified: trunk/Source/WebCore/rendering/SimpleLineLayout.cpp (197463 => 197464)
--- trunk/Source/WebCore/rendering/SimpleLineLayout.cpp 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/SimpleLineLayout.cpp 2016-03-02 22:29:26 UTC (rev 197464)
@@ -111,7 +111,8 @@
FlowHasNoParent = 1LLU << 46,
FlowHasNoChild = 1LLU << 47,
FlowChildIsSelected = 1LLU << 48,
- EndOfReasons = 1LLU << 49
+ FlowHasHangingPunctuation = 1LLU << 49,
+ EndOfReasons = 1LLU << 50
};
const unsigned NoReason = 0;
@@ -283,6 +284,9 @@
SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOutline, reasons, includeReasons);
if (flow.isRubyText() || flow.isRubyBase())
SET_REASON_AND_RETURN_IF_NEEDED(FlowIsRuby, reasons, includeReasons);
+ if (flow.style().hangingPunctuation() != NoHangingPunctuation)
+ SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHangingPunctuation, reasons, includeReasons);
+
// Printing does pagination without a flow thread.
if (flow.document().paginated())
SET_REASON_AND_RETURN_IF_NEEDED(FlowIsPaginated, reasons, includeReasons);
@@ -798,6 +802,9 @@
case FlowIsRuby:
stream << "ruby";
break;
+ case FlowHasHangingPunctuation:
+ stream << "hanging punctuation";
+ break;
case FlowIsPaginated:
stream << "paginated";
break;
Modified: trunk/Source/WebCore/rendering/line/BreakingContext.h (197463 => 197464)
--- trunk/Source/WebCore/rendering/line/BreakingContext.h 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/line/BreakingContext.h 2016-03-02 22:29:26 UTC (rev 197464)
@@ -737,7 +737,8 @@
const FontCascade& font = style.fontCascade();
bool isFixedPitch = font.isFixedPitch();
bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
-
+ bool canHangPunctuationAtStart = style.hangingPunctuation() & FirstHangingPunctuation;
+
unsigned lastSpace = m_current.offset();
float wordSpacing = m_currentStyle->fontCascade().wordSpacing();
float lastSpaceWordSpacing = 0;
@@ -787,6 +788,9 @@
UChar c = m_current.current();
m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
+ if (canHangPunctuationAtStart && !m_current.offset() && m_width.isFirstLine() && !m_width.committedWidth() && !wrapW && !m_current.offset())
+ m_width.addUncommittedWidth(-renderText.hangablePunctuationStartWidth());
+
if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
m_lineInfo.setEmpty(false, &m_block, &m_width);
Modified: trunk/Source/WebCore/rendering/line/LineWidth.h (197463 => 197464)
--- trunk/Source/WebCore/rendering/line/LineWidth.h 2016-03-02 21:56:47 UTC (rev 197463)
+++ trunk/Source/WebCore/rendering/line/LineWidth.h 2016-03-02 22:29:26 UTC (rev 197464)
@@ -77,6 +77,8 @@
void fitBelowFloats(bool isFirstLine = false);
void setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin = 0);
IndentTextOrNot shouldIndentText() const { return m_shouldIndentText; }
+
+ bool isFirstLine() const { return m_isFirstLine; }
private:
void computeAvailableWidthFromLeftAndRight();