зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
8e9eb86f9b
Коммит
786890b5c4
|
@ -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>
|
Загрузка…
Ссылка в новой задаче