Bug 1463589 - Add contain:size and contain:content parsing functionality. draft
authorMorgan Rae Reschenberg <mreschenberg@mozilla.com>
Tue, 29 May 2018 14:12:38 -0700
changeset 801222 48be5bcb32fc1333b561dfa60926bd9946e5d3bb
parent 798084 b75acf9652937ce79a9bf02de843c100db0e5ec7
push id111607
push userbmo:mreschenberg@berkeley.edu
push dateTue, 29 May 2018 22:25:11 +0000
bugs1463589
milestone62.0a1
Bug 1463589 - Add contain:size and contain:content parsing functionality. MozReview-Commit-ID: 7Wo31hpwKZq
.lldbinit
layout/reftests/w3c-css/submitted/contain/reftest.list
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleConsts.h
layout/style/test/property_database.js
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/specified/box.rs
--- a/.lldbinit
+++ b/.lldbinit
@@ -8,17 +8,17 @@
 # Import the module that defines complex Gecko debugging commands.  This assumes
 # you are either running lldb from the top level source directory, the objdir,
 # or the dist/bin directory.  (.lldbinit files in the objdir and dist/bin set
 # topsrcdir appropriately.)
 script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "third_party/python/lldbutils")); import lldbutils; lldbutils.init()
 
 # Mozilla's use of UNIFIED_SOURCES to include multiple source files into a
 # single compiled file breaks lldb breakpoint setting. This works around that.
-# See http://lldb.llvm.org/troubleshooting.html for more info.
+# See http://lldb.llvm.org/troubleshooting.html for more.
 settings set target.inline-breakpoint-strategy always
 
 # Show the dynamic type of an object when using "expr".  This, for example,
 # will show a variable declared as "nsIFrame *" that points to an nsBlockFrame
 # object as being of type "nsBlockFrame *" rather than "nsIFrame *".
 settings set target.prefer-dynamic-value run-target
 
 # Show the string value in atoms.
--- a/layout/reftests/w3c-css/submitted/contain/reftest.list
+++ b/layout/reftests/w3c-css/submitted/contain/reftest.list
@@ -3,9 +3,9 @@ default-preferences pref(layout.css.cont
 == contain-paint-clip-001.html contain-paint-clip-001-ref.html
 == contain-paint-clip-002.html contain-paint-clip-002-ref.html
 == contain-paint-clip-003.html contain-paint-clip-003-ref.html
 == contain-paint-clip-004.html contain-paint-clip-004-ref.html
 == contain-paint-clip-005.html contain-paint-clip-003-ref.html
 == contain-paint-containing-block-absolute-001.html contain-paint-containing-block-absolute-001-ref.html
 == contain-paint-containing-block-fixed-001.html contain-paint-containing-block-fixed-001-ref.html
 == contain-paint-formatting-context-float-001.html contain-paint-formatting-context-float-001-ref.html
-== contain-paint-formatting-context-margin-001.html contain-paint-formatting-context-margin-001-ref.html
+== contain-paint-formatting-context-margin-001.html contain-paint-formatting-context-margin-001-ref.html
\ No newline at end of file
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -30,17 +30,17 @@
   'name' entries *must* use only lowercase characters.
 
   ** Break these invariants and bad things will happen. **
 
  ******/
 
 // OUTPUT_CLASS=nsCSSKeywords
 // MACRO_NAME=CSS_KEY
-
+CSS_KEY(size, size)
 CSS_KEY(-moz-activehyperlinktext, _moz_activehyperlinktext)
 CSS_KEY(-moz-all, _moz_all)
 CSS_KEY(-moz-alt-content, _moz_alt_content)
 CSS_KEY(-moz-available, _moz_available)
 CSS_KEY(-moz-box, _moz_box)
 CSS_KEY(-moz-button, _moz_button)
 CSS_KEY(-moz-buttondefault, _moz_buttondefault)
 CSS_KEY(-moz-buttonhoverface, _moz_buttonhoverface)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1043,19 +1043,21 @@ const KTableEntry nsCSSProps::kListStyle
   { eCSSKeyword_inside, NS_STYLE_LIST_STYLE_POSITION_INSIDE },
   { eCSSKeyword_outside, NS_STYLE_LIST_STYLE_POSITION_OUTSIDE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kContainKTable[] = {
   { eCSSKeyword_none,    NS_STYLE_CONTAIN_NONE },
   { eCSSKeyword_strict,  NS_STYLE_CONTAIN_STRICT },
+  { eCSSKeyword_content, NS_STYLE_CONTAIN_CONTENT },
   { eCSSKeyword_layout,  NS_STYLE_CONTAIN_LAYOUT },
   { eCSSKeyword_style,   NS_STYLE_CONTAIN_STYLE },
   { eCSSKeyword_paint,   NS_STYLE_CONTAIN_PAINT },
+  { eCSSKeyword_size,    NS_STYLE_CONTAIN_SIZE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kObjectFitKTable[] = {
   { eCSSKeyword_fill,       NS_STYLE_OBJECT_FIT_FILL },
   { eCSSKeyword_contain,    NS_STYLE_OBJECT_FIT_CONTAIN },
   { eCSSKeyword_cover,      NS_STYLE_OBJECT_FIT_COVER },
   { eCSSKeyword_none,       NS_STYLE_OBJECT_FIT_NONE },
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5040,24 +5040,28 @@ nsComputedDOMStyle::DoGetContain()
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   int32_t mask = StyleDisplay()->mContain;
 
   if (mask == 0) {
     val->SetIdent(eCSSKeyword_none);
   } else if (mask & NS_STYLE_CONTAIN_STRICT) {
     NS_ASSERTION(mask == (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS),
-                 "contain: strict should imply contain: layout style paint");
+                 "contain: strict should imply contain: size layout style paint");
     val->SetIdent(eCSSKeyword_strict);
-  } else {
+  } else if (mask & NS_STYLE_CONTAIN_CONTENT) {
+    NS_ASSERTION(mask == (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS),
+                 "contain: content should imply contain: layout style paint");
+    val->SetIdent(eCSSKeyword_content);
+  }  else {
     nsAutoString valueStr;
-
     nsStyleUtil::AppendBitmaskCSSValue(nsCSSProps::kContainKTable,
-                                       mask, NS_STYLE_CONTAIN_LAYOUT,
-                                       NS_STYLE_CONTAIN_PAINT, valueStr);
+                                       mask,
+                                       NS_STYLE_CONTAIN_SIZE, NS_STYLE_CONTAIN_PAINT,
+                                       valueStr);
     val->SetString(valueStr);
   }
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetPosition()
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -450,26 +450,32 @@ enum class StyleDisplay : uint8_t {
   MozInlineStack,
   MozDeck,
   MozGroupbox,
   MozPopup,
 #endif
 };
 
 // See nsStyleDisplay
-// If these are re-ordered, nsComputedDOMStyle::DoGetContain() and
-// nsCSSValue::AppendToString() must be updated.
+// If these are re-ordered, nsComputedDOMStyle::DoGetContain() must be updated.
 #define NS_STYLE_CONTAIN_NONE                   0
-#define NS_STYLE_CONTAIN_STRICT                 0x1
-#define NS_STYLE_CONTAIN_LAYOUT                 0x2
-#define NS_STYLE_CONTAIN_STYLE                  0x4
-#define NS_STYLE_CONTAIN_PAINT                  0x8
+#define NS_STYLE_CONTAIN_SIZE                   0x01
+#define NS_STYLE_CONTAIN_LAYOUT                 0x02
+#define NS_STYLE_CONTAIN_STYLE                  0x04
+#define NS_STYLE_CONTAIN_PAINT                  0x08
+#define NS_STYLE_CONTAIN_STRICT                 0x10
+#define NS_STYLE_CONTAIN_CONTENT                0x20
 // NS_STYLE_CONTAIN_ALL_BITS does not correspond to a keyword.
 #define NS_STYLE_CONTAIN_ALL_BITS               (NS_STYLE_CONTAIN_LAYOUT | \
                                                  NS_STYLE_CONTAIN_STYLE  | \
+                                                 NS_STYLE_CONTAIN_PAINT  | \
+                                                 NS_STYLE_CONTAIN_SIZE)
+// NS_STYLE_CONTAIN_CONTENT_BITS does not correspond to a keyword.
+#define NS_STYLE_CONTAIN_CONTENT_BITS           (NS_STYLE_CONTAIN_LAYOUT | \
+                                                 NS_STYLE_CONTAIN_STYLE  | \
                                                  NS_STYLE_CONTAIN_PAINT)
 
 // Shared constants for all align/justify properties (nsStylePosition):
 #define NS_STYLE_ALIGN_AUTO             0
 #define NS_STYLE_ALIGN_NORMAL           1
 #define NS_STYLE_ALIGN_START            2
 #define NS_STYLE_ALIGN_END              3
 #define NS_STYLE_ALIGN_FLEX_START       4
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -7273,38 +7273,50 @@ if (IsCSSPropertyPrefEnabled("layout.css
     domProp: "contain",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [
       "strict",
       "layout",
       "style",
+      "size",
+      "content",
       "layout style",
       "style layout",
       "paint",
       "layout paint",
       "paint layout",
       "style paint",
       "paint style",
+      "size layout",
+      "style size",
+      "paint size",
       "layout style paint",
       "layout paint style",
       "style paint layout",
       "paint style layout",
     ],
     invalid_values: [
       "none strict",
       "strict layout",
       "strict layout style",
       "layout strict",
+      "layout content",
+      "strict content",
       "layout style strict",
       "layout style paint strict",
       "paint strict",
       "style strict",
       "paint paint",
+      "content content",
+      "size content",
+      "content strict size",
+      "paint layout content",
+      "layout size content",
       "strict strict",
       "auto",
       "10px",
       "0",
     ]
   };
 }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -3622,72 +3622,100 @@ fn static_assert() {
         T::AnimateableFeatures(custom_idents.into_boxed_slice())
     }
 
     <% impl_shape_source("shape_outside", "mShapeOutside") %>
 
     pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
         use gecko_bindings::structs::NS_STYLE_CONTAIN_NONE;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
         use properties::longhands::contain::SpecifiedValue;
 
         if v.is_empty() {
             self.gecko.mContain = NS_STYLE_CONTAIN_NONE as u8;
             return;
         }
 
         if v.contains(SpecifiedValue::STRICT) {
             self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8;
             return;
         }
+        if v.contains(SpecifiedValue::CONTENT) {
+            self.gecko.mContain = (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS) as u8;
+            return;
+        }
 
         let mut bitfield = 0;
         if v.contains(SpecifiedValue::LAYOUT) {
             bitfield |= NS_STYLE_CONTAIN_LAYOUT;
         }
         if v.contains(SpecifiedValue::STYLE) {
             bitfield |= NS_STYLE_CONTAIN_STYLE;
         }
         if v.contains(SpecifiedValue::PAINT) {
             bitfield |= NS_STYLE_CONTAIN_PAINT;
         }
+        if v.contains(SpecifiedValue::SIZE) {
+            bitfield |= NS_STYLE_CONTAIN_SIZE;
+        }
 
         self.gecko.mContain = bitfield as u8;
     }
 
     pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
         use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
         use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
+        use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
         use properties::longhands::contain::{self, SpecifiedValue};
 
         let mut servo_flags = contain::computed_value::T::empty();
         let gecko_flags = self.gecko.mContain;
 
-        if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 &&
-           gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8) != 0 {
+        if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 {
+            debug_assert_eq!(
+                gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8),
+                NS_STYLE_CONTAIN_ALL_BITS as u8,
+                "When strict is specified, ALL_BITS should be specified as well"
+            );
             servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS);
             return servo_flags;
         }
-
+        if gecko_flags & (NS_STYLE_CONTAIN_CONTENT as u8) != 0 {
+            debug_assert_eq!(
+                gecko_flags & (NS_STYLE_CONTAIN_CONTENT_BITS as u8),
+                NS_STYLE_CONTAIN_CONTENT_BITS as u8,
+                "When content is specified, CONTENT_BITS should be specified as well"
+            );
+            servo_flags.insert(SpecifiedValue::CONTENT | SpecifiedValue::CONTENT_BITS);
+            return servo_flags;
+        }
         if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 {
             servo_flags.insert(SpecifiedValue::LAYOUT);
         }
-        if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0{
+        if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0 {
             servo_flags.insert(SpecifiedValue::STYLE);
         }
         if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 {
             servo_flags.insert(SpecifiedValue::PAINT);
         }
+        if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 {
+            servo_flags.insert(SpecifiedValue::SIZE);
+        }
 
         return servo_flags;
     }
 
     ${impl_simple_copy("contain", "mContain")}
 
     ${impl_simple_type_with_conversion("touch_action")}
 </%self:impl_trait>
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -520,56 +520,66 @@ pub fn assert_touch_action_matches() {
         NS_STYLE_TOUCH_ACTION_PAN_X => TouchAction::TOUCH_ACTION_PAN_X,
         NS_STYLE_TOUCH_ACTION_PAN_Y => TouchAction::TOUCH_ACTION_PAN_Y,
         NS_STYLE_TOUCH_ACTION_MANIPULATION => TouchAction::TOUCH_ACTION_MANIPULATION,
     }
 }
 
 bitflags! {
     #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
-    #[value_info(other_values = "none,strict,layout,style,paint")]
+    #[value_info(other_values = "none,strict,content,size,layout,style,paint")]
     /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
     pub struct Contain: u8 {
+        /// 'size' variant, turns on size containment
+        const SIZE = 0x01;
         /// `layout` variant, turns on layout containment
-        const LAYOUT = 0x01;
+        const LAYOUT = 0x02;
         /// `style` variant, turns on style containment
-        const STYLE = 0x02;
+        const STYLE = 0x04;
         /// `paint` variant, turns on paint containment
-        const PAINT = 0x04;
+        const PAINT = 0x08;
         /// `strict` variant, turns on all types of containment
-        const STRICT = 0x8;
+        const STRICT = 0x10;
+        /// 'content' variant, turns on style, layout, and paint containment
+        const CONTENT = 0x20;
         /// variant with all the bits that contain: strict turns on
-        const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits;
+        const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits;
+        /// variant with all the bits that contain: content turns on
+        const CONTENT_BITS = Contain::STYLE.bits | Contain::LAYOUT.bits | Contain::PAINT.bits;
     }
 }
 
 impl ToCss for Contain {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         if self.is_empty() {
             return dest.write_str("none");
         }
         if self.contains(Contain::STRICT) {
             return dest.write_str("strict");
         }
+        if self.contains(Contain::CONTENT) {
+            return dest.write_str("content");
+        }
 
         let mut has_any = false;
         macro_rules! maybe_write_value {
             ($ident:path => $str:expr) => {
                 if self.contains($ident) {
                     if has_any {
                         dest.write_str(" ")?;
                     }
                     has_any = true;
                     dest.write_str($str)?;
                 }
             };
         }
+        maybe_write_value!(Contain::SIZE => "size");
         maybe_write_value!(Contain::LAYOUT => "layout");
         maybe_write_value!(Contain::STYLE => "style");
         maybe_write_value!(Contain::PAINT => "paint");
 
         debug_assert!(has_any);
         Ok(())
     }
 }
@@ -578,20 +588,22 @@ impl Parse for Contain {
     /// none | strict | content | [ size || layout || style || paint ]
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Contain, ParseError<'i>> {
         let mut result = Contain::empty();
         while let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
             let flag = match_ignore_ascii_case! { &name,
+                "size" => Some(Contain::SIZE),
                 "layout" => Some(Contain::LAYOUT),
                 "style" => Some(Contain::STYLE),
                 "paint" => Some(Contain::PAINT),
                 "strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS),
+                "content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS),
                 "none" if result.is_empty() => return Ok(result),
                 _ => None
             };
 
             let flag = match flag {
                 Some(flag) if !result.contains(flag) => flag,
                 _ => {
                     return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name)))