summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dom/base/Element.cpp18
-rw-r--r--dom/base/nsDocument.cpp21
-rw-r--r--dom/base/nsINode.cpp4
-rw-r--r--layout/base/RestyleManagerBase.cpp63
-rw-r--r--layout/base/nsCSSFrameConstructor.cpp15
-rw-r--r--layout/base/nsChangeHint.h55
-rw-r--r--layout/base/nsPresContext.cpp9
-rw-r--r--layout/base/nsPresContext.h22
-rw-r--r--layout/generic/nsFlexContainerFrame.cpp155
-rw-r--r--layout/generic/nsFlexContainerFrame.h15
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-1-ref.html18
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-1a.html23
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-1b.html23
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-1c.html19
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2-ref.html15
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2a.html26
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2b.html26
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2c.html24
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2d.html24
-rw-r--r--layout/reftests/scrolling/propagated-overflow-style-2e.html15
-rw-r--r--layout/reftests/scrolling/reftest.list10
-rw-r--r--layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001-ref.html16
-rw-r--r--layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001.html47
-rw-r--r--layout/reftests/w3c-css/submitted/flexbox/reftest.list2
-rw-r--r--layout/reftests/xul/css-flex-1-ref.html18
-rw-r--r--layout/reftests/xul/css-flex-1.xul153
-rw-r--r--layout/reftests/xul/reftest.list2
-rw-r--r--layout/style/nsStyleStruct.cpp23
-rw-r--r--layout/style/test/mochitest.ini1
-rw-r--r--layout/style/test/test_dynamic_change_causing_reflow.html107
-rw-r--r--layout/style/test/test_viewport_scrollbar_causing_reflow.html125
31 files changed, 1020 insertions, 74 deletions
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
index 8b9808aa3..092755590 100644
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1844,6 +1844,24 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
SetParentIsContent(false);
}
+#ifdef DEBUG
+ // If we can get access to the PresContext, then we sanity-check that
+ // we're not leaving behind a pointer to ourselves as the PresContext's
+ // cached provider of the viewport's scrollbar styles.
+ if (document) {
+ nsIPresShell* presShell = document->GetShell();
+ if (presShell) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (presContext) {
+ MOZ_ASSERT(this !=
+ presContext->GetViewportScrollbarStylesOverrideNode(),
+ "Leaving behind a raw pointer to this node (as having "
+ "propagated scrollbar styles) - that's dangerous...");
+ }
+ }
+ }
+#endif
+
// Ensure that CSS transitions don't continue on an element at a
// different place in the tree (even if reinserted before next
// animation refresh).
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index d5954a62c..fd3b52948 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2054,10 +2054,17 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
mFirstChild = content->GetNextSibling();
}
mChildren.RemoveChildAt(i);
+ if (content == mCachedRootElement) {
+ // Immediately clear mCachedRootElement, now that it's been removed
+ // from mChildren, so that GetRootElement() will stop returning this
+ // now-stale value.
+ mCachedRootElement = nullptr;
+ }
nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
content->UnbindFromTree();
}
- mCachedRootElement = nullptr;
+ MOZ_ASSERT(!mCachedRootElement,
+ "After removing all children, there should be no root elem");
}
mInUnlinkOrDeletion = oldVal;
@@ -3913,8 +3920,18 @@ nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
DestroyElementMaps();
}
- doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
+ // Preemptively clear mCachedRootElement, since we may be about to remove it
+ // from our child list, and we don't want to return this maybe-obsolete value
+ // from any GetRootElement() calls that happen inside of doRemoveChildAt().
+ // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
+ // GetRootElement() calls until after it's removed the child from mChildren.
+ // Any call before that point would restore this soon-to-be-obsolete cached
+ // answer, and our clearing here would be fruitless.)
mCachedRootElement = nullptr;
+ doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
+ MOZ_ASSERT(mCachedRootElement != oldKid,
+ "Stale pointer in mCachedRootElement, after we tried to clear it "
+ "(maybe somebody called GetRootElement() too early?)");
}
void
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
index 3a649a61d..715ca93ea 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1907,6 +1907,10 @@ void
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
nsIContent* aKid, nsAttrAndChildArray& aChildArray)
{
+ // NOTE: This function must not trigger any calls to
+ // nsIDocument::GetRootElement() calls until *after* it has removed aKid from
+ // aChildArray. Any calls before then could potentially restore a stale
+ // value for our cached root element, per note in nsDocument::RemoveChildAt().
NS_PRECONDITION(aKid && aKid->GetParentNode() == this &&
aKid == GetChildAt(aIndex) &&
IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");
diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp
index 9a5ce43eb..d96d9dbbb 100644
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -154,7 +154,7 @@ RestyleManagerBase::ChangeHintToString(nsChangeHint aHint)
"NeutralChange", "InvalidateRenderingObservers",
"ReflowChangesSizeOrPosition", "UpdateComputedBSize",
"UpdateUsesOpacity", "UpdateBackgroundPosition",
- "AddOrRemoveTransform"
+ "AddOrRemoveTransform", "CSSOverflowChange",
};
static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
"Name list doesn't match change hints.");
@@ -1070,6 +1070,67 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
FramePropertyTable* propTable = presContext->PropertyTable();
nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
+ // Handle nsChangeHint_CSSOverflowChange, by either updating the
+ // scrollbars on the viewport, or upgrading the change hint to frame-reconstruct.
+ for (nsStyleChangeData& data : aChangeList) {
+ if (data.mHint & nsChangeHint_CSSOverflowChange) {
+ data.mHint &= ~nsChangeHint_CSSOverflowChange;
+ bool doReconstruct = true; // assume the worst
+
+ // Only bother with this if we're html/body, since:
+ // (a) It'd be *expensive* to reframe these particular nodes. They're
+ // at the root, so reframing would mean rebuilding the world.
+ // (b) It's often *unnecessary* to reframe for "overflow" changes on
+ // these particular nodes. In general, the only reason we reframe
+ // for "overflow" changes is so we can construct (or destroy) a
+ // scrollframe & scrollbars -- and the html/body nodes often don't
+ // need their own scrollframe/scrollbars because they coopt the ones
+ // on the viewport (which always exist). So depending on whether
+ // that's happening, we can skip the reframe for these nodes.
+ if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body,
+ nsGkAtoms::html)) {
+ // If the restyled element provided/provides the scrollbar styles for
+ // the viewport before and/or after this restyle, AND it's not coopting
+ // that responsibility from some other element (which would need
+ // reconstruction to make its own scrollframe now), THEN: we don't need
+ // to reconstruct - we can just reflow, because no scrollframe is being
+ // added/removed.
+ nsIContent* prevOverrideNode =
+ presContext->GetViewportScrollbarStylesOverrideNode();
+ nsIContent* newOverrideNode =
+ presContext->UpdateViewportScrollbarStylesOverride();
+
+ if (data.mContent == prevOverrideNode ||
+ data.mContent == newOverrideNode) {
+ // If we get here, the restyled element provided the scrollbar styles
+ // for viewport before this restyle, OR it will provide them after.
+ if (!prevOverrideNode || !newOverrideNode ||
+ prevOverrideNode == newOverrideNode) {
+ // If we get here, the restyled element is NOT replacing (or being
+ // replaced by) some other element as the viewport's
+ // scrollbar-styles provider. (If it were, we'd potentially need to
+ // reframe to create a dedicated scrollframe for whichever element
+ // is being booted from providing viewport scrollbar styles.)
+ //
+ // Under these conditions, we're OK to assume that this "overflow"
+ // change only impacts the root viewport's scrollframe, which
+ // already exists, so we can simply reflow instead of reframing.
+ // When requesting this reflow, we send the exact same change hints
+ // that "width" and "height" would send (since conceptually,
+ // adding/removing scrollbars is like changing the available
+ // space).
+ data.mHint |= (nsChangeHint_ReflowHintsForISizeChange |
+ nsChangeHint_ReflowHintsForBSizeChange);
+ doReconstruct = false;
+ }
+ }
+ }
+ if (doReconstruct) {
+ data.mHint |= nsChangeHint_ReconstructFrame;
+ }
+ }
+ }
+
// Make sure to not rebuild quote or counter lists while we're
// processing restyles
frameConstructor->BeginUpdate();
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index f8c7f52a9..767298b85 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8246,11 +8246,19 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
*aDestroyedFramesFor = aChild;
}
+ nsPresContext* presContext = mPresShell->GetPresContext();
+ MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
+
if (aChild->IsHTMLElement(nsGkAtoms::body) ||
(!aContainer && aChild->IsElement())) {
- // This might be the element we propagated viewport scrollbar
- // styles from. Recompute those.
- mPresShell->GetPresContext()->UpdateViewportScrollbarStylesOverride();
+ // We might be removing the element that we propagated viewport scrollbar
+ // styles from. Recompute those. (This clause covers two of the three
+ // possible scrollbar-propagation sources: the <body> [as aChild or a
+ // descendant] and the root node. The other possible scrollbar-propagation
+ // source is a fullscreen element, and we have code elsewhere to update
+ // scrollbars after fullscreen elements are removed -- specifically, it's
+ // part of the fullscreen cleanup code called by Element::UnbindFromTree.)
+ presContext->UpdateViewportScrollbarStylesOverride();
}
// XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
@@ -8316,7 +8324,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
ClearDisplayContentsIn(aChild, aContainer);
}
- nsPresContext* presContext = mPresShell->GetPresContext();
#ifdef MOZ_XUL
if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
childFrame, CONTENT_REMOVED)) {
diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h
index 318b84840..eb2709de6 100644
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -217,6 +217,16 @@ enum nsChangeHint {
*/
nsChangeHint_AddOrRemoveTransform = 1 << 27,
+ /**
+ * Indicates that the overflow-x and/or overflow-y property changed.
+ *
+ * In most cases, this is equivalent to nsChangeHint_ReconstructFrame. But
+ * in some special cases where the change is really targeting the viewport's
+ * scrollframe, this is instead equivalent to nsChangeHint_AllReflowHints
+ * (because the viewport always has an associated scrollframe).
+ */
+ nsChangeHint_CSSOverflowChange = 1 << 28,
+
// IMPORTANT NOTE: When adding new hints, consider whether you need
// to add them to NS_HintsNotHandledForDescendantsIn() below. Please
// also add them to RestyleManager::ChangeHintToString and modify
@@ -225,7 +235,7 @@ enum nsChangeHint {
/**
* Dummy hint value for all hints. It exists for compile time check.
*/
- nsChangeHint_AllHints = (1 << 28) - 1,
+ nsChangeHint_AllHints = (1 << 29) - 1,
};
// Redefine these operators to return nothing. This will catch any use
@@ -306,6 +316,7 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight)
nsChangeHint_UpdatePostTransformOverflow | \
nsChangeHint_UpdateParentOverflow | \
nsChangeHint_ChildrenOnlyTransform | \
+ nsChangeHint_CSSOverflowChange | \
nsChangeHint_RecomputePosition | \
nsChangeHint_UpdateContainingBlock | \
nsChangeHint_AddOrRemoveTransform | \
@@ -374,6 +385,48 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint)
nsChangeHint_ClearAncestorIntrinsics | \
nsChangeHint_ClearDescendantIntrinsics | \
nsChangeHint_NeedDirtyReflow)
+
+// Below are the change hints that we send for ISize & BSize changes.
+// Each is similar to nsChangeHint_AllReflowHints with a few changes.
+
+// * For an ISize change, we send nsChangeHint_AllReflowHints, with two bits
+// excluded: nsChangeHint_ClearDescendantIntrinsics (because an ancestor's
+// inline-size change can't affect descendant intrinsic sizes), and
+// nsChangeHint_NeedDirtyReflow (because ISize changes don't need to *force*
+// all descendants to reflow).
+#define nsChangeHint_ReflowHintsForISizeChange \
+ nsChangeHint(nsChangeHint_AllReflowHints & \
+ ~(nsChangeHint_ClearDescendantIntrinsics | \
+ nsChangeHint_NeedDirtyReflow))
+
+// * For a BSize change, we send almost the same hints as for ISize changes,
+// with one extra: nsChangeHint_UpdateComputedBSize. We need this hint because
+// BSize changes CAN affect descendant intrinsic sizes, due to replaced
+// elements with percentage BSizes in descendants which also have percentage
+// BSizes. nsChangeHint_UpdateComputedBSize clears intrinsic sizes for frames
+// that have such replaced elements. (We could instead send
+// nsChangeHint_ClearDescendantIntrinsics, but that's broader than we need.)
+//
+// NOTE: You might think that BSize changes could exclude
+// nsChangeHint_ClearAncestorIntrinsics (which is inline-axis specific), but we
+// do need to send it, to clear cached results from CSS Flex measuring reflows.
+#define nsChangeHint_ReflowHintsForBSizeChange \
+ nsChangeHint((nsChangeHint_AllReflowHints | \
+ nsChangeHint_UpdateComputedBSize) & \
+ ~(nsChangeHint_ClearDescendantIntrinsics | \
+ nsChangeHint_NeedDirtyReflow))
+
+// * For changes to the float area of an already-floated element, we need all
+// reflow hints, but not the ones that apply to descendants.
+// Our descendants aren't impacted when our float area only changes
+// placement but not size/shape. (e.g. if we change which side we float to).
+// But our ancestors/siblings are potentially impacted, so we need to send
+// the non-descendant reflow hints.
+#define nsChangeHint_ReflowHintsForFloatAreaChange \
+ nsChangeHint(nsChangeHint_AllReflowHints & \
+ ~(nsChangeHint_ClearDescendantIntrinsics | \
+ nsChangeHint_NeedDirtyReflow))
+
#define NS_STYLE_HINT_REFLOW \
nsChangeHint(NS_STYLE_HINT_VISUAL | nsChangeHint_AllReflowHints)
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index d9f7b368c..4a54a8432 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -208,6 +208,7 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
mTextZoom(1.0), mFullZoom(1.0), mOverrideDPPX(0.0),
mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
mPageSize(-1, -1), mPPScale(1.0f),
+ mViewportScrollbarOverrideNode(nullptr),
mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
mImageAnimationModePref(imgIContainer::kNormalAnimMode),
mAllInvalidated(false),
@@ -1423,10 +1424,10 @@ nsPresContext::UpdateViewportScrollbarStylesOverride()
// Start off with our default styles, and then update them as needed.
mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_AUTO,
NS_STYLE_OVERFLOW_AUTO);
- nsIContent* propagatedFrom = nullptr;
+ mViewportScrollbarOverrideNode = nullptr;
// Don't propagate the scrollbar state in printing or print preview.
if (!IsPaginated()) {
- propagatedFrom =
+ mViewportScrollbarOverrideNode =
GetPropagatedScrollbarStylesForViewport(this, &mViewportStyleScrollbar);
}
@@ -1438,13 +1439,13 @@ nsPresContext::UpdateViewportScrollbarStylesOverride()
// the styles are from, so that the state of those elements is not
// affected across fullscreen change.
if (fullscreenElement != document->GetRootElement() &&
- fullscreenElement != propagatedFrom) {
+ fullscreenElement != mViewportScrollbarOverrideNode) {
mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
NS_STYLE_OVERFLOW_HIDDEN);
}
}
- return propagatedFrom;
+ return mViewportScrollbarOverrideNode;
}
bool
diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
index 4fdc60a2e..d8f876291 100644
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -719,7 +719,18 @@ public:
* it was propagated from.
*/
nsIContent* UpdateViewportScrollbarStylesOverride();
- const ScrollbarStyles& GetViewportScrollbarStylesOverride()
+
+ /**
+ * Returns the cached result from the last call to
+ * UpdateViewportScrollbarStylesOverride() -- i.e. return the node
+ * whose scrollbar styles we have propagated to the viewport (or nullptr if
+ * there is no such node).
+ */
+ nsIContent* GetViewportScrollbarStylesOverrideNode() const {
+ return mViewportScrollbarOverrideNode;
+ }
+
+ const ScrollbarStyles& GetViewportScrollbarStylesOverride() const
{
return mViewportStyleScrollbar;
}
@@ -1310,7 +1321,16 @@ protected:
nscolor mBodyTextColor;
+ // This is a non-owning pointer. May be null. If non-null, it's guaranteed
+ // to be pointing to a node that's still alive, because we'll reset it in
+ // UpdateViewportScrollbarStylesOverride() as part of the cleanup code
+ // when this node is removed from the document. (For <body> and the root node,
+ // this call happens in nsCSSFrameConstructor::ContentRemoved(). For
+ // fullscreen elements, it happens in the fullscreen-specific cleanup
+ // invoked by Element::UnbindFromTree().)
+ nsIContent* MOZ_NON_OWNING_REF mViewportScrollbarOverrideNode;
ScrollbarStyles mViewportStyleScrollbar;
+
uint8_t mFocusRingWidth;
bool mExistThrottledUpdates;
diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp
index b61024324..3818d3cb7 100644
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -33,6 +33,8 @@ typedef nsFlexContainerFrame::FlexItem FlexItem;
typedef nsFlexContainerFrame::FlexLine FlexLine;
typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
typedef nsFlexContainerFrame::StrutInfo StrutInfo;
+typedef nsFlexContainerFrame::CachedMeasuringReflowResult
+ CachedMeasuringReflowResult;
static mozilla::LazyLogModule gFlexContainerLog("nsFlexContainerFrame");
@@ -1756,6 +1758,108 @@ nsFlexContainerFrame::
}
}
+/**
+ * A cached result for a measuring reflow.
+ *
+ * Right now we only need to cache the available size and the computed height
+ * for checking that the reflow input is valid, and the height and the ascent
+ * to be used. This can be extended later if needed.
+ *
+ * The assumption here is that a given flex item measurement won't change until
+ * either the available size or computed height changes, or the flex container
+ * intrinsic size is marked as dirty (due to a style or DOM change).
+ *
+ * In particular the computed height may change between measuring reflows due to
+ * how the mIsFlexContainerMeasuringReflow flag affects size computation (see
+ * bug 1336708).
+ *
+ * Caching it prevents us from doing exponential reflows in cases of deeply
+ * nested flex and scroll frames.
+ *
+ * We store them in the frame property table for simplicity.
+ */
+class nsFlexContainerFrame::CachedMeasuringReflowResult
+{
+ // Members that are part of the cache key:
+ const LogicalSize mAvailableSize;
+ const nscoord mComputedHeight;
+
+ // Members that are part of the cache value:
+ const nscoord mHeight;
+ const nscoord mAscent;
+
+public:
+ CachedMeasuringReflowResult(const ReflowInput& aReflowInput,
+ const ReflowOutput& aDesiredSize)
+ : mAvailableSize(aReflowInput.AvailableSize())
+ , mComputedHeight(aReflowInput.ComputedHeight())
+ , mHeight(aDesiredSize.Height())
+ , mAscent(aDesiredSize.BlockStartAscent())
+ {}
+
+ bool IsValidFor(const ReflowInput& aReflowInput) const {
+ return mAvailableSize == aReflowInput.AvailableSize() &&
+ mComputedHeight == aReflowInput.ComputedHeight();
+ }
+
+ nscoord Height() const { return mHeight; }
+
+ nscoord Ascent() const { return mAscent; }
+};
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(CachedFlexMeasuringReflow,
+ CachedMeasuringReflowResult);
+
+const CachedMeasuringReflowResult&
+nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem(
+ FlexItem& aItem,
+ nsPresContext* aPresContext,
+ ReflowInput& aChildReflowInput)
+{
+ const FrameProperties props = aItem.Frame()->Properties();
+ if (const auto* cachedResult = props.Get(CachedFlexMeasuringReflow())) {
+ if (cachedResult->IsValidFor(aChildReflowInput)) {
+ return *cachedResult;
+ }
+ }
+
+ ReflowOutput childDesiredSize(aChildReflowInput);
+ nsReflowStatus childReflowStatus;
+
+ const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
+ ReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, aChildReflowInput,
+ 0, 0, flags, childReflowStatus);
+ aItem.SetHadMeasuringReflow();
+
+ // XXXdholbert Once we do pagination / splitting, we'll need to actually
+ // handle incomplete childReflowStatuses. But for now, we give our kids
+ // unconstrained available height, which means they should always complete.
+ MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
+ "We gave flex item unconstrained available height, so it "
+ "should be complete");
+
+ // Tell the child we're done with its initial reflow.
+ // (Necessary for e.g. GetBaseline() to work below w/out asserting)
+ FinishReflowChild(aItem.Frame(), aPresContext,
+ childDesiredSize, &aChildReflowInput, 0, 0, flags);
+
+ auto result =
+ new CachedMeasuringReflowResult(aChildReflowInput, childDesiredSize);
+
+ props.Set(CachedFlexMeasuringReflow(), result);
+ return *result;
+}
+
+/* virtual */ void
+nsFlexContainerFrame::MarkIntrinsicISizesDirty()
+{
+ for (nsIFrame* childFrame : mFrames) {
+ childFrame->Properties().Delete(CachedFlexMeasuringReflow());
+ }
+ nsContainerFrame::MarkIntrinsicISizesDirty();
+}
+
nscoord
nsFlexContainerFrame::
MeasureFlexItemContentHeight(nsPresContext* aPresContext,
@@ -1783,27 +1887,15 @@ nsFlexContainerFrame::
childRIForMeasuringHeight.SetVResize(true);
}
- ReflowOutput childDesiredSize(childRIForMeasuringHeight);
- nsReflowStatus childReflowStatus;
- const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
- ReflowChild(aFlexItem.Frame(), aPresContext,
- childDesiredSize, childRIForMeasuringHeight,
- 0, 0, flags, childReflowStatus);
-
- MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
- "We gave flex item unconstrained available height, so it "
- "should be complete");
-
- FinishReflowChild(aFlexItem.Frame(), aPresContext,
- childDesiredSize, &childRIForMeasuringHeight,
- 0, 0, flags);
+ const CachedMeasuringReflowResult& reflowResult =
+ MeasureAscentAndHeightForFlexItem(aFlexItem, aPresContext,
+ childRIForMeasuringHeight);
- aFlexItem.SetHadMeasuringReflow();
- aFlexItem.SetAscent(childDesiredSize.BlockStartAscent());
+ aFlexItem.SetAscent(reflowResult.Ascent());
// Subtract border/padding in vertical axis, to get _just_
// the effective computed value of the "height" property.
- nscoord childDesiredHeight = childDesiredSize.Height() -
+ nscoord childDesiredHeight = reflowResult.Height() -
childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
return std::max(0, childDesiredHeight);
@@ -3959,25 +4051,10 @@ nsFlexContainerFrame::SizeItemInCrossAxis(
// whether any of its ancestors are being resized).
aChildReflowInput.SetVResize(true);
}
- ReflowOutput childDesiredSize(aChildReflowInput);
- nsReflowStatus childReflowStatus;
- const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
- ReflowChild(aItem.Frame(), aPresContext,
- childDesiredSize, aChildReflowInput,
- 0, 0, flags, childReflowStatus);
- aItem.SetHadMeasuringReflow();
-
- // XXXdholbert Once we do pagination / splitting, we'll need to actually
- // handle incomplete childReflowStatuses. But for now, we give our kids
- // unconstrained available height, which means they should always complete.
- MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
- "We gave flex item unconstrained available height, so it "
- "should be complete");
- // Tell the child we're done with its initial reflow.
- // (Necessary for e.g. GetBaseline() to work below w/out asserting)
- FinishReflowChild(aItem.Frame(), aPresContext,
- childDesiredSize, &aChildReflowInput, 0, 0, flags);
+ // Potentially reflow the item, and get the sizing info.
+ const CachedMeasuringReflowResult& reflowResult =
+ MeasureAscentAndHeightForFlexItem(aItem, aPresContext, aChildReflowInput);
// Save the sizing info that we learned from this reflow
// -----------------------------------------------------
@@ -3989,7 +4066,7 @@ nsFlexContainerFrame::SizeItemInCrossAxis(
// so we don't bother with making aAxisTracker pick the cross-axis component
// for us.)
nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
- if (childDesiredSize.Height() < crossAxisBorderPadding) {
+ if (reflowResult.Height() < crossAxisBorderPadding) {
// Child's requested size isn't large enough for its border/padding!
// This is OK for the trivial nsFrame::Reflow() impl, but other frame
// classes should know better. So, if we get here, the child had better be
@@ -4002,10 +4079,10 @@ nsFlexContainerFrame::SizeItemInCrossAxis(
aItem.SetCrossSize(0);
} else {
// (normal case)
- aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding);
+ aItem.SetCrossSize(reflowResult.Height() - crossAxisBorderPadding);
}
- aItem.SetAscent(childDesiredSize.BlockStartAscent());
+ aItem.SetAscent(reflowResult.Ascent());
}
void
@@ -4295,7 +4372,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
LogicalSize availSize = aReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
ReflowInput childReflowInput(aPresContext, aReflowInput,
- item->Frame(), availSize);
+ item->Frame(), availSize);
if (!sizeOverride) {
// Directly override the computed main-size, by tweaking reflow state:
if (aAxisTracker.IsMainAxisHorizontal()) {
diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h
index 22b420d85..459ae8e20 100644
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -56,6 +56,7 @@ public:
class FlexLine;
class FlexboxAxisTracker;
struct StrutInfo;
+ class CachedMeasuringReflowResult;
// nsIFrame overrides
void Init(nsIContent* aContent,
@@ -66,6 +67,8 @@ public:
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
+ void MarkIntrinsicISizesDirty() override;
+
virtual void Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
@@ -195,6 +198,18 @@ protected:
const FlexboxAxisTracker& aAxisTracker);
/**
+ * This method gets a cached measuring reflow for a flex item, or does it and
+ * caches it.
+ *
+ * This avoids exponential reflows, see the comment on
+ * CachedMeasuringReflowResult.
+ */
+ const CachedMeasuringReflowResult& MeasureAscentAndHeightForFlexItem(
+ FlexItem& aItem,
+ nsPresContext* aPresContext,
+ ReflowInput& aChildReflowInput);
+
+ /**
* This method performs a "measuring" reflow to get the content height of
* aFlexItem.Frame() (treating it as if it had auto-height), & returns the
* resulting height.
diff --git a/layout/reftests/scrolling/propagated-overflow-style-1-ref.html b/layout/reftests/scrolling/propagated-overflow-style-1-ref.html
new file mode 100644
index 000000000..7c2b1b315
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-1-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Reference case with body and html *independently* scrollable.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ body {
+ overflow: scroll;
+ }
+ </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-1a.html b/layout/reftests/scrolling/propagated-overflow-style-1a.html
new file mode 100644
index 000000000..b5115d36f
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-1a.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with body and html *independently* scrollable,
+ with body's "overflow" set dynamically.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.body.style.overflow = "scroll";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-1b.html b/layout/reftests/scrolling/propagated-overflow-style-1b.html
new file mode 100644
index 000000000..4608b87d6
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-1b.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with body and html *independently* scrollable,
+ with html's "overflow" set dynamically.
+ </title>
+ <style>
+ body {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.documentElement.style.overflow = "scroll";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-1c.html b/layout/reftests/scrolling/propagated-overflow-style-1c.html
new file mode 100644
index 000000000..11809915a
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-1c.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with body and html *independently* scrollable,
+ with both html & body's "overflow" set dynamically.
+ </title>
+ <script>
+ function doTest() {
+ document.documentElement.style.overflow = "scroll";
+ document.body.style.overflow = "scroll";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2-ref.html b/layout/reftests/scrolling/propagated-overflow-style-2-ref.html
new file mode 100644
index 000000000..20c3b8ae5
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Reference case with the root viewport scrollable, via styles on html node.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2a.html b/layout/reftests/scrolling/propagated-overflow-style-2a.html
new file mode 100644
index 000000000..250bedd6c
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2a.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with only one of [html,body] being scrollable,
+ after body's "overflow" is reset dynamically.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ body {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.body.style.overflow = "visible";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2b.html b/layout/reftests/scrolling/propagated-overflow-style-2b.html
new file mode 100644
index 000000000..c94ddedb2
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2b.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with only one of [html,body] being scrollable,
+ after html's "overflow" is reset dynamically.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ body {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.documentElement.style.overflow = "visible";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2c.html b/layout/reftests/scrolling/propagated-overflow-style-2c.html
new file mode 100644
index 000000000..0ceb1f21a
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2c.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with only one of [html,body] being scrollable,
+ with their "overflow" styles being dynamically swapped.
+ </title>
+ <style>
+ html {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.documentElement.style.overflow = "visible";
+ document.body.style.overflow = "scroll";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2d.html b/layout/reftests/scrolling/propagated-overflow-style-2d.html
new file mode 100644
index 000000000..3353a3374
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2d.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>
+ Testcase with only one of [html,body] being scrollable,
+ with their "overflow" styles being dynamically swapped.
+ </title>
+ <style>
+ body {
+ overflow: scroll;
+ }
+ </style>
+ <script>
+ function doTest() {
+ document.documentElement.style.overflow = "scroll";
+ document.body.style.overflow = "visible";
+ document.documentElement.removeAttribute("class");
+ }
+ window.addEventListener("MozReftestInvalidate", doTest);
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/propagated-overflow-style-2e.html b/layout/reftests/scrolling/propagated-overflow-style-2e.html
new file mode 100644
index 000000000..f9105185b
--- /dev/null
+++ b/layout/reftests/scrolling/propagated-overflow-style-2e.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Testcase with the root viewport scrollable, via styles on body node.
+ </title>
+ <style>
+ body {
+ overflow: scroll;
+ }
+ </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/reftests/scrolling/reftest.list b/layout/reftests/scrolling/reftest.list
index db1b81db6..43997ced7 100644
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -85,3 +85,13 @@ fuzzy-if(asyncPan&&!layersGPUAccelerated,102,2420) == frame-scrolling-attr-2.htm
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
!= fractional-scroll-area-invalidation.html about:blank
+
+# Tests for "overflow" styles that may be propagated to the viewport:
+== propagated-overflow-style-1a.html propagated-overflow-style-1-ref.html
+== propagated-overflow-style-1b.html propagated-overflow-style-1-ref.html
+== propagated-overflow-style-1c.html propagated-overflow-style-1-ref.html
+== propagated-overflow-style-2a.html propagated-overflow-style-2-ref.html
+== propagated-overflow-style-2b.html propagated-overflow-style-2-ref.html
+== propagated-overflow-style-2c.html propagated-overflow-style-2-ref.html
+== propagated-overflow-style-2d.html propagated-overflow-style-2-ref.html
+== propagated-overflow-style-2e.html propagated-overflow-style-2-ref.html
diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001-ref.html
new file mode 100644
index 000000000..2473417b8
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<style>
+html, body { margin: 0; padding: 0; }
+
+.content {
+ margin-top: 100px;
+ width: 200px;
+ height: 200px;
+ background: blue;
+}
+</style>
+<body>
+ <div class="content"></div>
+</body>
diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001.html
new file mode 100644
index 000000000..b6e2fdff0
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-align-items-center-nested-001.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: Flexbox nested containers with align-items: center and flexible items</title>
+<link rel="match" href="flexbox-align-items-center-nested-001-ref.html">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#align-items-property">
+<style>
+html, body { margin: 0; padding: 0; }
+body {
+ height: 400px;
+ position: relative;
+}
+
+.container-0 {
+ display: flex;
+ position: absolute;
+ height: 100%;
+ flex-direction: column;
+}
+
+.container-1 {
+ flex: 1 0 auto;
+ display: flex;
+ align-items: center;
+}
+
+.container-2 {
+ height: 100%;
+ display: flex;
+ align-items: center;
+}
+
+.content {
+ width: 200px;
+ height: 200px;
+ background: blue;
+}
+</style>
+<body>
+ <div class="container-0">
+ <div class="container-1">
+ <div class="container-2">
+ <div class="content"></div>
+ </div>
+ </div>
+ </div>
+</body>
diff --git a/layout/reftests/w3c-css/submitted/flexbox/reftest.list b/layout/reftests/w3c-css/submitted/flexbox/reftest.list
index a623a0b59..fd8bfccc9 100644
--- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list
+++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list
@@ -39,6 +39,8 @@ fuzzy-if(Android,158,32) == flexbox-align-self-vert-rtl-001.xhtml flexbox-align
== flexbox-align-self-vert-rtl-003.xhtml flexbox-align-self-vert-rtl-003-ref.xhtml
== flexbox-align-self-vert-rtl-004.xhtml flexbox-align-self-vert-rtl-004-ref.xhtml
+== flexbox-align-items-center-nested-001.html flexbox-align-items-center-nested-001-ref.html
+
# Tests for computing the baseline of a flex container
== flexbox-baseline-align-self-baseline-horiz-001.html flexbox-baseline-align-self-baseline-horiz-001-ref.html
== flexbox-baseline-align-self-baseline-vert-001.html flexbox-baseline-align-self-baseline-vert-001-ref.html
diff --git a/layout/reftests/xul/css-flex-1-ref.html b/layout/reftests/xul/css-flex-1-ref.html
new file mode 100644
index 000000000..a47eb8e9c
--- /dev/null
+++ b/layout/reftests/xul/css-flex-1-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ body { margin: 0 }
+ div.ref {
+ border: 1px solid black;
+ box-sizing: border-box;
+ background: green;
+ height: 50px;
+ width: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div class="ref"></div>
+</body>
+</html>
diff --git a/layout/reftests/xul/css-flex-1.xul b/layout/reftests/xul/css-flex-1.xul
new file mode 100644
index 000000000..7955373dd
--- /dev/null
+++ b/layout/reftests/xul/css-flex-1.xul
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
+<head>
+<link rel="icon" href="/mozilla-central/static/hgicon.png" type="image/png" />
+<meta name="robots" content="index, nofollow"/>
+<link rel="stylesheet" href="/mozilla-central/static/style-gitweb.css" type="text/css" />
+
+<style type="text/css">
+div.feed {
+ float: right;
+}
+a img {
+ border-width: 0px;
+}
+div.log_link {
+ width: 80px;
+ background-color: white;
+}
+
+div.log_body {
+ padding-left: 96px;
+}
+</style>
+<script type="text/javascript" src="/mozilla-central/static/mercurial.js"></script>
+
+<link rel="stylesheet" href="/mozilla-central/highlightcss" type="text/css" />
+<title>mozilla-central: layout/reftests/xul/css-flex-1.xul@67bbef772796</title>
+<link rel="alternate" type="application/atom+xml"
+ href="/mozilla-central/atom-log" title="Atom feed for mozilla-central"/>
+<link rel="alternate" type="application/rss+xml"
+ href="/mozilla-central/rss-log" title="RSS feed for mozilla-central"/>
+</head>
+<body>
+
+<div class="page_header">
+<div class="logo"> <a href="https://developer.mozilla.org/en/docs/Mercurial"> <img src="/mozilla-central/static/moz-logo-bw-rgb.svg" alt="mercurial" /> </a> </div>
+<a href="/">Mercurial</a> &gt; <a href="/mozilla-central">mozilla-central</a> / file revision / layout/reftests/xul/css-flex-1.xul@67bbef772796
+</div>
+
+<div class="page_nav">
+<div>
+<a href="/mozilla-central/summary">summary</a> |
+<a href="/mozilla-central/shortlog">shortlog</a> |
+<a href="/mozilla-central/log">changelog</a> |
+<a href="/mozilla-central/pushloghtml">pushlog</a> |
+<a href="/mozilla-central/graph">graph</a> |
+<a href="/mozilla-central/tags">tags</a> |
+<a href="/mozilla-central/bookmarks">bookmarks</a> |
+<a href="/mozilla-central/branches">branches</a> |
+<a href="/mozilla-central/file/67bbef772796/layout/reftests/xul/">files</a> |
+<a href="/mozilla-central/rev/67bbef772796">changeset</a> |
+file |
+<a href="/mozilla-central/file/tip/layout/reftests/xul/css-flex-1.xul">latest</a> |
+<a href="/mozilla-central/log/67bbef772796/layout/reftests/xul/css-flex-1.xul">revisions</a> |
+<a href="/mozilla-central/annotate/67bbef772796/layout/reftests/xul/css-flex-1.xul">annotate</a> |
+<a href="/mozilla-central/diff/67bbef772796/layout/reftests/xul/css-flex-1.xul">diff</a> |
+<a href="/mozilla-central/comparison/67bbef772796/layout/reftests/xul/css-flex-1.xul">comparison</a> |
+<a href="/mozilla-central/raw-file/67bbef772796/layout/reftests/xul/css-flex-1.xul">raw</a> |
+<a href="/mozilla-central/help">help</a>
+</div>
+
+<div class="search">
+<form id="searchform" action="/mozilla-central/log">
+
+<input name="rev" type="text" value="" size="40" />
+<div id="hint">Find changesets by keywords (author, files, the commit message), revision
+number or hash, or <a href="/mozilla-central/help/revsets">revset expression</a>.</div>
+</form>
+</div>
+</div>
+
+<div class="title">layout/reftests/xul/css-flex-1.xul</div>
+
+<div class="title_text">
+<table cellspacing="0">
+<tr>
+ <td>author</td>
+ <td>&#68;&#97;&#110;&#105;&#101;&#108;&#32;&#72;&#111;&#108;&#98;&#101;&#114;&#116;&#32;&#60;&#100;&#104;&#111;&#108;&#98;&#101;&#114;&#116;&#64;&#99;&#115;&#46;&#115;&#116;&#97;&#110;&#102;&#111;&#114;&#100;&#46;&#101;&#100;&#117;&#62;</td>
+</tr>
+<tr>
+ <td></td>
+ <td class="date age">Wed, 08 Feb 2017 23:08:43 -0800</td>
+</tr>
+
+<tr>
+ <td>changeset 341731</td>
+ <td style="font-family:monospace"><a class="list" href="/mozilla-central/rev/67bbef772796">67bbef772796</a></td>
+</tr>
+
+
+<tr>
+ <td>permissions</td>
+ <td style="font-family:monospace">-rw-r--r--</td>
+</tr>
+</table>
+</div>
+
+<div class="page_path description"><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1338053">Bug 1338053</a>: Make nsFlexContainerFrame::MarkIntrinsicISizesDirty() also call its parent class's method. r=emilio
+
+MozReview-Commit-ID: 72oIlunLcVq</div>
+
+<div class="page_body">
+<pre class="sourcelines stripes"
+ data-logurl="/mozilla-central/log/67bbef772796/layout/reftests/xul/css-flex-1.xul"
+ data-selectabletag="SPAN"
+ data-ishead="1">
+
+<a href="#l1"></a><span id="l1">&lt;?xml version=&quot;1.0&quot;?&gt;</span>
+<a href="#l2"></a><span id="l2">&lt;window xmlns=&quot;http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul&quot;</span>
+<a href="#l3"></a><span id="l3"> class=&quot;reftest-wait&quot;</span>
+<a href="#l4"></a><span id="l4"> onload=&quot;tweak()&quot;&gt;</span>
+<a href="#l5"></a><span id="l5"> &lt;style xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;</span>
+<a href="#l6"></a><span id="l6"> &lt;![CDATA[</span>
+<a href="#l7"></a><span id="l7"> panelview {</span>
+<a href="#l8"></a><span id="l8"> border: 1px solid black;</span>
+<a href="#l9"></a><span id="l9"> background: green;</span>
+<a href="#l10"></a><span id="l10"> display: flex;</span>
+<a href="#l11"></a><span id="l11"> height: 50px;</span>
+<a href="#l12"></a><span id="l12"> }</span>
+<a href="#l13"></a><span id="l13"> ]]&gt;</span>
+<a href="#l14"></a><span id="l14"> &lt;/style&gt;</span>
+<a href="#l15"></a><span id="l15"> &lt;script&gt;</span>
+<a href="#l16"></a><span id="l16"> &lt;![CDATA[</span>
+<a href="#l17"></a><span id="l17"> function tweak() {</span>
+<a href="#l18"></a><span id="l18"> var tweakMe = document.getElementById(&quot;tweakMe&quot;);</span>
+<a href="#l19"></a><span id="l19"> tweakMe.style.width = &quot;100px&quot;;</span>
+<a href="#l20"></a><span id="l20"> document.documentElement.className = &quot;&quot;;</span>
+<a href="#l21"></a><span id="l21"> }</span>
+<a href="#l22"></a><span id="l22"> ]]&gt;</span>
+<a href="#l23"></a><span id="l23"> &lt;/script&gt;</span>
+<a href="#l24"></a><span id="l24"> &lt;hbox&gt;</span>
+<a href="#l25"></a><span id="l25"> &lt;panelview id=&quot;tweakMe&quot;&gt;&lt;/panelview&gt;</span>
+<a href="#l26"></a><span id="l26"> &lt;/hbox&gt;</span>
+<a href="#l27"></a><span id="l27">&lt;/window&gt;</span>
+</pre>
+</div>
+
+<script type="text/javascript" src="/mozilla-central/static/followlines.js"></script>
+
+<div class="page_footer">
+<div class="page_footer_text">mozilla-central</div>
+<div class="page_footer_text" style="padding-left: 10px">Deployed from <a href="https://hg.mozilla.org/hgcustom/version-control-tools/rev/bd13917afa61">bd13917afa61</a> at 2018-04-20T21:06:08Z.</div>
+<div class="rss_logo">
+<a href="/mozilla-central/rss-log">RSS</a>
+<a href="/mozilla-central/atom-log">Atom</a>
+</div>
+<br />
+
+</div>
+</body>
+</html>
+
diff --git a/layout/reftests/xul/reftest.list b/layout/reftests/xul/reftest.list
index da09b7c81..35b9f9025 100644
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -1,3 +1,5 @@
+== css-flex-1.xul css-flex-1-ref.html
+
== menuitem-key.xul menuitem-key-ref.xul
# these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
index 2f12d6201..553239e0e 100644
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1629,23 +1629,11 @@ nsStylePosition::CalcDifference(const nsStylePosition& aNewData,
if (aOldStyleVisibility) {
bool isVertical = WritingMode(aOldStyleVisibility).IsVertical();
if (isVertical ? widthChanged : heightChanged) {
- // Block-size changes can affect descendant intrinsic sizes due to
- // replaced elements with percentage bsizes in descendants which
- // also have percentage bsizes. This is handled via
- // nsChangeHint_UpdateComputedBSize which clears intrinsic sizes
- // for frames that have such replaced elements.
- hint |= nsChangeHint_NeedReflow |
- nsChangeHint_UpdateComputedBSize |
- nsChangeHint_ReflowChangesSizeOrPosition;
+ hint |= nsChangeHint_ReflowHintsForBSizeChange;
}
if (isVertical ? heightChanged : widthChanged) {
- // None of our inline-size differences can affect descendant
- // intrinsic sizes and none of them need to force children to
- // reflow.
- hint |= nsChangeHint_AllReflowHints &
- ~(nsChangeHint_ClearDescendantIntrinsics |
- nsChangeHint_NeedDirtyReflow);
+ hint |= nsChangeHint_ReflowHintsForISizeChange;
}
} else {
if (widthChanged || heightChanged) {
@@ -3258,8 +3246,6 @@ nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const
|| mDisplay != aNewData.mDisplay
|| mContain != aNewData.mContain
|| (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None)
- || mOverflowX != aNewData.mOverflowX
- || mOverflowY != aNewData.mOverflowY
|| mScrollBehavior != aNewData.mScrollBehavior
|| mScrollSnapTypeX != aNewData.mScrollSnapTypeX
|| mScrollSnapTypeY != aNewData.mScrollSnapTypeY
@@ -3271,6 +3257,11 @@ nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const
hint |= nsChangeHint_ReconstructFrame;
}
+ if (mOverflowX != aNewData.mOverflowX
+ || mOverflowY != aNewData.mOverflowY) {
+ hint |= nsChangeHint_CSSOverflowChange;
+ }
+
/* Note: When mScrollBehavior, mScrollSnapTypeX, mScrollSnapTypeY,
* mScrollSnapPointsX, mScrollSnapPointsY, or mScrollSnapDestination are
* changed, nsChangeHint_NeutralChange is not sufficient to enter
diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini
index 406c6f901..8182691ca 100644
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -295,6 +295,7 @@ skip-if = toolkit == 'android'
[test_variables.html]
support-files = support/external-variable-url.css
[test_video_object_fit.html]
+[test_viewport_scrollbar_causing_reflow.html]
[test_viewport_units.html]
[test_visited_image_loading.html]
skip-if = toolkit == 'android' #TIMED_OUT
diff --git a/layout/style/test/test_dynamic_change_causing_reflow.html b/layout/style/test/test_dynamic_change_causing_reflow.html
index a941191f6..a5bb3045c 100644
--- a/layout/style/test/test_dynamic_change_causing_reflow.html
+++ b/layout/style/test/test_dynamic_change_causing_reflow.html
@@ -95,6 +95,90 @@ const gTestcases = [
expectReflow: true,
},
+ // * Changing 'overflow' on <body> should cause reflow,
+ // but not frame reconstruction
+ {
+ elem: document.body,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: scroll",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: scroll",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: visible",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: visible",
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+
+ // * Changing 'overflow' on <html> should cause reflow,
+ // but not frame reconstruction
+ {
+ elem: document.documentElement,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.documentElement,
+ beforeStyle: "overflow: visible",
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+
+ // * Setting 'overflow' on arbitrary node should cause reflow as well as
+ // frame reconstruction
+ {
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: auto",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: visible",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+
// * Changing 'display' should cause frame construction and reflow.
{
beforeStyle: "display: inline",
@@ -135,23 +219,34 @@ function runOneTest(aTestcase)
return;
}
+ // Figure out which element we'll be tweaking (defaulting to gElem)
+ let elem = aTestcase.elem ?
+ aTestcase.elem : gElem;
+
+ // Verify that 'style' attribute is unset (avoid causing ourselves trouble):
+ if (elem.hasAttribute("style")) {
+ ok(false,
+ "test element has 'style' attribute already set! We're going to stomp " +
+ "on whatever's there when we clean up...");
+ }
+
// Set the "before" style, and compose the first part of the message
// to be used in our "is"/"isnot" invocations:
let msgPrefix = "Changing style ";
if (aTestcase.beforeStyle) {
- gElem.setAttribute("style", aTestcase.beforeStyle);
+ elem.setAttribute("style", aTestcase.beforeStyle);
msgPrefix += "from '" + aTestcase.beforeStyle + "' ";
}
- msgPrefix += "to '" + aTestcase.afterStyle + "' ";
+ msgPrefix += "on " + elem.nodeName + " ";
// Establish initial counts:
- let unusedVal = gElem.offsetHeight; // flush layout
+ let unusedVal = elem.offsetHeight; // flush layout
let origFramesConstructed = gUtils.framesConstructed;
let origFramesReflowed = gUtils.framesReflowed;
// Make the change and flush:
- gElem.setAttribute("style", aTestcase.afterStyle);
- unusedVal = gElem.offsetHeight; // flush layout
+ elem.setAttribute("style", aTestcase.afterStyle);
+ unusedVal = elem.offsetHeight; // flush layout
// Make our is/isnot assertions about whether things should have changed:
checkFinalCount(gUtils.framesConstructed, origFramesConstructed,
@@ -162,7 +257,7 @@ function runOneTest(aTestcase)
"reflow");
// Clean up!
- gElem.removeAttribute("style");
+ elem.removeAttribute("style");
}
gTestcases.forEach(runOneTest);
diff --git a/layout/style/test/test_viewport_scrollbar_causing_reflow.html b/layout/style/test/test_viewport_scrollbar_causing_reflow.html
new file mode 100644
index 000000000..dfd7ec450
--- /dev/null
+++ b/layout/style/test/test_viewport_scrollbar_causing_reflow.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1367568
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1367568</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug 1367568</a>
+<div id="content">
+ <!-- Some fixed-width divs that we shouldn't have to reflow when the viewport
+ changes: -->
+ <div style="width: 100px">
+ fixed-width
+ <div>(child)</div>
+ </div>
+ <div style="position: absolute; width: 150px">
+ abs-fixed-width
+ <div>(child)</div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+"use strict";
+
+/** Test for Bug 1367568 **/
+
+/**
+ * This test verifies that "overflow" changes on the <body> don't cause
+ * an unnecessarily large amount of reflow.
+ */
+
+// Vars used in setStyleAndMeasure that we really only have to look up once:
+const gUtils = SpecialPowers.getDOMWindowUtils(window);
+
+function setStyleAndMeasure(initialStyle, finalStyle) {
+ is(document.body.style.length, 0,
+ "Bug in test - body should start with empty style");
+ let unusedVal = document.body.offsetHeight; // flush layout
+ let constructCount = gUtils.framesConstructed;
+
+ document.body.style = initialStyle;
+ unusedVal = document.body.offsetHeight; // flush layout
+ let reflowCountBeforeTweak = gUtils.framesReflowed;
+
+ document.body.style = finalStyle;
+ unusedVal = document.body.offsetHeight; // flush layout
+ let reflowCountAfterTweak = gUtils.framesReflowed;
+
+ // Clean up:
+ document.body.style = "";
+
+ is(gUtils.framesConstructed, constructCount,
+ "Style tweak shouldn't have triggered frame construction");
+
+ // ...and return the delta:
+ return reflowCountAfterTweak - reflowCountBeforeTweak;
+}
+
+function main() {
+ // First, we sanity-check that our measurement make sense -- if we leave
+ // styles unchanged, we should measure no frames being reflowed:
+ let count = setStyleAndMeasure("width: 50px; height: 80px",
+ "width: 50px; height: 80px");
+ is(count, 0,
+ "Shouldn't reflow anything when we leave 'width' & 'height' unchanged");
+
+ // Now: see how many frames are reflowed when the "width" & "height" change.
+ // We'll use this as the reference when measuring reflow counts for various
+ // changes to "overflow" below.
+ count = setStyleAndMeasure("width: 50px; height: 80px",
+ "width: 90px; height: 60px");
+ ok(count > 0,
+ "Should reflow some frames when 'width' & 'height' change");
+
+ // Expected maximum number of frames reflowed for "overflow" changes
+ // (+2 is to allow for reflowing scrollbars themselves):
+ const expectedMax = count + 2;
+
+ // Shared ending for messages in all ok() checks below:
+ const messageSuffix =
+ " shouldn't be greater than count for tweaking width/height on body (" +
+ expectedMax + ")";
+
+ // OK, here is where the relevant tests actually begin!!
+ // See how many frames we reflow for various tweaks to "overflow" on
+ // the body -- we expect the count to be no larger than |expectedMax|.
+ count = setStyleAndMeasure("", "overflow: scroll");
+ ok(count <= expectedMax,
+ "Reflow count when setting 'overflow: scroll' on body (" + count + ")" +
+ messageSuffix);
+
+ count = setStyleAndMeasure("", "overflow: hidden");
+ ok(count <= expectedMax,
+ "Reflow count when setting 'overflow: hidden' on body (" + count + ")" +
+ messageSuffix);
+
+ // Test removal of "overflow: scroll":
+ count = setStyleAndMeasure("overflow: scroll", "");
+ ok(count <= expectedMax,
+ "Reflow count when removing 'overflow: scroll' from body (" + count + ")" +
+ messageSuffix);
+
+ count = setStyleAndMeasure("overflow: hidden", "");
+ ok(count <= expectedMax,
+ "Reflow count when removing 'overflow: hidden' from body (" + count + ")" +
+ messageSuffix);
+
+ // Test change between two non-'visible' overflow values:
+ count = setStyleAndMeasure("overflow: scroll", "overflow: hidden");
+ ok(count <= expectedMax,
+ "Reflow count when changing 'overflow' on body (" + count + ")" +
+ messageSuffix);
+}
+
+main();
+
+</script>
+</pre>
+</body>
+</html>