Adding a new CSS property

  • Revision slug: Adding_a_new_style_property
  • Revision title: Adding a new CSS property
  • Revision id: 455723
  • Created:
  • Creator: DBaron
  • Is current revision? No
  • Comment rename from Style Property -> CSS Property

Revision Content

This page describes how to add a new CSS property to the style system.  The style system is the part of the code in Gecko that is responsible for producing a computed value for every property for every element.  See the Gecko Overview for more information about the style system.

This document assumes that you have a specifcation for the property available.  Issues about how to write such a specification (including things such as whether it is appropriate to use prefixes or when properties should be inherited by default) are not covered here.

If you need more details on any of the points mentioned here, a good place to find them is by looking at other changes in the version control history of the files mentioned.

Getting started

First, you'll want to add an appropriate test entry to property_database.js.  See the documentation at the top of the file and the other entries for examples.  This will cause tests of your new property to be added to many of the mochitests in layout/style/test, which can be run with the command "TEST_PATH=layout/style make mochitest-plain".

Then, unless you're implementing a shorthand property, you need to decide which style struct the computed value of the proerty should go in.  (Again, see the Gecko Overview for more information.)  It should generally be grouped with related properties.  Remember that each style struct contains only properties that inherit by default or only properties that do not.  (Which set the property is in is given in the specification, which says "Inherited: yes" or "Inherited: no" in the property's definition.)  Also note that some of the style structs intentionally contain only properties set/reset by a particular common shorthand property; this improves the effectiveness of some of the performance and memory optimizations done with the rule tree, and thus we should avoid adding a property not reset by that shorthand to such a struct.

Once you know which struct you're going to use (or that the property is a shorthand), you can add appropriate entry to nsCSSPropList.h.  Again, see the documentation at the top of the file and the other entries for examples, and also see the next section for details about parsing.

Parsing

To start implementing the parsing, first read the Syntax line in the property's specification (whose syntax is in turn defined in the CSS Values and Units specification) and any prose in the specification that adds additional restrictions to that syntax.

Every property in nsCSSPropList.h must have in its flags field exactly one of the four CSS_PROPERTY_PARSE_* flags defined in nsCSSProps.h.  Given the syntax for the property, you can determine which one to use.  If the property's values can be expressed as a single value, or as a space-separated or comma-separated list of values that can be described by the VARIANT_* flags (also in nsCSSProps.h), you should use CSS_PROPERTY_PARSE_VALUE or CSS_PROPERTY_PARSE_VALUE_LIST, and set the appropriate VARIANT_* flags in the entry in nsCSSPropList.h (and maybe also CSS_PROPERTY_VALUE_LIST_USES_COMMAS).  Otherwise, set the variant entry in the nsCSSPropList entry to 0.  If you need custom parsing code, this can be inserted at one of two different places:  you can override the entire property value parsing by using CSS_PROPERTY_PARSE_FUNCTION, which is generally the right thing to do for shorthand properties.  Or, preferably, you can still use CSS_PROPERTY_PARSE_VALUE or ..._VALUE_LIST and just override the value parsing by setting CSS_PROPERTY_VALUE_PARSER_FUNCTION.  You must take the latter approach for properties that are part of shorthands if you want the shorthand parser to be able to reuse the longhand parser.  (Note that when the longhand property CSS_PROPERTY_PARSE_VALUE_LIST, the shorthand property parser would be assumed to be reusing the longhand parser once per item, not for a whole list, as for properties like background-image or transform-timing-function.)

If the property takes a list of keywords other than inherit/initial/etc., auto, none, or normal (which can be expressed using the VARIANT_* flags), you should use VARIANT_KEYWORD and add a keyword table to the nsCSSProps class.  The name of this keyword table needs to go in the nsCSSPropList entry; otherwise that keyword table should be null.  If you need a keyword table, you should include auto, normal, or none (if needed) in that table rather than combining VARIANT_KEYWORD with VARIANT_AUTO, VARIANT_NORMAL, or VARIANT_NONE.

If you add a shorthand property, you'll need to use the CSS_PROP_SHORTHAND macro.  You'll need to use CSS_PROPERTY_PARSE_FUNCTION as described above; you can't use the other options for shorthands.  And for shorthands you also need to include a subproperty table in nsCSSProps, whose name must match the "method" argument  in the CSS_PROP_SHORTHAND macro.  When writing the custom parsing code for a shorthand, you'll need to ensure that any successful parse sets all of the subproperties of the shorthand (since that's the way shorthands work in CSS).

Some common mistakes to watch out for when writing custom parsing code (which might go away if we redesign the parser along the lines described in css3-syntax):

  • make sure to call SkipUntil() to look for the matching close parentheses, braces, or brackets whenever you hit an error inside of them.
  • make sure to call UngetToken() if you have called GetToken() but the result is unexpected.  This is particularly important in case the unexpected token is a parenthesis or something else that requires matching the other half of a pair.
  • if you're adding a preference-controlled property to a non-preference-controlled shorthand, you need to call AppendValue for that property if and only if its preference is enabled (and see next point)
  • if you need to check a preference in custom parsing code (e.g., because you're adding a new property to a shorthand, but only conditionally on that property's preference), call nsCSSProps::IsEnabled(), which is faster than calling into the preferences code

For further understanding of how the parsing code works, you should read and understand the code in nsCSSParser.cpp.

Computed Style

add member variables to style structs, fix constructor and copy constructor

change hints and CalcDifference (see nsChangeHint.h and the code that implements change hint propcessing)

rule node

nsComputedDOMStyle

Revision Source

<p>This page describes how to add a new CSS property to the style system.&nbsp; The style system is the part of the code in Gecko that is responsible for producing a computed value for every property for every element.&nbsp; See <a href="https://wiki.mozilla.org/Gecko:Overview#Style_System" title="https://wiki.mozilla.org/Gecko:Overview#Style_System">the Gecko Overview</a> for more information about the style system.</p>
<p>This document assumes that you have a specifcation for the property available.&nbsp; Issues about how to write such a specification (including things such as whether it is appropriate to use prefixes or when properties should be inherited by default) are not covered here.</p>
<p>If you need more details on any of the points mentioned here, a good place to find them is by looking at other changes in the version control history of the files mentioned.</p>
<h2 id="Getting_started">Getting started</h2>
<p>First, you'll want to add an appropriate test entry to <a href="http://mxr.mozilla.org/mozilla-central/source/layout/style/test/property_database.js" title="http://mxr.mozilla.org/mozilla-central/source/layout/style/test/property_database.js">property_database.js</a>.&nbsp; See the documentation at the top of the file and the other entries for examples.&nbsp; This will cause tests of your new property to be added to many of the mochitests in layout/style/test, which can be run with the command "TEST_PATH=layout/style make mochitest-plain".</p>
<p>Then, unless you're implementing a shorthand property, you need to decide which style struct the computed value of the proerty should go in.&nbsp; (Again, see <a href="https://wiki.mozilla.org/Gecko:Overview#Style_System" title="https://wiki.mozilla.org/Gecko:Overview#Style_System">the Gecko Overview</a> for more information.)&nbsp; It should generally be grouped with related properties.&nbsp; Remember that each style struct contains only properties that inherit by default or only properties that do not.&nbsp; (Which set the property is in is given in the specification, which says "Inherited: yes" or "Inherited: no" in the property's definition.)&nbsp; Also note that some of the style structs intentionally contain only properties set/reset by a particular common shorthand property; this improves the effectiveness of some of the performance and memory optimizations done with the rule tree, and thus we should avoid adding a property not reset by that shorthand to such a struct.</p>
<p>Once you know which struct you're going to use (or that the property is a shorthand), you can add appropriate entry to <a href="http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSPropList.h" title="http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSPropList.h">nsCSSPropList.h</a>.&nbsp; Again, see the documentation at the top of the file and the other entries for examples, and also see the next section for details about parsing.</p>
<h2 id="Parsing">Parsing</h2>
<p>To start implementing the parsing, first read the Syntax line in the property's specification (whose syntax is in turn defined in the <a href="http://dev.w3.org/csswg/css-values/#value-defs" title="http://dev.w3.org/csswg/css-values/#value-defs">CSS Values and Units</a> specification) and any prose in the specification that adds additional restrictions to that syntax.</p>
<p>Every property in nsCSSPropList.h must have in its flags field exactly one of the four CSS_PROPERTY_PARSE_* flags defined in <a href="http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSProps.h" title="http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSProps.h">nsCSSProps.h</a>.&nbsp; Given the syntax for the property, you can determine which one to use.&nbsp; If the property's values can be expressed as a single value, or as a space-separated or comma-separated list of values that can be described by the VARIANT_* flags (also in nsCSSProps.h), you should use CSS_PROPERTY_PARSE_VALUE or CSS_PROPERTY_PARSE_VALUE_LIST, and set the appropriate VARIANT_* flags in the entry in nsCSSPropList.h (and maybe also CSS_PROPERTY_VALUE_LIST_USES_COMMAS).&nbsp; Otherwise, set the variant entry in the nsCSSPropList entry to 0.&nbsp; If you need custom parsing code, this can be inserted at one of two different places:&nbsp; you can override the entire property value parsing by using CSS_PROPERTY_PARSE_FUNCTION, which is generally the right thing to do for shorthand properties.&nbsp; Or, preferably, you can still use CSS_PROPERTY_PARSE_VALUE or ..._VALUE_LIST and just override the value parsing by setting CSS_PROPERTY_VALUE_PARSER_FUNCTION.&nbsp; You must take the latter approach for properties that are part of shorthands if you want the shorthand parser to be able to reuse the longhand parser.&nbsp; (Note that when the longhand property CSS_PROPERTY_PARSE_VALUE_LIST, the shorthand property parser would be assumed to be reusing the longhand parser once per item, not for a whole list, as for properties like background-image or transform-timing-function.)</p>
<p>If the property takes a list of keywords other than inherit/initial/etc., auto, none, or normal (which can be expressed using the VARIANT_* flags), you should use VARIANT_KEYWORD and add a keyword table to the nsCSSProps class.&nbsp; The name of this keyword table needs to go in the nsCSSPropList entry; otherwise that keyword table should be null.&nbsp; If you need a keyword table, you should include auto, normal, or none (if needed) in that table rather than combining VARIANT_KEYWORD with VARIANT_AUTO, VARIANT_NORMAL, or VARIANT_NONE.</p>
<p>If you add a shorthand property, you'll need to use the CSS_PROP_SHORTHAND macro.&nbsp; You'll need to use CSS_PROPERTY_PARSE_FUNCTION as described above; you can't use the other options for shorthands.&nbsp; And for shorthands you also need to include a subproperty table in nsCSSProps, whose name must match the "method" argument&nbsp; in the CSS_PROP_SHORTHAND macro.&nbsp; When writing the custom parsing code for a shorthand, you'll need to ensure that any successful parse sets all of the subproperties of the shorthand (since that's the way shorthands work in CSS).</p>
<p>Some common mistakes to watch out for when writing custom parsing code (which might go away if we redesign the parser along the lines described in css3-syntax):</p>
<ul>
  <li>make sure to call SkipUntil() to look for the matching close parentheses, braces, or brackets whenever you hit an error inside of them.</li>
  <li>make sure to call UngetToken() if you have called GetToken() but the result is unexpected.&nbsp; This is particularly important in case the unexpected token is a parenthesis or something else that requires matching the other half of a pair.</li>
  <li>if you're adding a preference-controlled property to a non-preference-controlled shorthand, you need to call AppendValue for that property if and only if its preference is enabled (and see next point)</li>
  <li>if you need to check a preference in custom parsing code (e.g., because you're adding a new property to a shorthand, but only conditionally on that property's preference), call nsCSSProps::IsEnabled(), which is faster than calling into the preferences code</li>
</ul>
<p>For further understanding of how the parsing code works, you should read and understand the code in nsCSSParser.cpp.</p>
<h2 id="Computed_Style">Computed Style</h2>
<p>add member variables to style structs, fix constructor and copy constructor</p>
<p>change hints and CalcDifference (see nsChangeHint.h and the code that implements change hint propcessing)</p>
<p>rule node</p>
<p>nsComputedDOMStyle</p>
Revert to this revision