Bug 1477524 - Update quotes and counters to work trough the flattened tree r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D146693
This commit is contained in:
Martin Robinson 2022-05-26 11:15:16 +00:00
Родитель 8e9eb86f9b
Коммит 786890b5c4
16 изменённых файлов: 435 добавлений и 17 удалений

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

@ -1040,6 +1040,30 @@ void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
ExtendedContentSlots()->mAssignedSlot = aSlot;
}
Maybe<uint32_t> nsIContent::ComputeFlatTreeIndexOf(
const nsINode* aPossibleChild) const {
if (!aPossibleChild) {
return Nothing();
}
if (aPossibleChild->GetFlattenedTreeParentNode() != this) {
return Nothing();
}
FlattenedChildIterator iter(this);
uint32_t index = 0u;
for (nsIContent* child = iter.GetNextChild(); child;
child = iter.GetNextChild()) {
if (child == aPossibleChild) {
return Some(index);
}
++index;
}
return Nothing();
}
#ifdef MOZ_DOM_LIST
void nsIContent::Dump() { List(); }
#endif

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

@ -372,6 +372,17 @@ class nsIContent : public nsINode {
*/
inline nsIContent* GetFlattenedTreeParent() const;
/**
* Get the index of a child within this content's flat tree children.
*
* @param aPossibleChild the child to get the index of.
* @return the index of the child, or Nothing if not a child. Be aware that
* anonymous children (e.g. a <div> child of an <input> element) will
* result in Nothing.
*/
mozilla::Maybe<uint32_t> ComputeFlatTreeIndexOf(
const nsINode* aPossibleChild) const;
protected:
// Handles getting inserted or removed directly under a <slot> element.
// This is meant to only be called from the two functions below.

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

@ -32,6 +32,7 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/CharacterData.h"
#include "mozilla/dom/ChildIterator.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DebuggerNotificationBinding.h"
#include "mozilla/dom/DocumentType.h"
@ -147,6 +148,17 @@ bool nsINode::IsInclusiveDescendantOf(const nsINode* aNode) const {
return false;
}
bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const {
MOZ_ASSERT(aNode, "The node is nullptr.");
for (nsINode* node : InclusiveFlatTreeAncestors(*this)) {
if (node == aNode) {
return true;
}
}
return false;
}
bool nsINode::IsShadowIncludingInclusiveDescendantOf(
const nsINode* aNode) const {
MOZ_ASSERT(aNode, "The node is nullptr.");

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

@ -447,6 +447,14 @@ class nsINode : public mozilla::dom::EventTarget {
*/
bool IsShadowIncludingInclusiveDescendantOf(const nsINode* aNode) const;
/**
* Returns true if the given node is this node or one of its descendants
* in the "flat tree."
*
* @param aNode must not be nullptr.
*/
bool IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const;
/**
* Return this node as a document fragment. Asserts IsDocumentFragment().
*

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

@ -131,6 +131,20 @@ void nsCounterUseNode::GetText(WritingMode aWM, CounterStyle* aStyle,
}
}
static const nsIContent* GetParentContentForScope(nsIFrame* frame) {
// We do not want elements with `display: contents` to establish scope for
// counters. We'd like to do something like
// `nsIFrame::GetClosestFlattenedTreeAncestorPrimaryFrame()` above, but this
// may be called before the primary frame is set on frames.
nsIContent* content = frame->GetContent()->GetFlattenedTreeParent();
while (content && content->IsElement() &&
content->AsElement()->IsDisplayContents()) {
content = content->GetFlattenedTreeParent();
}
return content;
}
void nsCounterList::SetScope(nsCounterNode* aNode) {
// This function is responsible for setting |mScopeStart| and
// |mScopePrev| (whose purpose is described in nsCounterManager.h).
@ -206,9 +220,13 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
// Get the content node for aNode's rendering object's *parent*,
// since scope includes siblings, so we want a descendant check on
// parents.
nsIContent* nodeContent = aNode->mPseudoFrame->GetContent()->GetParent();
// parents. Note here that mPseudoFrame is a bit of a misnomer, as it
// might not be a pseudo element at all, but a normal element that
// happens to increment a counter. We want to respect the flat tree
// here, but skipping any <slot> element that happens to contain
// mPseudoFrame. That's why this uses GetInFlowParent() instead
// of GetFlattenedTreeParent().
const nsIContent* nodeContent = GetParentContentForScope(aNode->mPseudoFrame);
for (nsCounterNode *prev = Prev(aNode), *start; prev;
prev = start->mScopePrev) {
// If |prev| starts a scope (because it's a real or implied
@ -221,7 +239,8 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
: prev->mScopeStart;
// |startContent| is analogous to |nodeContent| (see above).
nsIContent* startContent = start->mPseudoFrame->GetContent()->GetParent();
const nsIContent* startContent =
GetParentContentForScope(start->mPseudoFrame);
NS_ASSERTION(nodeContent || !startContent,
"null check on startContent should be sufficient to "
"null check nodeContent as well, since if nodeContent "
@ -233,8 +252,8 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
nodeContent == startContent) &&
// everything is inside the root (except the case above,
// a second reset on the root)
// FIXME(bug 1477524): should use flattened tree here:
(!startContent || nodeContent->IsInclusiveDescendantOf(startContent))) {
(!startContent ||
nodeContent->IsInclusiveFlatTreeDescendantOf(startContent))) {
aNode->mScopeStart = start;
aNode->mScopePrev = prev;
didSetScopeFor(aNode);

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

@ -1119,10 +1119,10 @@ int32_t nsLayoutUtils::DoCompareTreePosition(
MOZ_ASSERT(aContent1, "aContent1 must not be null");
MOZ_ASSERT(aContent2, "aContent2 must not be null");
AutoTArray<nsINode*, 32> content1Ancestors;
nsINode* c1;
AutoTArray<nsIContent*, 32> content1Ancestors;
nsIContent* c1;
for (c1 = aContent1; c1 && c1 != aCommonAncestor;
c1 = c1->GetParentOrShadowHostNode()) {
c1 = c1->GetFlattenedTreeParent()) {
content1Ancestors.AppendElement(c1);
}
if (!c1 && aCommonAncestor) {
@ -1131,10 +1131,10 @@ int32_t nsLayoutUtils::DoCompareTreePosition(
aCommonAncestor = nullptr;
}
AutoTArray<nsINode*, 32> content2Ancestors;
nsINode* c2;
AutoTArray<nsIContent*, 32> content2Ancestors;
nsIContent* c2;
for (c2 = aContent2; c2 && c2 != aCommonAncestor;
c2 = c2->GetParentOrShadowHostNode()) {
c2 = c2->GetFlattenedTreeParent()) {
content2Ancestors.AppendElement(c2);
}
if (!c2 && aCommonAncestor) {
@ -1146,8 +1146,8 @@ int32_t nsLayoutUtils::DoCompareTreePosition(
int last1 = content1Ancestors.Length() - 1;
int last2 = content2Ancestors.Length() - 1;
nsINode* content1Ancestor = nullptr;
nsINode* content2Ancestor = nullptr;
nsIContent* content1Ancestor = nullptr;
nsIContent* content2Ancestor = nullptr;
while (last1 >= 0 && last2 >= 0 &&
((content1Ancestor = content1Ancestors.ElementAt(last1)) ==
(content2Ancestor = content2Ancestors.ElementAt(last2)))) {
@ -1171,7 +1171,7 @@ int32_t nsLayoutUtils::DoCompareTreePosition(
// content1Ancestor != content2Ancestor, so they must be siblings with the
// same parent
nsINode* parent = content1Ancestor->GetParentOrShadowHostNode();
nsIContent* parent = content1Ancestor->GetFlattenedTreeParent();
#ifdef DEBUG
// TODO: remove the uglyness, see bug 598468.
NS_ASSERTION(gPreventAssertInCompareTreePosition || parent,
@ -1181,8 +1181,10 @@ int32_t nsLayoutUtils::DoCompareTreePosition(
return 0;
}
const Maybe<uint32_t> index1 = parent->ComputeIndexOf(content1Ancestor);
const Maybe<uint32_t> index2 = parent->ComputeIndexOf(content2Ancestor);
const Maybe<uint32_t> index1 =
parent->ComputeFlatTreeIndexOf(content1Ancestor);
const Maybe<uint32_t> index2 =
parent->ComputeFlatTreeIndexOf(content2Ancestor);
// None of the nodes are anonymous, just do a regular comparison.
if (index1.isSome() && index2.isSome()) {

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

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Quote scope Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-content-3/#quote-values">
<style>
q {
quotes: '1''1''2''2''3''3';
}
</style>
</head>
<body>
<q>
<q>Quote</q>
</q>
</body>
</html>

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

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>Quote scope Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-content-3/#quote-values">
<link rel="match" href="quotes-slot-scoping-ref.html">
</head>
<body>
<div id="host">
<q slot="quote">Quote</q>
</div>
<script>
function makeStyle() {
let style = document.createElement('style');
style.textContent = `
q {
quotes: '1' '1' '2' '2' '3' '3';
}
`;
return style;
}
document.body.appendChild(makeStyle());
const shadowRoot = document
.getElementById('host')
.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<q>
<slot name="quote"></slot>
</q>
`;
shadowRoot.appendChild(makeStyle());
</script>
</body>
</html>

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

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML LI element: counter order with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<style>
li::before { content: counters(list-item,'.') ' '; }
</style>
</head>
<body>
<ol>
<li>One</li>
<li>Two</li>
<ol>
<li>Two Point One</li>
</ol>
</ol>
</body>
</html>

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

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML LI element: counter order with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<link rel="match" href="counter-list-item-slot-order-ref.html">
<style>
li::before { content: counters(list-item,'.') ' '; }
</style>
</head>
<body>
<div id="host">
<li slot="list3">Two Point One</li>
<li slot="list2">Two</li>
<li slot="list1">One</li>
</div>
<script>
const shadowRoot = document
.getElementById('host')
.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<ol>
<slot name="list1"></slot>
<slot name="list2"></slot>
<ol>
<slot name="list3"></slot>
</ol>
</ol>
`;
</script>
</body>
</html>

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

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<style>
.counted {
counter-increment: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
</style>
</head>
<body>
<div>
<div class="counted">One</div>
<div class="counted">Two</div>
<div class="counted">Three</div>
</div>
</body>
</html>

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

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order with "display: contents"</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<link rel="match" href="counter-order-display-contents-ref.html">
</head>
<body>
<style>
.counted {
counter-increment: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
</style>
<div style="display: contents;">
<div class="counted">One</div>
</div>
<div style="display: contents;">
<div class="counted">Two</div>
</div>
<div style="display: contents;">
<div class="counted">Three</div>
</div>
</body>
</html>

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

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<style>
.counted {
counter-increment: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
</style>
</head>
<body>
<div>
<div class="counted">One</div>
<div class="counted">Two</div>
<div class="counted">Three</div>
</div>
</body>
</html>

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

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order and scope with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<style>
.counted {
counter-increment: section;
}
.reset {
counter-reset: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
</style>
</head>
<body>
<div>
<div class="counted">One</div>
<div class="counted">Two</div>
<div class="counted">Three</div>
<div class="reset">Reset1</div>
<div class="counted">Four</div>
<div class="counted">Five</div>
<div class="counted">Six</div>
<div class="reset">Reset2</div>
<div class="counted">Seven</div>
<div class="counted">Eight</div>
<div class="counted">Nine</div>
</div>
</body>
</html>

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

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order and scope with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<link rel="match" href="counter-slot-order-scoping-ref.html">
</head>
<body>
<div id="host">
<div class="counted" slot="content3">Three</div>
<div class="counted" slot="content1">One</div>
<div class="counted" slot="content6">Six</div>
<div class="counted" slot="content4">Four</div>
<div style="counter-reset: section;" slot="reset">Reset2</div>
<div class="counted" slot="content9">Nine</div>
<div class="counted" slot="content7">Seven</div>
</div>
<script>
function makeStyle() {
let style = document.createElement('style');
style.textContent = `
.counted {
counter-increment: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
`;
return style;
}
document.body.appendChild(makeStyle());
const shadowRoot = document
.getElementById('host')
.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div>
<slot name="content1"></slot>
<div class="counted" slot="content2">Two</div>
<slot name="content3"></slot>
<div style="counter-reset: section;">Reset1</div>
<slot name="content4"></slot>
<div class="counted" slot="content5">Five</div>
<slot name="content6"></slot>
<slot name="reset"></slot>
<slot name="content7"></slot>
<div class="counted" slot="content8">Eight</div>
<slot name="content9"></slot>
</div>
`;
shadowRoot.appendChild(makeStyle());
</script>
</body>
</html>

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

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS counter order and scope with Shadow DOM and SLOT</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://www.w3.org/TR/css-lists-3/#inheriting-counters">
<link rel="match" href="counter-slot-order-ref.html">
<style>
.counted {
counter-increment: section;
}
.counted::before {
content: "C=" counter(section) " ";
}
</style>
</head>
<body>
<div id="host">
<div class="counted" slot="content3">Three</div>
<div class="counted" slot="content2">Two</div>
<div class="counted" slot="content1">One</div>
</div>
<script>
const shadowRoot = document
.getElementById('host')
.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div>
<slot name="content1"></slot>
<slot name="content2"></slot>
<slot name="content3"></slot>
</div>
`;
</script>
</body>
</html>