Bug 288704 part 2 - [css-lists] Implement display:list-item counters using a built-in 'list-item' CSS counter. r=emilio

This commit is contained in:
Mats Palmgren 2019-03-24 23:13:52 +01:00
Родитель f46136021c
Коммит c750eaac07
26 изменённых файлов: 349 добавлений и 18 удалений

Просмотреть файл

@ -67,6 +67,18 @@ void HTMLLIElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
value->GetEnumValue());
}
// Map <li value=INTEGER> to 'counter-set: list-item INTEGER;
// counter-increment: list-item 0;'.
const nsAttrValue* attrVal = aAttributes->GetAttr(nsGkAtoms::value);
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
if (!aDecls.PropertyIsSet(eCSSProperty_counter_set)) {
aDecls.SetCounterSetListItem(attrVal->GetIntegerValue());
}
if (!aDecls.PropertyIsSet(eCSSProperty_counter_increment)) {
aDecls.SetCounterIncrementListItem(0);
}
}
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
}
@ -74,6 +86,7 @@ NS_IMETHODIMP_(bool)
HTMLLIElement::IsAttributeMapped(const nsAtom* aAttribute) const {
static const MappedAttributeEntry attributes[] = {
{nsGkAtoms::type},
{nsGkAtoms::value},
{nullptr},
};

Просмотреть файл

@ -59,6 +59,8 @@ bool HTMLSharedListElement::ParseAttribute(
return aResult.ParseEnumValue(aValue, kListTypeTable, false) ||
aResult.ParseEnumValue(aValue, kOldListTypeTable, true);
}
}
if (mNodeInfo->Equals(nsGkAtoms::ol)) {
if (aAttribute == nsGkAtoms::start) {
return aResult.ParseIntValue(aValue);
}
@ -72,7 +74,6 @@ bool HTMLSharedListElement::ParseAttribute(
void HTMLSharedListElement::MapAttributesIntoRule(
const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
if (!aDecls.PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value && value->Type() == nsAttrValue::eEnum) {
aDecls.SetKeywordValue(eCSSProperty_list_style_type,
@ -83,9 +84,34 @@ void HTMLSharedListElement::MapAttributesIntoRule(
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
}
void HTMLSharedListElement::MapOLAttributesIntoRule(
const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
if (!aDecls.PropertyIsSet(eCSSProperty_counter_reset)) {
const nsAttrValue* startAttr = aAttributes->GetAttr(nsGkAtoms::start);
bool haveStart = startAttr && startAttr->Type() == nsAttrValue::eInteger;
int32_t start = 0;
if (haveStart) {
start = startAttr->GetIntegerValue() - 1;
}
bool haveReversed = !!aAttributes->GetAttr(nsGkAtoms::reversed);
if (haveReversed) {
if (haveStart) {
start += 2; // i.e. the attr value + 1
} else {
start = std::numeric_limits<int32_t>::min();
}
}
if (haveStart || haveReversed) {
aDecls.SetCounterResetListItem(start);
}
}
HTMLSharedListElement::MapAttributesIntoRule(aAttributes, aDecls);
}
NS_IMETHODIMP_(bool)
HTMLSharedListElement::IsAttributeMapped(const nsAtom* aAttribute) const {
if (mNodeInfo->Equals(nsGkAtoms::ol) || mNodeInfo->Equals(nsGkAtoms::ul)) {
if (mNodeInfo->Equals(nsGkAtoms::ul)) {
static const MappedAttributeEntry attributes[] = {{nsGkAtoms::type},
{nullptr}};
@ -97,14 +123,31 @@ HTMLSharedListElement::IsAttributeMapped(const nsAtom* aAttribute) const {
return FindAttributeDependence(aAttribute, map);
}
if (mNodeInfo->Equals(nsGkAtoms::ol)) {
static const MappedAttributeEntry attributes[] = {{nsGkAtoms::type},
{nsGkAtoms::start},
{nsGkAtoms::reversed},
{nullptr}};
static const MappedAttributeEntry* const map[] = {
attributes,
sCommonAttributeMap,
};
return FindAttributeDependence(aAttribute, map);
}
return nsGenericHTMLElement::IsAttributeMapped(aAttribute);
}
nsMapRuleToAttributesFunc HTMLSharedListElement::GetAttributeMappingFunction()
const {
if (mNodeInfo->Equals(nsGkAtoms::ol) || mNodeInfo->Equals(nsGkAtoms::ul)) {
if (mNodeInfo->Equals(nsGkAtoms::ul)) {
return &MapAttributesIntoRule;
}
if (mNodeInfo->Equals(nsGkAtoms::ol)) {
return &MapOLAttributesIntoRule;
}
return nsGenericHTMLElement::GetAttributeMappingFunction();
}

Просмотреть файл

@ -58,6 +58,8 @@ class HTMLSharedListElement final : public nsGenericHTMLElement {
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
MappedDeclarations&);
static void MapOLAttributesIntoRule(const nsMappedAttributes* aAttributes,
MappedDeclarations&);
};
} // namespace dom

Просмотреть файл

@ -18,6 +18,7 @@
#include "mozilla/dom/GeneratedImageContent.h"
#include "mozilla/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "mozilla/dom/HTMLSharedListElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/EventStates.h"
#include "mozilla/Likely.h"
@ -9589,9 +9590,21 @@ inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
bool listItemListIsDirty = false;
for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
"Needed pseudos didn't get created; expect bad things");
// display:list-item boxes affects the start value of the "list-item" counter
// when an <ol reversed> element doesn't have an explicit start value.
if (!listItemListIsDirty &&
iter.item().mComputedStyle->StyleList()->mMozListReversed
== StyleMozListReversed::True &&
iter.item().mComputedStyle->StyleDisplay()->mDisplay == StyleDisplay::ListItem) {
auto* list = mCounterManager.CounterListFor(NS_LITERAL_STRING("list-item"));
list->SetDirty();
CountersDirty();
listItemListIsDirty = true;
}
ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
}

Просмотреть файл

@ -56,7 +56,9 @@ void nsCounterUseNode::Calc(nsCounterList* aList) {
// Should be called immediately after calling |Insert|.
void nsCounterChangeNode::Calc(nsCounterList* aList) {
NS_ASSERTION(!aList->IsDirty(), "Why are we calculating with a dirty list?");
if (mType == RESET || mType == SET) {
if (IsContentBasedReset()) {
// RecalcAll takes care of this case.
} else if (mType == RESET || mType == SET) {
mValueAfter = mChangeValue;
} else {
NS_ASSERTION(mType == INCREMENT, "invalid type");
@ -156,8 +158,21 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
void nsCounterList::RecalcAll() {
mDirty = false;
// Setup the scope and calculate the default start value for <ol reversed>.
for (nsCounterNode* node = First(); node; node = Next(node)) {
SetScope(node);
if (node->IsContentBasedReset()) {
node->mValueAfter = 1;
} else if ((node->mType == nsCounterChangeNode::INCREMENT ||
node->mType == nsCounterChangeNode::SET) &&
node->mScopeStart &&
node->mScopeStart->IsContentBasedReset()) {
++node->mScopeStart->mValueAfter;
}
}
for (nsCounterNode* node = First(); node; node = Next(node)) {
auto oldValue = node->mValueAfter;
node->Calc(this);
if (node->mType == nsCounterNode::USE) {
@ -171,6 +186,14 @@ void nsCounterList::RecalcAll() {
useNode->mText->SetData(text, IgnoreErrors());
}
}
if (oldValue != node->mValueAfter && node->mPseudoFrame &&
node->mPseudoFrame->StyleDisplay()->mDisplay == StyleDisplay::ListItem) {
auto* shell = node->mPseudoFrame->PresShell();
shell->FrameNeedsReflow(node->mPseudoFrame,
nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
}
}

Просмотреть файл

@ -72,6 +72,9 @@ struct nsCounterNode : public nsGenConNode {
// to avoid virtual function calls in the common case
inline void Calc(nsCounterList* aList);
// Is this a <ol reversed> RESET node?
inline bool IsContentBasedReset();
};
struct nsCounterUseNode : public nsCounterNode {
@ -150,6 +153,11 @@ inline void nsCounterNode::Calc(nsCounterList* aList) {
ChangeNode()->Calc(aList);
}
inline bool nsCounterNode::IsContentBasedReset() {
return mType == RESET &&
ChangeNode()->mChangeValue == std::numeric_limits<int32_t>::min();
}
class nsCounterList : public nsGenConList {
public:
nsCounterList() : nsGenConList(), mDirty(false) {}

Просмотреть файл

@ -999,8 +999,8 @@ fuzzy-if(Android,0-11,0-17) fuzzy-if(webrender,0-1,0-10) == 413361-1.html 413361
== 417178-1.html 417178-1-ref.html
== 417246-1.html 417246-1-ref.html
== 417676.html 417676-ref.html
asserts(1) == 418574-1.html 418574-1-ref.html # bug 478135
asserts(1) == 418574-2.html 418574-2-ref.html # bug 478135
== 418574-1.html 418574-1-ref.html
== 418574-2.html 418574-2-ref.html
== 418766-1a.html 418766-1-ref.html
== 418766-1b.html 418766-1-ref.html
== 419060.html 419060-ref.html

Просмотреть файл

@ -5,5 +5,5 @@
2147483647
2147483647
-2147483647
-2147483647
-2147483647
0
0

Просмотреть файл

@ -10,5 +10,6 @@ span::after { content: counter(c); }
<span style="counter-reset: c 2147483648"></span>
<span style="counter-reset: c 2147483649"></span>
<span style="counter-reset: c -2147483647"></span>
<!-- The next two computes to std::numeric_limits<int32_t>::min() which we use as the "magic" number for the content based <ol reversed> start value. See https://drafts.csswg.org/css-lists-3/#ua-stylesheet -->
<span style="counter-reset: c -2147483648"></span>
<span style="counter-reset: c -2147483649"></span>

Просмотреть файл

@ -4,7 +4,7 @@ fuzzy-if(OSX,0-11,0-1) == numbering-3.html numbering-3-ref.html
fuzzy-if(OSX,0-76,0-2) == numbering-4.html numbering-4-ref.html
== numbering-5.html numbering-5-ref.html
== ol-reversed-1a.html ol-reversed-1-ref.html
asserts(1) == ol-reversed-1b.html ol-reversed-1-ref.html # bug 478135
== ol-reversed-1b.html ol-reversed-1-ref.html
== ol-reversed-1c.html ol-reversed-1-ref.html
== ol-reversed-2.html ol-reversed-2-ref.html
== ol-reversed-3.html ol-reversed-3-ref.html

Просмотреть файл

@ -96,6 +96,21 @@ class MappedDeclarations final {
Servo_DeclarationBlock_SetIntValue(mDecl, aId, aValue);
}
// Set "counter-reset: list-item <integer>".
void SetCounterResetListItem(int32_t aValue) {
Servo_DeclarationBlock_SetCounterResetListItem(mDecl, aValue);
}
// Set "counter-set: list-item <integer>".
void SetCounterSetListItem(int32_t aValue) {
Servo_DeclarationBlock_SetCounterSetListItem(mDecl, aValue);
}
// Set "counter-increment: list-item <integer>".
void SetCounterIncrementListItem(int32_t aValue) {
Servo_DeclarationBlock_SetCounterIncrementListItem(mDecl, aValue);
}
// Set a property to a pixel value
void SetPixelValue(nsCSSPropertyID aId, float aValue) {
Servo_DeclarationBlock_SetPixelValue(mDecl, aId, aValue);

Просмотреть файл

@ -700,6 +700,15 @@ void Servo_DeclarationBlock_SetIntValue(
const RawServoDeclarationBlock* declarations, nsCSSPropertyID property,
int32_t value);
void Servo_DeclarationBlock_SetCounterResetListItem(
const RawServoDeclarationBlock* declarations, int32_t value);
void Servo_DeclarationBlock_SetCounterSetListItem(
const RawServoDeclarationBlock* declarations, int32_t value);
void Servo_DeclarationBlock_SetCounterIncrementListItem(
const RawServoDeclarationBlock* declarations, int32_t value);
void Servo_DeclarationBlock_SetPixelValue(
const RawServoDeclarationBlock* declarations, nsCSSPropertyID property,
float value);

Просмотреть файл

@ -580,6 +580,7 @@ menu[type="context"] {
}
ul, ol, menu {
counter-reset: list-item;
-moz-list-reversed: false;
}
@ -783,6 +784,7 @@ video > .caption-box {
details > summary:first-of-type,
details > summary:-moz-native-anonymous {
display: list-item;
counter-increment: list-item 0;
list-style: disclosure-closed inside;
}

Просмотреть файл

@ -708,6 +708,47 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
}
}
/// For HTML elements with 'display:list-item' we add a default 'counter-increment:list-item'
/// unless 'counter-increment' already has a value for 'list-item'.
///
/// https://drafts.csswg.org/css-lists-3/#declaring-a-list-item
#[cfg(feature = "gecko")]
fn adjust_for_list_item<E>(&mut self, element: Option<E>)
where
E: TElement,
{
use crate::properties::longhands::counter_increment::computed_value::T as ComputedIncrement;
use crate::values::CustomIdent;
use crate::values::generics::counters::{CounterPair};
use crate::values::specified::list::MozListReversed;
if self.style.get_box().clone_display() != Display::ListItem {
return;
}
if self.style.pseudo.is_some() {
return;
}
if !element.map_or(false, |e| e.is_html_element()) {
return;
}
// Note that we map <li value=INTEGER> to 'counter-set: list-item INTEGER;
// counter-increment: list-item 0;' so we'll return here unless the author
// explicitly specified something else.
let increments = self.style.get_counters().clone_counter_increment();
if increments.iter().any(|i| i.name.0 == atom!("list-item")) {
return;
}
let reversed = self.style.get_list().clone__moz_list_reversed() == MozListReversed::True;
let increment = if reversed { -1 } else { 1 };
let list_increment = CounterPair {
name: CustomIdent(atom!("list-item")),
value: increment,
};
let increments = increments.iter().cloned().chain(std::iter::once(list_increment));
self.style.mutate_counters().set_counter_increment(ComputedIncrement::new(increments.collect()));
}
/// Adjusts the style to account for various fixups that don't fit naturally
/// into the cascade.
///
@ -772,6 +813,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
#[cfg(feature = "gecko")]
{
self.adjust_for_appearance(element);
self.adjust_for_list_item(element);
}
self.set_bits();
}

Просмотреть файл

@ -4381,6 +4381,57 @@ pub extern "C" fn Servo_DeclarationBlock_SetIntValue(
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetCounterResetListItem(
declarations: &RawServoDeclarationBlock,
counter_value: i32,
) {
use style::values::generics::counters::{CounterPair, CounterSetOrReset};
use style::properties::{PropertyDeclaration};
let prop = PropertyDeclaration::CounterReset(CounterSetOrReset::new(vec![CounterPair {
name: CustomIdent(atom!("list-item")),
value: style::values::specified::Integer::new(counter_value),
}]));
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
decls.push(prop, Importance::Normal);
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetCounterSetListItem(
declarations: &RawServoDeclarationBlock,
counter_value: i32,
) {
use style::values::generics::counters::{CounterPair, CounterSetOrReset};
use style::properties::{PropertyDeclaration};
let prop = PropertyDeclaration::CounterSet(CounterSetOrReset::new(vec![CounterPair {
name: CustomIdent(atom!("list-item")),
value: style::values::specified::Integer::new(counter_value),
}]));
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
decls.push(prop, Importance::Normal);
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetCounterIncrementListItem(
declarations: &RawServoDeclarationBlock,
counter_value: i32,
) {
use style::values::generics::counters::{CounterPair, CounterIncrement};
use style::properties::{PropertyDeclaration};
let prop = PropertyDeclaration::CounterIncrement(CounterIncrement::new(vec![CounterPair {
name: CustomIdent(atom!("list-item")),
value: style::values::specified::Integer::new(counter_value),
}]));
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
decls.push(prop, Importance::Normal);
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(
declarations: &RawServoDeclarationBlock,

Просмотреть файл

@ -1,2 +0,0 @@
[c561-list-displ-000.xht]
expected: FAIL

Просмотреть файл

@ -0,0 +1,2 @@
[inheritance.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1405176

Просмотреть файл

@ -1,2 +0,0 @@
[legend-list-item-numbering.html]
expected: FAIL

Просмотреть файл

@ -1,2 +0,0 @@
[grouping-li-reftest-list-owner-mixed.html]
expected: FAIL

Просмотреть файл

@ -1,2 +0,0 @@
[grouping-li-reftest-list-owner-not-dir.html]
expected: FAIL

Просмотреть файл

@ -29,7 +29,6 @@
li:before
{
content: counter(list-item) ". ";
counter-increment: list-item;
}
]]></style>

Просмотреть файл

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Reference:_CSS Lists: 'counter-increment:list-item' on LI</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
body { margin-left: 10em; }
</style>
</head>
<body>
<ol><li value=0>a<li value=4>b<li value=4>c</ol>
<ol><li value=0>a<li value=9>b<li value=9>c</ol>
<ol><li value=-1>a<li value=3>b<li value=2>c</ol>
<ol><li value=0>a<li value=4>b<li value=4>c</ol>
<ol><li value=2>a<li value=6>b<li value=8>c</ol>
</body>
</html>

Просмотреть файл

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>CSS Lists: 'counter-increment:list-item' on LI</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
<link rel="match" href="li-list-item-counter-ref.html">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
body { margin-left: 10em; }
li { counter-increment: list-item 0; }
.dec { counter-increment:list-item -1; }
.zero { counter-increment:list-item 1 list-item -1; }
.two { counter-increment:list-item 3 list-item -1; }
</style>
</head>
<body>
<ol><li>a<li value=4>b<li>c</ol>
<ol><li>a<li value=4 style="counter-increment:list-item 5">b<li>c</ol>
<ol><li class=dec>a<li value=4 class=dec>b<li class=dec>c</ol>
<ol><li class=zero>a<li value=4 class=zero>b<li class=zero>c</ol>
<ol><li class=two>a<li value=4 class=two>b<li class=two>c</ol>
</body>
</html>

Просмотреть файл

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>Reference:_CSS Lists: 'counter-set:list-item' trumps LI @value</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
body { margin-left: 10em; }
</style>
</head>
<body>
<ol><li>a<li value=99>b</ol>
<ol><li>a<li value=149>b</ol>
<ol><li>a<li value=54>b</ol>
<ol><li>a<li value=149>b</ol>
<ol><li>a<li value=51>b</ol>
<ol><li>a<li value=88>b</ol>
</body>
</html>

Просмотреть файл

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html><head>
<meta charset="utf-8">
<title>CSS Lists: 'counter-set:list-item' trumps LI @value</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-set">
<link rel="match" href="li-value-counter-reset-001-ref.html">
<style>
html,body {
color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
}
body { margin-left: 10em; }
li.set { counter-set: list-item 99; }
</style>
</head>
<body>
<ol><li>a<li value=4 class=set>b</ol>
<ol><li>a<li value=4 class=set style="counter-increment:list-item 50">b</ol>
<ol><li>a<li value=4 style="counter-increment:list-item 50">b</ol>
<ol><li>a<li class=set style="counter-increment:list-item 50">b</ol>
<ol><li>a<li style="counter-increment:list-item 50">b</ol>
<ol><li>a<li value=4 class=set style="counter-set:list-item 88">b</ol>
</body>
</html>

Просмотреть файл

@ -1411,6 +1411,7 @@ STATIC_ATOMS = [
Atom("limitingConeAngle", "limitingConeAngle"),
Atom("linear", "linear"),
Atom("linearGradient", "linearGradient"),
Atom("list_item", "list-item"),
Atom("list_style_type", "list-style-type"),
Atom("luminanceToAlpha", "luminanceToAlpha"),
Atom("luminosity", "luminosity"),