Bug 1312379 part 1 - [css-align][css-flexbox][css-grid] Introduce nsIFrame methods for calculating baselines per CSS Alignment and CSS2 'vertical-align'. r=dholbert

This commit is contained in:
Mats Palmgren 2016-12-20 23:56:35 +01:00
Родитель 888ef8dd4c
Коммит ed15c70d6d
14 изменённых файлов: 294 добавлений и 46 удалений

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

@ -5877,7 +5877,8 @@ nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() == 0)) {
// empty grid/flex/table container
aResult->mBStart = 0;
aResult->mBaseline = aFrame->SynthesizeBaselineFromBorderBox(aWM);
aResult->mBaseline = aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,
BaselineSharingGroup::eFirst);
aResult->mBEnd = aFrame->BSize(aWM);
return true;
}

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

@ -490,12 +490,44 @@ nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItem
}
nscoord
nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const
nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const
{
nscoord result;
if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result))
return result;
return nsFrame::GetLogicalBaseline(aWritingMode);
auto lastBaseline =
GetBaseline(aWM, BaselineSharingGroup::eLast, AlignmentContext::eInline);
return BSize(aWM) - lastBaseline;
}
bool
nsBlockFrame::GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const
{
if (aBaselineGroup == BaselineSharingGroup::eFirst) {
return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline);
}
for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd();
line != line_end; ++line) {
if (line->IsBlock()) {
nscoord offset;
nsIFrame* kid = line->mFirstChild;
if (kid->GetVerticalAlignBaseline(aWM, &offset)) {
// Ignore relative positioning for baseline calculations.
const nsSize& sz = line->mContainerSize;
offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
*aBaseline = BSize(aWM) - offset;
return true;
}
} else {
// XXX Is this the right test? We have some bogus empty lines
// floating around, but IsEmpty is perhaps too weak.
if (line->BSize() != 0 || !line->IsEmpty()) {
*aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent());
return true;
}
}
}
return false;
}
nscoord

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

@ -121,6 +121,19 @@ public:
virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const override
{
nscoord lastBaseline;
if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
*aBaseline = BSize() - lastBaseline;
return true;
}
return false;
}
bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const override;
virtual nscoord GetCaretBaseline() const override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
virtual nsSplittableType GetSplittableType() const override;

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

@ -455,7 +455,8 @@ public:
nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
if (!found) {
mAscent = mFrame->SynthesizeBaselineFromBorderBox(mWM);
mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
BaselineSharingGroup::eFirst);
}
}
return mAscent;

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

@ -83,6 +83,23 @@ public:
nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override;
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const override
{
return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
}
bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const override
{
if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
return false;
}
*aBaseline = GetLogicalBaseline(aWM);
return true;
}
// nsContainerFrame overrides
uint16_t CSSAlignmentForAbsPosChild(
const ReflowInput& aChildRI,

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

@ -726,6 +726,12 @@ public:
return mHelper.ComputeCustomOverflow(aOverflowAreas);
}
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const override {
*aBaseline = GetLogicalBaseline(aWM);
return true;
}
// Recomputes the scrollable overflow area we store in the helper to take children
// that are affected by perpsective set on the outer frame and scroll at different
// rates.
@ -1116,6 +1122,12 @@ public:
return mHelper.ComputeCustomOverflow(aOverflowAreas);
}
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const override {
*aBaseline = GetLogicalBaseline(aWM);
return true;
}
// Called to set the child frames. We typically have three: the scroll area,
// the vertical scrollbar, and the horizontal scrollbar.
virtual void SetInitialChildList(ChildListID aListID,

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

@ -6722,11 +6722,8 @@ nsGridContainerFrame::SynthesizeBaseline(
if (grid && aGridOrderItem.mIsInEdgeTrack) {
isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
grid->GetBBaseline(aGroup, &baseline);
} else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
GetBBaseline(aGroup, childWM, child, &baseline)) {
if (aGroup == BaselineSharingGroup::eLast) {
baseline = size - baseline; // convert to distance from border-box end
}
} else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
baseline = child->GetBaseline(childWM, aGroup, AlignmentContext::eGrid);
} else {
baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
}

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

@ -15,14 +15,6 @@
#include "nsHashKeys.h"
#include "nsTHashtable.h"
// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
enum BaselineSharingGroup
{
// NOTE Used as an array index so must be 0 and 1.
eFirst = 0,
eLast = 1,
};
/**
* Factory function.
* @return a newly allocated nsGridContainerFrame (infallible)
@ -120,6 +112,22 @@ public:
return b;
}
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const override
{
return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
}
bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const override
{
if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
return false;
}
return GetBBaseline(aBaselineGroup, aBaseline);
}
#ifdef DEBUG_FRAME_DUMP
nsresult GetFrameName(nsAString& aResult) const override;
#endif

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

@ -417,6 +417,24 @@ enum nsBidiDirection {
};
namespace mozilla {
// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
enum BaselineSharingGroup
{
// NOTE Used as an array index so must be 0 and 1.
eFirst = 0,
eLast = 1,
};
// Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context
enum class AlignmentContext
{
eInline,
eTable,
eFlexbox,
eGrid,
};
/*
* For replaced elements only. Gets the intrinsic dimensions of this element.
* The dimensions may only be one of the following two types:
@ -499,6 +517,8 @@ static void ReleaseValue(T* aPropertyValue)
class nsIFrame : public nsQueryFrame
{
public:
using AlignmentContext = mozilla::AlignmentContext;
using BaselineSharingGroup = mozilla::BaselineSharingGroup;
template <typename T> using Maybe = mozilla::Maybe<T>;
using Nothing = mozilla::Nothing;
using OnNonvisible = mozilla::OnNonvisible;
@ -1199,11 +1219,93 @@ public:
bool GetShapeBoxBorderRadii(nscoord aRadii[8]) const;
/**
* XXX: this method will likely be replaced by GetVerticalAlignBaseline
* Get the position of the frame's baseline, relative to the top of
* the frame (its top border edge). Only valid when Reflow is not
* needed.
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context, with the ltr/rtl
* direction tweak done by nsIFrame::GetWritingMode(nsIFrame*) in inline
* contexts (see that method).
*/
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const = 0;
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const = 0;
/**
* Synthesize a first(last) inline-axis baseline from our margin-box.
* An alphabetical baseline is at the start(end) edge and a central baseline
* is at the center of our block-axis margin-box (aWM tells which to use).
* https://drafts.csswg.org/css-align-3/#synthesize-baselines
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context
* @return an offset from our border-box block-axis start(end) edge for
* a first(last) baseline respectively
* (implemented in nsIFrameInlines.h)
*/
inline nscoord SynthesizeBaselineBOffsetFromMarginBox(
mozilla::WritingMode aWM,
BaselineSharingGroup aGroup) const;
/**
* Synthesize a first(last) inline-axis baseline from our border-box.
* An alphabetical baseline is at the start(end) edge and a central baseline
* is at the center of our block-axis border-box (aWM tells which to use).
* https://drafts.csswg.org/css-align-3/#synthesize-baselines
* @note The returned value is only valid when reflow is not needed.
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context
* @return an offset from our border-box block-axis start(end) edge for
* a first(last) baseline respectively
* (implemented in nsIFrameInlines.h)
*/
inline nscoord SynthesizeBaselineBOffsetFromBorderBox(
mozilla::WritingMode aWM,
BaselineSharingGroup aGroup) const;
/**
* Return the position of the frame's inline-axis baseline, or synthesize one
* for the given alignment context. The returned baseline is the distance from
* the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
* @note The returned value is only valid when reflow is not needed.
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context
* @param aBaselineOffset out-param, only valid if the method returns true
* (implemented in nsIFrameInlines.h)
*/
inline nscoord GetBaseline(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
AlignmentContext aAlignmentContext) const;
/**
* XXX: this method is taking over the role that GetLogicalBaseline has.
* Return true if the frame has a CSS2 'vertical-align' baseline.
* If it has, then the returned baseline is the distance from the block-
* axis border-box start edge.
* @note This method should only be used in AlignmentContext::eInline contexts.
* @note The returned value is only valid when reflow is not needed.
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context
* @param aBaseline the baseline offset, only valid if the method returns true
*/
virtual bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
nscoord* aBaseline) const {
return false;
}
/**
* Return true if the frame has a first(last) inline-axis natural baseline per
* CSS Box Alignment. If so, then the returned baseline is the distance from
* the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
* https://drafts.csswg.org/css-align-3/#natural-baseline
* @note The returned value is only valid when reflow is not needed.
* @note You should only call this on frames with a WM that's parallel to aWM.
* @param aWM the writing-mode of the alignment context
* @param aBaseline the baseline offset, only valid if the method returns true
*/
virtual bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const {
return false;
}
/**
* Get the position of the baseline on which the caret needs to be placed,
@ -1215,18 +1317,6 @@ public:
return GetLogicalBaseline(GetWritingMode());
}
/**
* Synthesize a baseline from our border box. For an alphabetical baseline
* this is the end edge of the border box. For a central baseline it's
* the center of the border box.
* https://drafts.csswg.org/css-align-3/#synthesize-baselines
*/
nscoord SynthesizeBaselineFromBorderBox(mozilla::WritingMode aWM) const
{
nscoord borderBoxSize = BSize(aWM);
return aWM.IsAlphabeticalBaseline() ? borderBoxSize : borderBoxSize / 2;
}
///////////////////////////////////////////////////////////////////////////////
// The public visibility API.
///////////////////////////////////////////////////////////////////////////////

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

@ -96,4 +96,68 @@ nsIFrame::GetDisplay() const
return StyleDisplay()->GetDisplay(this);
}
nscoord
nsIFrame::SynthesizeBaselineBOffsetFromMarginBox(
mozilla::WritingMode aWM,
BaselineSharingGroup aGroup) const
{
MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
auto margin = GetLogicalUsedMargin(aWM);
if (aGroup == BaselineSharingGroup::eFirst) {
if (aWM.IsAlphabeticalBaseline()) {
// First baseline for inverted-line content is the block-start margin edge,
// as the frame is in effect "flipped" for alignment purposes.
return MOZ_UNLIKELY(aWM.IsLineInverted()) ? -margin.BStart(aWM)
: BSize(aWM) + margin.BEnd(aWM);
}
nscoord marginBoxCenter = (BSize(aWM) + margin.BStartEnd(aWM)) / 2;
return marginBoxCenter - margin.BStart(aWM);
}
MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
if (aWM.IsAlphabeticalBaseline()) {
// Last baseline for inverted-line content is the block-start margin edge,
// as the frame is in effect "flipped" for alignment purposes.
return MOZ_UNLIKELY(aWM.IsLineInverted()) ? BSize(aWM) + margin.BStart(aWM)
: -margin.BEnd(aWM);
}
// Round up for central baseline offset, to be consistent with eFirst.
nscoord marginBoxSize = BSize(aWM) + margin.BStartEnd(aWM);
nscoord marginBoxCenter = (marginBoxSize / 2) + (marginBoxSize % 2);
return marginBoxCenter - margin.BEnd(aWM);
}
nscoord
nsIFrame::SynthesizeBaselineBOffsetFromBorderBox(
mozilla::WritingMode aWM,
BaselineSharingGroup aGroup) const
{
MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
nscoord borderBoxSize = BSize(aWM);
if (aGroup == BaselineSharingGroup::eFirst) {
return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? borderBoxSize
: borderBoxSize / 2;
}
MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
// Round up for central baseline offset, to be consistent with eFirst.
auto borderBoxCenter = (borderBoxSize / 2) + (borderBoxSize % 2);
return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? 0 : borderBoxCenter;
}
nscoord
nsIFrame::GetBaseline(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
AlignmentContext aAlignmentContext) const
{
MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
nscoord baseline;
if (GetNaturalBaselineBOffset(aWM, aBaselineGroup, &baseline)) {
return baseline;
}
if (aAlignmentContext == AlignmentContext::eInline) {
return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup);
}
// XXX AlignmentContext::eTable should use content box?
return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup);
}
#endif

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

@ -84,7 +84,7 @@ x { display:block; height:20px; }
<!-- grid wrapped in FIELDSET inline -->
<div class="columns" style="height: 40px; margin-left:200px">
<div style="padding-top:2px; background:grey">
<div style="display:inline-block; overflow:hidden; border:none; padding:0; margin:0">
<div style="display:inline-block; border:none; padding:0; margin:0">
<div class="grid">
<span style="grid-row:span 2"><x></x></span>
<span><x></x></span>

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

@ -23,24 +23,29 @@
div { display: inline-block; }
table { display: inline-table; }
.big {
height: 100px;
font: 24px sans-serif;
margin-top: 20px;
}
.lime { background: lime; }
.pink { background: pink; }
.aqua { background: aqua; }
i { display:inline-block; width:20px; height:2px; background:black; }
.ref {
-moz-appearance:none;
-ms-appearance:none;
-webkit-appearance:none;
appearance:none;
border:none;
margin:0;
padding:0;
border-bottom:2px solid black;
width:20px;
}
</style>
</head>
<body>
<div class="flexbox">
<div class="lime">text</div
><button>btn</button
><input type="radio"
/><input type="checkbox"
/><label class="pink">label</label
><label class="pink">label</label
><table cellspacing="0" cellpadding="0" class="aqua">
<label>lab<br/>el</label>
</table
@ -54,5 +59,8 @@
<fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
</table>
</div>
<div class="flexbox" style="font-size:0"><input type="radio"
/><input type="checkbox"
/><input type="checkbox" class="ref"/></div>
</body>
</html>

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

@ -30,19 +30,24 @@
.lime { background: lime; }
.pink { background: pink; }
.aqua { background: aqua; }
i { display:inline-block; width:20px; height:2px; background:black; }
</style>
</head>
<body>
<div class="flexbox">
<div class="lime">text</div>
<button>btn</button>
<input type="radio"/>
<input type="checkbox"/>
<label class="pink">label</label>
<label class="aqua">lab<br/>el</label>
<fieldset>field<br/>set</fieldset>
<fieldset><legend>leg</legend>field<br/>set</fieldset>
<fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
</div>
<div class="flexbox" style="font-size:0">
<input type="radio"/>
<input type="checkbox"/>
<i></i>
</div>
</body>
</html>

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

@ -17,7 +17,7 @@ fails == flexbox-align-self-baseline-horiz-2.xhtml flexbox-align-self-baseline-
# This one fails on windows R (but not Ru, strangely) and GTK.
# On Windows R and GTK, the single-line <label> flex item has a different
# background size in test vs. ref
fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) == flexbox-align-self-baseline-horiz-3.xhtml flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate
fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) skip-if(Android) == flexbox-align-self-baseline-horiz-3.xhtml flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate the random-if. The skip-if(Android) is because checkbox/radio appearance:none doesn't work as expected.
== flexbox-align-self-baseline-horiz-4.xhtml flexbox-align-self-baseline-horiz-4-ref.xhtml
# Tests for box-sizing on flex containers and flex items.