Bug 1409083 Part 1: Capture computed flex data for use by devtools. r=dholbert
MozReview-Commit-ID: 2TorIXOJZdh
This commit is contained in:
@@ -948,7 +948,8 @@ public:
|
||||
|
||||
// Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
|
||||
// CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
|
||||
void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
|
||||
void ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
|
||||
ComputedFlexLineInfo* aLineInfo);
|
||||
|
||||
void PositionItemsInMainAxis(uint8_t aJustifyContent,
|
||||
nscoord aContentBoxMainSize,
|
||||
@@ -2380,7 +2381,8 @@ FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
|
||||
}
|
||||
|
||||
void
|
||||
FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
|
||||
FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
|
||||
ComputedFlexLineInfo* aLineInfo)
|
||||
{
|
||||
MOZ_LOG(gFlexContainerLog, LogLevel::Debug, ("ResolveFlexibleLengths\n"));
|
||||
|
||||
@@ -2392,11 +2394,13 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
|
||||
// direction we've chosen:
|
||||
FreezeItemsEarly(isUsingFlexGrow);
|
||||
|
||||
if (mNumFrozenItems == mNumItems) {
|
||||
// All our items are frozen, so we have no flexible lengths to resolve.
|
||||
if ((mNumFrozenItems == mNumItems) && !aLineInfo) {
|
||||
// All our items are frozen, so we have no flexible lengths to resolve,
|
||||
// and we aren't being asked to generate computed line info.
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(!IsEmpty(), "empty lines should take the early-return above");
|
||||
MOZ_ASSERT(!IsEmpty() || aLineInfo,
|
||||
"empty lines should take the early-return above");
|
||||
|
||||
// Subtract space occupied by our items' margins/borders/padding, so we can
|
||||
// just be dealing with the space available for our flex items' content
|
||||
@@ -2430,6 +2434,21 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
|
||||
availableFreeSpace -= item->GetMainSize();
|
||||
}
|
||||
|
||||
// If we have an aLineInfo structure to fill out, and this is the
|
||||
// first time through the loop, capture these sizes as mainBaseSizes.
|
||||
// We only care about the first iteration, because additional
|
||||
// iterations will only reset item base sizes to these values.
|
||||
// We also set a 0 mainDeltaSize. This will be modified later if
|
||||
// the item is stretched or shrunk.
|
||||
if (aLineInfo && (iterationCounter == 0)) {
|
||||
uint32_t itemIndex = 0;
|
||||
for (FlexItem* item = mItems.getFirst(); item; item = item->getNext(),
|
||||
++itemIndex) {
|
||||
aLineInfo->mItems[itemIndex].mMainBaseSize = item->GetMainSize();
|
||||
aLineInfo->mItems[itemIndex].mMainDeltaSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
|
||||
(" available free space = %d\n", availableFreeSpace));
|
||||
|
||||
@@ -2595,6 +2614,45 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
|
||||
item, sizeDelta, item->GetMainSize()));
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an aLineInfo structure to fill out, capture any
|
||||
// size changes that may have occurred in the previous loop.
|
||||
// We don't do this inside the previous loop, because we don't
|
||||
// want to burden layout when aLineInfo is null.
|
||||
if (aLineInfo) {
|
||||
uint32_t itemIndex = 0;
|
||||
for (FlexItem* item = mItems.getFirst(); item; item = item->getNext(),
|
||||
++itemIndex) {
|
||||
// Calculate a deltaSize that represents how much the
|
||||
// flex sizing algorithm "wants" to stretch or shrink this
|
||||
// item during this pass through the algorithm. Later
|
||||
// passes through the algorithm may overwrite this value.
|
||||
// Also, this value may not reflect how much the size of
|
||||
// the item is actually changed, since the size of the
|
||||
// item will be clamped to min and max values later in
|
||||
// this pass. That's intentional, since we want to report
|
||||
// the value that the sizing algorithm tried to stretch
|
||||
// or shrink the item.
|
||||
nscoord deltaSize = item->GetMainSize() -
|
||||
aLineInfo->mItems[itemIndex].mMainBaseSize;
|
||||
|
||||
aLineInfo->mItems[itemIndex].mMainDeltaSize = deltaSize;
|
||||
// If any item on the line is growing, mark the aLineInfo
|
||||
// structure; likewise if any item is shrinking. Items in
|
||||
// a line can't be both growing and shrinking.
|
||||
if (deltaSize > 0) {
|
||||
MOZ_ASSERT(aLineInfo->mGrowthState !=
|
||||
ComputedFlexLineInfo::GrowthState::SHRINKING);
|
||||
aLineInfo->mGrowthState =
|
||||
ComputedFlexLineInfo::GrowthState::GROWING;
|
||||
} else if (deltaSize < 0) {
|
||||
MOZ_ASSERT(aLineInfo->mGrowthState !=
|
||||
ComputedFlexLineInfo::GrowthState::GROWING);
|
||||
aLineInfo->mGrowthState =
|
||||
ComputedFlexLineInfo::GrowthState::SHRINKING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3974,6 +4032,24 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
|
||||
const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
|
||||
|
||||
// Check to see if we need to create a computed info structure, to
|
||||
// be filled out for use by devtools.
|
||||
if (HasAnyStateBits(NS_STATE_FLEX_GENERATE_COMPUTED_VALUES)) {
|
||||
// This state bit will never be cleared. That's acceptable because
|
||||
// it's only set in a Chrome API invoked by devtools, and won't
|
||||
// impact normal browsing.
|
||||
|
||||
// Re-use the ComputedFlexContainerInfo, if it exists.
|
||||
ComputedFlexContainerInfo* info = GetProperty(FlexContainerInfo());
|
||||
if (info) {
|
||||
// We can reuse, as long as we clear out old data.
|
||||
info->mLines.Clear();
|
||||
} else {
|
||||
info = new ComputedFlexContainerInfo();
|
||||
SetProperty(FlexContainerInfo(), info);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're being fragmented into a constrained BSize, then subtract off
|
||||
// borderpadding BStart from that constrained BSize, to get the available
|
||||
// BSize for our content box. (No need to subtract the borderpadding BStart
|
||||
@@ -4117,6 +4193,59 @@ nsFlexContainerFrame::CalculatePackingSpace(uint32_t aNumThingsToPack,
|
||||
*aPackingSpaceRemaining -= totalEdgePackingSpace;
|
||||
}
|
||||
|
||||
nsFlexContainerFrame*
|
||||
nsFlexContainerFrame::GetFlexFrameWithComputedInfo(nsIFrame* aFrame)
|
||||
{
|
||||
// Prepare a lambda function that we may need to call multiple times.
|
||||
auto GetFlexContainerFrame = [](nsIFrame *aFrame) {
|
||||
// Return the aFrame's content insertion frame, iff it is
|
||||
// a flex container frame.
|
||||
nsFlexContainerFrame* flexFrame = nullptr;
|
||||
|
||||
if (aFrame) {
|
||||
nsIFrame* contentFrame = aFrame->GetContentInsertionFrame();
|
||||
if (contentFrame && (contentFrame->IsFlexContainerFrame())) {
|
||||
flexFrame = static_cast<nsFlexContainerFrame*>(contentFrame);
|
||||
}
|
||||
}
|
||||
return flexFrame;
|
||||
};
|
||||
|
||||
nsFlexContainerFrame* flexFrame = GetFlexContainerFrame(aFrame);
|
||||
if (flexFrame) {
|
||||
// Generate the FlexContainerInfo data, if it's not already there.
|
||||
bool reflowNeeded = !flexFrame->HasProperty(FlexContainerInfo());
|
||||
|
||||
if (reflowNeeded) {
|
||||
// Trigger a reflow that generates additional flex property data.
|
||||
// Hold onto aFrame while we do this, in case reflow destroys it.
|
||||
AutoWeakFrame weakFrameRef(aFrame);
|
||||
|
||||
nsIPresShell* shell = flexFrame->PresContext()->PresShell();
|
||||
flexFrame->AddStateBits(NS_STATE_FLEX_GENERATE_COMPUTED_VALUES);
|
||||
shell->FrameNeedsReflow(flexFrame,
|
||||
nsIPresShell::eResize,
|
||||
NS_FRAME_IS_DIRTY);
|
||||
shell->FlushPendingNotifications(FlushType::Layout);
|
||||
|
||||
// Since the reflow may have side effects, get the flex frame
|
||||
// again. But if the weakFrameRef is no longer valid, then we
|
||||
// must bail out.
|
||||
if (!weakFrameRef.IsAlive()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
flexFrame = GetFlexContainerFrame(weakFrameRef.GetFrame());
|
||||
|
||||
MOZ_ASSERT(!flexFrame ||
|
||||
flexFrame->HasProperty(FlexContainerInfo()),
|
||||
"The state bit should've made our forced-reflow "
|
||||
"generate a FlexContainerInfo object");
|
||||
}
|
||||
}
|
||||
return flexFrame;
|
||||
}
|
||||
|
||||
void
|
||||
nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
@@ -4147,13 +4276,70 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
|
||||
}
|
||||
|
||||
// Construct our computed info if we've been asked to do so. This is
|
||||
// necessary to do now so we can capture some computed values for
|
||||
// FlexItems during layout that would not otherwise be saved (like
|
||||
// size adjustments). We'll later fix up the line properties,
|
||||
// because the correct values aren't available yet.
|
||||
ComputedFlexContainerInfo* containerInfo = nullptr;
|
||||
if (HasAnyStateBits(NS_STATE_FLEX_GENERATE_COMPUTED_VALUES)) {
|
||||
containerInfo = GetProperty(FlexContainerInfo());
|
||||
MOZ_ASSERT(containerInfo,
|
||||
"::Reflow() should have created container info.");
|
||||
|
||||
if (!aStruts.IsEmpty()) {
|
||||
// We restarted DoFlexLayout, and may have stale mLines to clear:
|
||||
containerInfo->mLines.Clear();
|
||||
} else {
|
||||
MOZ_ASSERT(containerInfo->mLines.IsEmpty(),
|
||||
"Shouldn't have lines yet.");
|
||||
}
|
||||
|
||||
for (const FlexLine* line = lines.getFirst(); line;
|
||||
line = line->getNext()) {
|
||||
ComputedFlexLineInfo* lineInfo =
|
||||
containerInfo->mLines.AppendElement();
|
||||
// Most lineInfo properties will be set later, but we set
|
||||
// mGrowthState to UNCHANGED here because it may be later
|
||||
// modified by ResolveFlexibleLengths().
|
||||
lineInfo->mGrowthState =
|
||||
ComputedFlexLineInfo::GrowthState::UNCHANGED;
|
||||
|
||||
// The remaining lineInfo properties will be filled out at the
|
||||
// end of this function, when we have real values. But we still
|
||||
// add all the items here, so we can capture computed data for
|
||||
// each item.
|
||||
for (const FlexItem* item = line->GetFirstItem(); item;
|
||||
item = item->getNext()) {
|
||||
ComputedFlexItemInfo* itemInfo =
|
||||
lineInfo->mItems.AppendElement();
|
||||
|
||||
itemInfo->mNode = item->Frame()->GetContent();
|
||||
|
||||
// mMainBaseSize and itemInfo->mMainDeltaSize will
|
||||
// be filled out in ResolveFlexibleLengths().
|
||||
|
||||
// Other FlexItem properties can be captured now.
|
||||
itemInfo->mMainMinSize = item->GetMainMinSize();
|
||||
itemInfo->mMainMaxSize = item->GetMainMaxSize();
|
||||
itemInfo->mCrossMinSize = item->GetCrossMinSize();
|
||||
itemInfo->mCrossMaxSize = item->GetCrossMaxSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aContentBoxMainSize =
|
||||
ResolveFlexContainerMainSize(aReflowInput, aAxisTracker,
|
||||
aContentBoxMainSize, aAvailableBSizeForContent,
|
||||
lines.getFirst(), aStatus);
|
||||
|
||||
for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
|
||||
line->ResolveFlexibleLengths(aContentBoxMainSize);
|
||||
uint32_t lineIndex = 0;
|
||||
for (FlexLine* line = lines.getFirst(); line; line = line->getNext(),
|
||||
++lineIndex) {
|
||||
ComputedFlexLineInfo* lineInfo = containerInfo ?
|
||||
&containerInfo->mLines[lineIndex] :
|
||||
nullptr;
|
||||
line->ResolveFlexibleLengths(aContentBoxMainSize, lineInfo);
|
||||
}
|
||||
|
||||
// Cross Size Determination - Flexbox spec section 9.4
|
||||
@@ -4463,6 +4649,19 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
aReflowInput, aStatus);
|
||||
|
||||
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize)
|
||||
|
||||
// Finally update our line sizing values in our containerInfo.
|
||||
if (containerInfo) {
|
||||
uint32_t lineIndex = 0;
|
||||
for (const FlexLine* line = lines.getFirst(); line;
|
||||
line = line->getNext(), ++lineIndex) {
|
||||
ComputedFlexLineInfo* lineInfo = &containerInfo->mLines[lineIndex];
|
||||
|
||||
lineInfo->mCrossSize = line->GetLineCrossSize();
|
||||
lineInfo->mFirstBaselineOffset = line->GetFirstBaselineOffset();
|
||||
lineInfo->mLastBaselineOffset = line->GetLastBaselineOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -20,6 +20,54 @@ class LogicalPoint;
|
||||
nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
|
||||
nsStyleContext* aContext);
|
||||
|
||||
/**
|
||||
* These structures are used to capture data during reflow to be
|
||||
* extracted by devtools via Chrome APIs. The structures are only
|
||||
* created when requested in GetFlexFrameWithComputedInfo(), and
|
||||
* the structures are attached to the nsFlexContainerFrame via the
|
||||
* FlexContainerInfo property.
|
||||
*/
|
||||
struct ComputedFlexItemInfo
|
||||
{
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
/**
|
||||
* mMainBaseSize is a measure of the size of the item in the main
|
||||
* axis before the flex sizing algorithm is applied. In the spec,
|
||||
* this is called "flex base size", but we use this name to connect
|
||||
* the value to the other main axis sizes.
|
||||
*/
|
||||
nscoord mMainBaseSize;
|
||||
/**
|
||||
* mMainDeltaSize is the value that the flex sizing algorithm
|
||||
* "wants" to use to stretch or shrink the item, before clamping to
|
||||
* the item's main min and max sizes. Since the flex sizing
|
||||
* algorithm proceeds linearly, the mMainDeltaSize for an item only
|
||||
* respects the resolved size of items already frozen.
|
||||
*/
|
||||
nscoord mMainDeltaSize;
|
||||
nscoord mMainMinSize;
|
||||
nscoord mMainMaxSize;
|
||||
nscoord mCrossMinSize;
|
||||
nscoord mCrossMaxSize;
|
||||
};
|
||||
|
||||
struct ComputedFlexLineInfo
|
||||
{
|
||||
nsTArray<ComputedFlexItemInfo> mItems;
|
||||
nscoord mCrossSize;
|
||||
nscoord mFirstBaselineOffset;
|
||||
nscoord mLastBaselineOffset;
|
||||
enum GrowthState {
|
||||
UNCHANGED,
|
||||
SHRINKING,
|
||||
GROWING,
|
||||
} mGrowthState;
|
||||
};
|
||||
|
||||
struct ComputedFlexContainerInfo
|
||||
{
|
||||
nsTArray<ComputedFlexLineInfo> mLines;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the rendering object used for laying out elements with
|
||||
@@ -111,8 +159,7 @@ public:
|
||||
* Helper function to calculate packing space and initial offset of alignment
|
||||
* subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
|
||||
* space-between, space-around, and space-evenly.
|
||||
*
|
||||
* @param aNumThingsToPack Number of alignment subjects.
|
||||
* * @param aNumThingsToPack Number of alignment subjects.
|
||||
* @param aAlignVal Value for align-self or justify-self.
|
||||
* @param aFirstSubjectOffset Outparam for first subject offset.
|
||||
* @param aNumPackingSpacesRemaining Outparam for number of equal-sized
|
||||
@@ -127,6 +174,31 @@ public:
|
||||
uint32_t* aNumPackingSpacesRemaining,
|
||||
nscoord* aPackingSpaceRemaining);
|
||||
|
||||
/**
|
||||
* This property is created by a call to
|
||||
* nsFlexContainerFrame::GetFlexFrameWithComputedInfo.
|
||||
*/
|
||||
NS_DECLARE_FRAME_PROPERTY_DELETABLE(FlexContainerInfo, ComputedFlexContainerInfo)
|
||||
/**
|
||||
* This function should only be called on a nsFlexContainerFrame
|
||||
* that has just been returned by a call to
|
||||
* GetFlexFrameWithComputedInfo.
|
||||
*/
|
||||
const ComputedFlexContainerInfo* GetFlexContainerInfo()
|
||||
{
|
||||
const ComputedFlexContainerInfo* info = GetProperty(FlexContainerInfo());
|
||||
MOZ_ASSERT(info, "Property generation wasn't requested.");
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return aFrame as a flex frame after ensuring it has computed flex info.
|
||||
* @return nullptr if aFrame is null or doesn't have a flex frame
|
||||
* as its content insertion frame.
|
||||
* @note this might destroy layout/style data since it may flush layout.
|
||||
*/
|
||||
static nsFlexContainerFrame* GetFlexFrameWithComputedInfo(nsIFrame* aFrame);
|
||||
|
||||
protected:
|
||||
// Protected constructor & destructor
|
||||
explicit nsFlexContainerFrame(nsStyleContext* aContext)
|
||||
|
||||
@@ -323,8 +323,11 @@ FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORD
|
||||
// 'display:-webkit-{inline-}box' container.
|
||||
FRAME_STATE_BIT(FlexContainer, 21, NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX)
|
||||
|
||||
// True iff computed flex values should be generated on the next reflow
|
||||
FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_GENERATE_COMPUTED_VALUES)
|
||||
|
||||
// True if the container has no flex items; may lie if there is a pending reflow
|
||||
FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
|
||||
FRAME_STATE_BIT(FlexContainer, 23, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
|
||||
|
||||
// == Frame state bits that apply to grid container frames ====================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user