I think you may also need to add some custom code to nsCSSValue::AppendToString(), with a call to AppendBitmaskCSSValue() similar to your DoGetContain() code. (This is for serializing a specified-style representation, e.g. if someone sets an element's "style" attribute and then tries to read back elem.style.contain.)
(Once you've enabled this property in mochitests via testing/profiles/prefs_general.js, you'll likely hit test-failures that are pointing to this missing serialization code.)

It seems that if I remove VARIANT_HK, as in the patch above, that tab-completion of the property in the style editor no longer works. This is because of
> layout/inspector/inDOMUtils.cpp:870
> if (propertyParserVariant & VARIANT_KEYWORD) {
> GetKeywordsForProperty(propertyID, array);
> }
I assume this means I should add VARIANT_HK back in?

I suppose it's worth throwing VARIANT_HK back in there for now, though, since it's not entirely unused as I'd thought.
(Could you check some other properties from nsCSSPropList.h that (a) include a keyword table in their macro-blurb, and (b) have 0 instead of VARIANT_HK, and see if they have this problem too? e.g. "image-orientation: from-image". If so, please file a bug in Core|CSS on fixing those.)

(In reply to Daniel Holbert [:dholbert] from comment #15)
> >+ aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT |
> >+ NS_STYLE_CONTAIN_EVERYTHING, eCSSUnit_Enumerated);
>
> Add a comment before the SetIntValue call here, e.g.:
Sorry to trail off there -- I forgot to paste in the my draft sample-comment :)
Sample comment:
// 'strict' implies all the other keyword values. We keep
// the 'strict' bit set as well, for use when re-serializing.

This version passes the tests, and everything seems to work well when I experimented with it in the browser.
If you think I should remove / change the assertions I added, please let me know.
The reason to the strange change in the devtools test is that that test previously depended on there only being 6 CSS properties which began with 'co'.

Comment on attachment 8615543[details][diff][review]
ParseContain
Looks good! Just some minor stylistic nits. r=me with the following addressed:
>+++ b/layout/style/nsCSSParser.cpp
>+bool
>+CSSParserImpl::ParseContain(nsCSSValue& aValue)
>+{
>+ static const int32_t maskContain[] = {
>+ MASK_END_VALUE
>+ };
>+
>+ if (ParseVariant(aValue,
>+ VARIANT_INHERIT | VARIANT_NONE,
>+ nullptr)) {
>+ return true;
>+ }
Looks like this ParseVariant(...) call can be collapsed to one line -- let's do that, for brevity.
>+ if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable,
>+ maskContain)) {
Fix indentation here. Also, declare maskContain immediately before this line, since it's not used until this point (and it's short), to make its purpose more clear. Also, I'd say we should just make it a one-liner, e.g.
static const int32_t maskContain[] = { MASK_END_VALUE };
...since it doesn't actually have any useful entries to list out, so there's no benefit from making it a multi-line thing.
>+++ b/layout/style/nsCSSPropList.h
>+CSS_PROP_DISPLAY(
>+ contain,
>+ contain,
>+ Contain,
>+ CSS_PROPERTY_PARSE_VALUE |
>+ CSS_PROPERTY_VALUE_PARSER_FUNCTION,
>+ "layout.css.contain.enabled",
>+ // Does not affect parsing, but is needed for tab completion.
>+ VARIANT_HK | VARIANT_NONE,
End this comment with a ":" to make it clearer that it's talking about the next line.
Also, s/tab completion/tab completion in devtools/
(Remember to file a bug on fixing other properties that have broken tab-completion due to this, too.)
>+ kContainKTable,
>+ offsetof(nsStyleDisplay, mContain),
>+ eStyleAnimType_None)
I'm pretty sure this "offsetof" parameter is only used if we have a non-"eStyleAnimType_None" animtype.
So: given that this is eStyleAnimType_None, let's just use CSS_PROP_NO_OFFSET instead of offsetof(...). We do this for a lot of other keyword-valued properties, and I think that's probably the pattern we should follow. (I think we do what you've got here for some properties, too, but I think those places should probably be simplified to use CSS_PROP_NO_OFFSET as well.)
>+++ b/layout/style/nsCSSProps.cpp
>+const KTableValue nsCSSProps::kContainKTable[] = {
>+ eCSSKeyword_none, NS_STYLE_CONTAIN_NONE,
>+ eCSSKeyword_strict, NS_STYLE_CONTAIN_STRICT,
>+ eCSSKeyword_layout, NS_STYLE_CONTAIN_LAYOUT,
>+ eCSSKeyword_style, NS_STYLE_CONTAIN_STYLE,
>+ eCSSKeyword_paint, NS_STYLE_CONTAIN_PAINT,
>+ eCSSKeyword_UNKNOWN,-1
>+};
Reduce the amount of whitespace between the entries here (by ~12 space characters I think).
(The list-alignment is nice for readability, but with too much space, it's harder to scan from left value to right value)
>+++ b/layout/style/nsCSSValue.cpp
>+ case eCSSProperty_contain:
>+ if (intValue & NS_STYLE_CONTAIN_STRICT) {
>+ NS_ASSERTION(intValue == (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS),
>+ "contain: strict should imply contain: layout style paint.");
Nit: drop the period at the end of the assertion message. (When an assertion fails, we print it with a semicolon appended to the end of the message, IIRC; so if the message already ends with a period, it ends up ending with ".;" which looks weird.)
>+ nsStyleUtil::AppendBitmaskCSSValue(aProperty,
>+ intValue,
>+ NS_STYLE_CONTAIN_STRICT,
>+ NS_STYLE_CONTAIN_PAINT,
>+ aResult);
>+ break;
I don't think we need the de-indentation here -- doesn't look like we're at risk of going over 80 characters. So, just align the args below "aProperty".
>+++ b/layout/style/nsComputedDOMStyle.cpp
>+ } 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.");
As above, drop the final "."
>+ } else {
>+ nsAutoString valueStr;
>+
>+ nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_contain,
>+ mask, NS_STYLE_CONTAIN_LAYOUT,
>+ NS_STYLE_CONTAIN_PAINT, valueStr);
As above, I don't think there's a need for this de-indentation here -- so, don't de-indent the later function-args; align them below the 1st one.