Bug 1170781 - Patch 1: Implement CSS 'contain: paint'. r=dholbert
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -6754,18 +6754,21 @@ nsBlockFrame::Init(nsIContent* aCo
AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
}
// If a box has a different block flow direction than its containing block:
// ...
// If the box is a block container, then it establishes a new block
// formatting context.
// (http://dev.w3.org/csswg/css-writing-modes/#block-flow)
- if (GetParent() && StyleVisibility()->mWritingMode !=
- GetParent()->StyleVisibility()->mWritingMode) {
+ // If the box has contain: paint (or contain: strict), then it should also
+ // establish a formatting context.
+ if ((GetParent() && StyleVisibility()->mWritingMode !=
+ GetParent()->StyleVisibility()->mWritingMode) ||
+ StyleDisplay()->IsContainPaint()) {
AddStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
}
if ((GetStateBits() &
(NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) ==
(NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -5700,16 +5700,31 @@ nsRuleNode::ComputeDisplayData(void* aSt
// If 'visible' is specified but doesn't match the other dimension, it
// turns into 'auto'.
if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
display->mOverflowX = NS_STYLE_OVERFLOW_AUTO;
if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE)
display->mOverflowY = NS_STYLE_OVERFLOW_AUTO;
}
+ // When 'contain: paint', update overflow from 'visible' to 'clip'.
+ if (display->IsContainPaint()) {
+ // XXX This actually sets overflow-[x|y] to -moz-hidden-unscrollable.
+ if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) {
+ // This uncacheability (and the one below) could be fixed by adding
+ // mOriginalOverflowX and mOriginalOverflowY fields, if necessary.
+ display->mOverflowX = NS_STYLE_OVERFLOW_CLIP;
+ conditions.SetUncacheable();
+ }
+ if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
+ display->mOverflowY = NS_STYLE_OVERFLOW_CLIP;
+ conditions.SetUncacheable();
+ }
+ }
+
SetDiscrete(*aRuleData->ValueForOverflowClipBox(), display->mOverflowClipBox,
conditions,
SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
parentDisplay->mOverflowClipBox,
NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX, 0, 0, 0, 0);
SetDiscrete(*aRuleData->ValueForResize(), display->mResize, conditions,
SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
@@ -5841,16 +5856,28 @@ nsRuleNode::ComputeDisplayData(void* aSt
// Note that it's OK to cache this struct in the ruletree
// because it's fine as-is for any style context that points to
// it directly, and any use of it as aStartStruct (e.g. if a
// more specific rule sets "float: none") will use
// mOriginalDisplay, which we have carefully not changed.
}
+ if (display->IsContainPaint()) {
+ // An element with contain:paint or contain:layout needs to "be a
+ // formatting context". For the purposes of the "display" property, that
+ // just means we need to promote "display:inline" to "inline-block".
+ // XXX We may also need to promote ruby display vals; see bug 1179349.
+
+ // It's okay to cache this change in the rule tree for the same
+ // reasons as floats in the previous condition.
+ if (display->mDisplay == NS_STYLE_DISPLAY_INLINE) {
+ display->mDisplay = NS_STYLE_DISPLAY_INLINE_BLOCK;
+ }
+ }
}
/* Convert the nsCSSValueList into an nsTArray<nsTransformFunction *>. */
const nsCSSValue* transformValue = aRuleData->ValueForTransform();
switch (transformValue->GetUnit()) {
case eCSSUnit_Null:
break;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2369,16 +2369,20 @@ struct nsStyleDisplay {
bool IsScrollableOverflow() const {
// mOverflowX and mOverflowY always match when one of them is
// NS_STYLE_OVERFLOW_VISIBLE or NS_STYLE_OVERFLOW_CLIP.
return mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
mOverflowX != NS_STYLE_OVERFLOW_CLIP;
}
+ bool IsContainPaint() const {
+ return NS_STYLE_CONTAIN_PAINT & mContain;
+ }
+
/* Returns whether the element has the -moz-transform property
* or a related property. */
bool HasTransformStyle() const {
return mSpecifiedTransform != nullptr ||
mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
(mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM);
}
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -135,17 +135,18 @@ nsStyleDisplay::HasTransform(const nsIFr
return HasTransformStyle() && aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
}
bool
nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const
{
NS_ASSERTION(aContextFrame->StyleDisplay() == this,
"unexpected aContextFrame");
- return (HasTransform(aContextFrame) || HasPerspectiveStyle() ||
+ return (IsContainPaint() || HasTransform(aContextFrame) ||
+ HasPerspectiveStyle() ||
aContextFrame->StyleSVGReset()->HasFilters()) &&
!aContextFrame->IsSVGText();
}
bool
nsStyleDisplay::IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const
{
NS_ASSERTION(aContextFrame->StyleDisplay() == this,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2563,17 +2563,17 @@ var gCSSProperties = {
invalid_values: []
},
"display": {
domProp: "display",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "inline" ],
/* XXX none will really mess with other properties */
- prerequisites: { "float": "none", "position": "static" },
+ prerequisites: { "float": "none", "position": "static", "contain": "none" },
other_values: [
"block",
"flex",
"inline-flex",
"list-item",
"inline-block",
"table",
"inline-table",
@@ -3221,36 +3221,36 @@ var gCSSProperties = {
"calc(3*25px + 5em)",
],
invalid_values: [ "5%", "5" ]
},
"overflow": {
domProp: "overflow",
inherited: false,
type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
- prerequisites: { "display": "block" },
+ prerequisites: { "display": "block", "contain": "none" },
subproperties: [ "overflow-x", "overflow-y" ],
initial_values: [ "visible" ],
other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable", "-moz-scrollbars-none" ],
invalid_values: []
},
"overflow-x": {
domProp: "overflowX",
inherited: false,
type: CSS_TYPE_LONGHAND,
- prerequisites: { "display": "block", "overflow-y": "visible" },
+ prerequisites: { "display": "block", "overflow-y": "visible", "contain": "none" },
initial_values: [ "visible" ],
other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ],
invalid_values: []
},
"overflow-y": {
domProp: "overflowY",
inherited: false,
type: CSS_TYPE_LONGHAND,
- prerequisites: { "display": "block", "overflow-x": "visible" },
+ prerequisites: { "display": "block", "overflow-x": "visible", "contain": "none" },
initial_values: [ "visible" ],
other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ],
invalid_values: []
},
"padding": {
domProp: "padding",
inherited: false,
type: CSS_TYPE_TRUE_SHORTHAND,