Bug 1463600 - Implement CSS 'contain: style' r=emilio
Add an implementation of CSS `contain: style`. This introduces two new data structures, the ContainStyleScope and ContainStyleScopeManager. ContainStyleScope manages one `contain: style` "world" which has its own counter and quote lists. The contents of these lists depend on their parent scopes, but are not affected by their children. ContainStyleScopeManager manages a tree of scopes starting at a root scope which is outside of any `contain: style` element. Scopes are stored in a hash table that is keyed off of the nsIContent which establishes the `contain: style` scope. When modifying quote or content lists, the ContainStyleScopeManager is responsible for finding the appropriate `contain: style` scope to modify. Perhaps the most complex part of this is that counters and quotes have read access to the state of counters and quotes that are in ancestor `contain: style` scopes. In the case of counters, USE nodes that are at the beginning of counter lists might have a counter scope that starts in an ancestor `contain: style` scope. When nsCounterNode::SetScope() is called, the code may look upward in the `contain: style` scope tree to find the start of the counter scope. In the case of quotes, the first node in the quote list must look for the state of quotes in ancestor `contain: style` scopes. Differential Revision: https://phabricator.services.mozilla.com/D149508
This commit is contained in:
@@ -5972,6 +5972,7 @@ exports.CSS_PROPERTIES = {
|
||||
"revert-layer",
|
||||
"size",
|
||||
"strict",
|
||||
"style",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
|
||||
242
layout/base/ContainStyleScopeManager.cpp
Normal file
242
layout/base/ContainStyleScopeManager.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ContainStyleScopeManager.h"
|
||||
|
||||
#include "mozilla/ComputedStyle.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "CounterStyleManager.h"
|
||||
#include "nsCounterManager.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsQuoteList.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsGenConNode* ContainStyleScope::GetPrecedingElementInGenConList(
|
||||
nsGenConList* aList) {
|
||||
auto IsAfter = [this](nsGenConNode* aNode) {
|
||||
return nsLayoutUtils::CompareTreePosition(
|
||||
mContent, aNode->mPseudoFrame->GetContent()) > 0;
|
||||
};
|
||||
return aList->BinarySearch(IsAfter);
|
||||
}
|
||||
|
||||
void ContainStyleScope::RecalcAllCounters() {
|
||||
GetCounterManager().RecalcAll();
|
||||
for (auto* child : mChildren) {
|
||||
child->RecalcAllCounters();
|
||||
}
|
||||
}
|
||||
|
||||
void ContainStyleScope::RecalcAllQuotes() {
|
||||
GetQuoteList().RecalcAll();
|
||||
for (auto* child : mChildren) {
|
||||
child->RecalcAllQuotes();
|
||||
}
|
||||
}
|
||||
|
||||
ContainStyleScope& ContainStyleScopeManager::GetOrCreateScopeForContent(
|
||||
nsIContent* aContent) {
|
||||
for (; aContent; aContent = aContent->GetFlattenedTreeParent()) {
|
||||
auto* element = dom::Element::FromNode(*aContent);
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not allow elements which have `display: contents` to create style
|
||||
// boundaries. See https://github.com/w3c/csswg-drafts/issues/7392.
|
||||
if (element->IsDisplayContents()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* style = Servo_Element_GetMaybeOutOfDateStyle(element);
|
||||
if (!style) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!style->SelfOrAncestorHasContainStyle()) {
|
||||
return GetRootScope();
|
||||
}
|
||||
|
||||
if (!style->StyleDisplay()->IsContainStyle()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto* scope = mScopes.Get(aContent)) {
|
||||
return *scope;
|
||||
}
|
||||
|
||||
auto& parentScope =
|
||||
GetOrCreateScopeForContent(aContent->GetFlattenedTreeParent());
|
||||
return *mScopes.InsertOrUpdate(
|
||||
aContent, MakeUnique<ContainStyleScope>(this, &parentScope, aContent));
|
||||
}
|
||||
|
||||
return GetRootScope();
|
||||
}
|
||||
|
||||
ContainStyleScope& ContainStyleScopeManager::GetScopeForContent(
|
||||
nsIContent* aContent) {
|
||||
MOZ_ASSERT(aContent);
|
||||
|
||||
if (auto* element = dom::Element::FromNode(*aContent)) {
|
||||
if (const auto* style = Servo_Element_GetMaybeOutOfDateStyle(element)) {
|
||||
if (!style->SelfOrAncestorHasContainStyle()) {
|
||||
return GetRootScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; aContent; aContent = aContent->GetFlattenedTreeParent()) {
|
||||
if (auto* scope = mScopes.Get(aContent)) {
|
||||
return *scope;
|
||||
}
|
||||
}
|
||||
|
||||
return GetRootScope();
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::Clear() {
|
||||
GetRootScope().GetQuoteList().Clear();
|
||||
GetRootScope().GetCounterManager().Clear();
|
||||
|
||||
DestroyScope(&GetRootScope());
|
||||
MOZ_DIAGNOSTIC_ASSERT(mScopes.IsEmpty(),
|
||||
"Destroying the root scope should destroy all scopes.");
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::DestroyScopesFor(nsIFrame* aFrame) {
|
||||
if (auto* scope = mScopes.Get(aFrame->GetContent())) {
|
||||
DestroyScope(scope);
|
||||
}
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::DestroyScope(ContainStyleScope* aScope) {
|
||||
// Deleting a scope modifies the array of children in its parent, so we don't
|
||||
// use an iterator here.
|
||||
while (!aScope->GetChildren().IsEmpty()) {
|
||||
DestroyScope(aScope->GetChildren().ElementAt(0));
|
||||
}
|
||||
mScopes.Remove(aScope->GetContent());
|
||||
}
|
||||
|
||||
bool ContainStyleScopeManager::DestroyCounterNodesFor(nsIFrame* aFrame) {
|
||||
bool result = false;
|
||||
for (auto* scope = &GetScopeForContent(aFrame->GetContent()); scope;
|
||||
scope = scope->GetParent()) {
|
||||
result |= scope->GetCounterManager().DestroyNodesFor(aFrame);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ContainStyleScopeManager::AddCounterChanges(nsIFrame* aNewFrame) {
|
||||
return GetOrCreateScopeForContent(
|
||||
aNewFrame->GetContent()->GetFlattenedTreeParent())
|
||||
.GetCounterManager()
|
||||
.AddCounterChanges(aNewFrame);
|
||||
}
|
||||
|
||||
nsCounterList* ContainStyleScopeManager::GetOrCreateCounterList(
|
||||
dom::Element& aElement, nsAtom* aCounterName) {
|
||||
return GetOrCreateScopeForContent(&aElement)
|
||||
.GetCounterManager()
|
||||
.GetOrCreateCounterList(aCounterName);
|
||||
}
|
||||
|
||||
bool ContainStyleScopeManager::CounterDirty(nsAtom* aCounterName) {
|
||||
return mDirtyCounters.Contains(aCounterName);
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::SetCounterDirty(nsAtom* aCounterName) {
|
||||
mDirtyCounters.Insert(aCounterName);
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::RecalcAllCounters() {
|
||||
GetRootScope().RecalcAllCounters();
|
||||
mDirtyCounters.Clear();
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
|
||||
void ContainStyleScopeManager::DumpCounters() {
|
||||
GetRootScope().GetCounterManager().Dump();
|
||||
for (auto& entry : mScopes) {
|
||||
entry.GetWeak()->GetCounterManager().Dump();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
static bool GetFirstCounterValueForScopeAndFrame(ContainStyleScope* aScope,
|
||||
nsIFrame* aFrame,
|
||||
CounterValue& aOrdinal) {
|
||||
if (aScope->GetCounterManager().GetFirstCounterValueForFrame(aFrame,
|
||||
aOrdinal)) {
|
||||
return true;
|
||||
}
|
||||
for (auto* child : aScope->GetChildren()) {
|
||||
if (GetFirstCounterValueForScopeAndFrame(child, aFrame, aOrdinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::GetSpokenCounterText(nsIFrame* aFrame,
|
||||
nsAString& aText) {
|
||||
CounterValue ordinal = 1;
|
||||
GetFirstCounterValueForScopeAndFrame(&GetRootScope(), aFrame, ordinal);
|
||||
|
||||
CounterStyle* counterStyle =
|
||||
aFrame->PresContext()->CounterStyleManager()->ResolveCounterStyle(
|
||||
aFrame->StyleList()->mCounterStyle);
|
||||
nsAutoString text;
|
||||
bool isBullet;
|
||||
counterStyle->GetSpokenCounterText(ordinal, aFrame->GetWritingMode(), text,
|
||||
isBullet);
|
||||
if (isBullet) {
|
||||
aText = text;
|
||||
if (!counterStyle->IsNone()) {
|
||||
aText.Append(' ');
|
||||
}
|
||||
} else {
|
||||
counterStyle->GetPrefix(aText);
|
||||
aText += text;
|
||||
nsAutoString suffix;
|
||||
counterStyle->GetSuffix(suffix);
|
||||
aText += suffix;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContainStyleScopeManager::SetAllCountersDirty() {
|
||||
GetRootScope().GetCounterManager().SetAllDirty();
|
||||
for (auto& entry : mScopes) {
|
||||
entry.GetWeak()->GetCounterManager().SetAllDirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool ContainStyleScopeManager::DestroyQuoteNodesFor(nsIFrame* aFrame) {
|
||||
bool result = false;
|
||||
for (auto* scope = &GetScopeForContent(aFrame->GetContent()); scope;
|
||||
scope = scope->GetParent()) {
|
||||
result |= scope->GetQuoteList().DestroyNodesFor(aFrame);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsQuoteList* ContainStyleScopeManager::QuoteListFor(dom::Element& aElement) {
|
||||
return &GetOrCreateScopeForContent(&aElement).GetQuoteList();
|
||||
}
|
||||
|
||||
void ContainStyleScopeManager::RecalcAllQuotes() {
|
||||
GetRootScope().RecalcAllQuotes();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
136
layout/base/ContainStyleScopeManager.h
Normal file
136
layout/base/ContainStyleScopeManager.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ContainStyleScopeManager_h_
|
||||
#define ContainStyleScopeManager_h_
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsTHashMap.h"
|
||||
#include "nsTHashSet.h"
|
||||
#include "nsQuoteList.h"
|
||||
#include "nsCounterManager.h"
|
||||
#include <memory>
|
||||
|
||||
class nsIContent;
|
||||
class nsAtom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainStyleScopeManager;
|
||||
|
||||
/* Implementation of a self-contained `contain: style` scope which manages its
|
||||
* own counters and quotes. Since the `counters()` function has read access to
|
||||
* other `contain: style` scopes, USE counter nodes may link across `contain:
|
||||
* style` scopes. */
|
||||
class ContainStyleScope final {
|
||||
public:
|
||||
ContainStyleScope(ContainStyleScopeManager* aManager,
|
||||
ContainStyleScope* aParent, nsIContent* aContent)
|
||||
: mQuoteList(this),
|
||||
mCounterManager(this),
|
||||
mScopeManager(aManager),
|
||||
mParent(aParent),
|
||||
mContent(aContent) {
|
||||
MOZ_ASSERT(aManager);
|
||||
if (mParent) {
|
||||
mParent->AddChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
~ContainStyleScope() {
|
||||
if (mParent) {
|
||||
mParent->RemoveChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsQuoteList& GetQuoteList() { return mQuoteList; }
|
||||
nsCounterManager& GetCounterManager() { return mCounterManager; }
|
||||
ContainStyleScopeManager& GetScopeManager() { return *mScopeManager; }
|
||||
ContainStyleScope* GetParent() { return mParent; }
|
||||
nsIContent* GetContent() { return mContent; }
|
||||
|
||||
void AddChild(ContainStyleScope* aScope) { mChildren.AppendElement(aScope); }
|
||||
void RemoveChild(ContainStyleScope* aScope) {
|
||||
mChildren.RemoveElement(aScope);
|
||||
}
|
||||
const nsTArray<ContainStyleScope*>& GetChildren() const { return mChildren; }
|
||||
|
||||
void RecalcAllCounters();
|
||||
void RecalcAllQuotes();
|
||||
|
||||
// Find the element in the given nsGenConList that directly precedes
|
||||
// the mContent node of this ContainStyleScope in the flat tree. Can
|
||||
// return null if no element in the list precedes the content.
|
||||
nsGenConNode* GetPrecedingElementInGenConList(nsGenConList*);
|
||||
|
||||
private:
|
||||
nsQuoteList mQuoteList;
|
||||
nsCounterManager mCounterManager;
|
||||
|
||||
// We are owned by the |mScopeManager|, so this is guaranteed to be a live
|
||||
// pointer as long as we are alive as well.
|
||||
ContainStyleScopeManager* mScopeManager;
|
||||
|
||||
// Although parent and child relationships are represented as raw pointers
|
||||
// here, |mScopeManager| is responsible for managing creation and deletion of
|
||||
// all these data structures and also that it happens in the correct order.
|
||||
ContainStyleScope* mParent;
|
||||
nsTArray<ContainStyleScope*> mChildren;
|
||||
|
||||
// |mContent| is guaranteed to outlive this scope because mScopeManager will
|
||||
// delete the scope when the corresponding frame for |mContent| is destroyed.
|
||||
nsIContent* mContent;
|
||||
};
|
||||
|
||||
/* Management of the tree `contain: style` scopes. This class ensures that
|
||||
* recalculation is done top-down, so that nodes that rely on other nodes in
|
||||
* ancestor `contain: style` scopes are calculated properly. */
|
||||
class ContainStyleScopeManager {
|
||||
public:
|
||||
ContainStyleScopeManager() : mRootScope(this, nullptr, nullptr) {}
|
||||
ContainStyleScope& GetRootScope() { return mRootScope; }
|
||||
ContainStyleScope& GetOrCreateScopeForContent(nsIContent*);
|
||||
ContainStyleScope& GetScopeForContent(nsIContent*);
|
||||
|
||||
void Clear();
|
||||
|
||||
// If this frame creates a `contain: style` scope, destroy that scope and
|
||||
// all of its child scopes.
|
||||
void DestroyScopesFor(nsIFrame*);
|
||||
|
||||
// Destroy this scope and all its children starting from the leaf nodes.
|
||||
void DestroyScope(ContainStyleScope*);
|
||||
|
||||
bool DestroyCounterNodesFor(nsIFrame*);
|
||||
bool AddCounterChanges(nsIFrame* aNewFrame);
|
||||
nsCounterList* GetOrCreateCounterList(dom::Element&, nsAtom* aCounterName);
|
||||
|
||||
bool CounterDirty(nsAtom* aCounterName);
|
||||
void SetCounterDirty(nsAtom* aCounterName);
|
||||
void RecalcAllCounters();
|
||||
void SetAllCountersDirty();
|
||||
|
||||
bool DestroyQuoteNodesFor(nsIFrame*);
|
||||
nsQuoteList* QuoteListFor(dom::Element&);
|
||||
void RecalcAllQuotes();
|
||||
|
||||
#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
|
||||
void DumpCounters();
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
void GetSpokenCounterText(nsIFrame* aFrame, nsAString& aText);
|
||||
#endif
|
||||
|
||||
private:
|
||||
ContainStyleScope mRootScope;
|
||||
nsClassHashtable<nsPtrHashKey<nsIContent>, ContainStyleScope> mScopes;
|
||||
nsTHashSet<nsRefPtrHashKey<nsAtom>> mDirtyCounters;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* ContainStyleScopeManager_h_ */
|
||||
@@ -72,6 +72,7 @@ EXPORTS += [
|
||||
EXPORTS.mozilla += [
|
||||
"AccessibleCaretEventHub.h",
|
||||
"ArenaObjectID.h",
|
||||
"ContainStyleScopeManager.h",
|
||||
"DisplayPortUtils.h",
|
||||
"GeckoMVMContext.h",
|
||||
"GeometryUtils.h",
|
||||
@@ -101,6 +102,7 @@ UNIFIED_SOURCES += [
|
||||
"AccessibleCaret.cpp",
|
||||
"AccessibleCaretEventHub.cpp",
|
||||
"AccessibleCaretManager.cpp",
|
||||
"ContainStyleScopeManager.cpp",
|
||||
"DisplayPortUtils.cpp",
|
||||
"GeckoMVMContext.cpp",
|
||||
"GeometryUtils.cpp",
|
||||
|
||||
@@ -1566,18 +1566,23 @@ nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
|
||||
}
|
||||
|
||||
void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
|
||||
if (mQuoteList.DestroyNodesFor(aFrame)) QuotesDirty();
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
|
||||
mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) {
|
||||
QuotesDirty();
|
||||
}
|
||||
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
|
||||
mCounterManager.DestroyNodesFor(aFrame)) {
|
||||
mContainStyleScopeManager.DestroyCounterNodesFor(aFrame)) {
|
||||
// Technically we don't need to update anything if we destroyed only
|
||||
// USE nodes. However, this is unlikely to happen in the real world
|
||||
// since USE nodes generally go along with INCREMENT nodes.
|
||||
CountersDirty();
|
||||
}
|
||||
|
||||
if (aFrame->StyleDisplay()->IsContainStyle()) {
|
||||
mContainStyleScopeManager.DestroyScopesFor(aFrame);
|
||||
}
|
||||
|
||||
RestyleManager()->NotifyDestroyingFrame(aFrame);
|
||||
}
|
||||
|
||||
@@ -1608,7 +1613,7 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
|
||||
}
|
||||
|
||||
already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
|
||||
nsFrameConstructorState& aState, const Element& aOriginatingElement,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
ComputedStyle& aPseudoStyle, uint32_t aContentIndex) {
|
||||
using Type = StyleContentItem::Tag;
|
||||
// Get the content value
|
||||
@@ -1660,7 +1665,8 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
|
||||
ptr = CounterStylePtr::FromStyle(counters._2);
|
||||
}
|
||||
|
||||
nsCounterList* counterList = mCounterManager.CounterListFor(name);
|
||||
auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
|
||||
aOriginatingElement, name);
|
||||
auto node = MakeUnique<nsCounterUseNode>(
|
||||
std::move(ptr), std::move(separator), aContentIndex,
|
||||
/* aAllCounters = */ type == Type::Counters);
|
||||
@@ -1674,8 +1680,10 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
|
||||
case Type::NoOpenQuote:
|
||||
case Type::NoCloseQuote: {
|
||||
auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
|
||||
auto* quoteList =
|
||||
mContainStyleScopeManager.QuoteListFor(aOriginatingElement);
|
||||
auto initializer = MakeUnique<nsGenConInitializer>(
|
||||
std::move(node), &mQuoteList, &nsCSSFrameConstructor::QuotesDirty);
|
||||
std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty);
|
||||
return CreateGenConTextNode(aState, u""_ns, std::move(initializer));
|
||||
}
|
||||
|
||||
@@ -1715,7 +1723,8 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
|
||||
}
|
||||
|
||||
void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle(
|
||||
nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
const ComputedStyle& aPseudoStyle,
|
||||
const FunctionRef<void(nsIContent*)> aAddChild) {
|
||||
const nsStyleList* styleList = aPseudoStyle.StyleList();
|
||||
if (!styleList->mListStyleImage.IsNone()) {
|
||||
@@ -1726,11 +1735,13 @@ void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle(
|
||||
aAddChild(child);
|
||||
return;
|
||||
}
|
||||
CreateGeneratedContentFromListStyleType(aState, aPseudoStyle, aAddChild);
|
||||
CreateGeneratedContentFromListStyleType(aState, aOriginatingElement,
|
||||
aPseudoStyle, aAddChild);
|
||||
}
|
||||
|
||||
void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType(
|
||||
nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
const ComputedStyle& aPseudoStyle,
|
||||
const FunctionRef<void(nsIContent*)> aAddChild) {
|
||||
const nsStyleList* styleList = aPseudoStyle.StyleList();
|
||||
CounterStyle* counterStyle =
|
||||
@@ -1765,8 +1776,8 @@ void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType(
|
||||
return;
|
||||
}
|
||||
|
||||
nsCounterList* counterList =
|
||||
mCounterManager.CounterListFor(nsGkAtoms::list_item);
|
||||
auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
|
||||
aOriginatingElement, nsGkAtoms::list_item);
|
||||
auto initializer = MakeUnique<nsGenConInitializer>(
|
||||
std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
|
||||
RefPtr<nsIContent> child =
|
||||
@@ -1906,7 +1917,8 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem(
|
||||
}
|
||||
// If a ::marker has no 'content' then generate it from its 'list-style-*'.
|
||||
if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) {
|
||||
CreateGeneratedContentFromListStyle(aState, *pseudoStyle, AppendChild);
|
||||
CreateGeneratedContentFromListStyle(aState, aOriginatingElement,
|
||||
*pseudoStyle, AppendChild);
|
||||
}
|
||||
auto flags = ItemFlags{ItemFlag::IsGeneratedContent} + aExtraFlags;
|
||||
AddFrameConstructionItemsInternal(aState, container, aParentFrame, true,
|
||||
@@ -4680,7 +4692,8 @@ void nsCSSFrameConstructor::InitAndRestoreFrame(
|
||||
RestoreFrameStateFor(aNewFrame, aState.mFrameState);
|
||||
}
|
||||
|
||||
if (aAllowCounters && mCounterManager.AddCounterChanges(aNewFrame)) {
|
||||
if (aAllowCounters &&
|
||||
mContainStyleScopeManager.AddCounterChanges(aNewFrame)) {
|
||||
CountersDirty();
|
||||
}
|
||||
}
|
||||
@@ -7271,7 +7284,8 @@ void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
|
||||
}
|
||||
};
|
||||
CreateGeneratedContentFromListStyleType(
|
||||
state, *insertion.mParentFrame->Style(), InsertChild);
|
||||
state, *insertion.mContainer->AsElement(),
|
||||
*insertion.mParentFrame->Style(), InsertChild);
|
||||
if (!firstNewChild) {
|
||||
// No fallback content - we're done.
|
||||
return;
|
||||
@@ -7932,12 +7946,12 @@ void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
|
||||
|
||||
if (mQuotesDirty) {
|
||||
mQuotesDirty = false;
|
||||
mQuoteList.RecalcAll();
|
||||
mContainStyleScopeManager.RecalcAllQuotes();
|
||||
}
|
||||
|
||||
if (mCountersDirty) {
|
||||
mCountersDirty = false;
|
||||
mCounterManager.RecalcAll();
|
||||
mContainStyleScopeManager.RecalcAllCounters();
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
|
||||
@@ -7945,20 +7959,19 @@ void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
|
||||
}
|
||||
|
||||
void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
|
||||
mCounterManager.SetAllDirty();
|
||||
mContainStyleScopeManager.SetAllCountersDirty();
|
||||
CountersDirty();
|
||||
}
|
||||
|
||||
void nsCSSFrameConstructor::WillDestroyFrameTree() {
|
||||
#if defined(DEBUG_dbaron_off)
|
||||
mCounterManager.Dump();
|
||||
mContainStyleScopeManager.DumpCounters();
|
||||
#endif
|
||||
|
||||
mIsDestroyingFrameTree = true;
|
||||
|
||||
// Prevent frame tree destruction from being O(N^2)
|
||||
mQuoteList.Clear();
|
||||
mCounterManager.Clear();
|
||||
mContainStyleScopeManager.Clear();
|
||||
nsFrameManager::Destroy();
|
||||
}
|
||||
|
||||
@@ -12069,6 +12082,5 @@ void nsCSSFrameConstructor::AddSizeOfIncludingThis(
|
||||
// Measurement of the following members may be added later if DMD finds it
|
||||
// is worthwhile:
|
||||
// - mFCItemPool
|
||||
// - mQuoteList
|
||||
// - mCounterManager
|
||||
// - mContainStyleScopeManager
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "mozilla/ArenaAllocator.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ContainStyleScopeManager.h"
|
||||
#include "mozilla/FunctionRef.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
@@ -23,8 +24,6 @@
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsILayoutHistoryState.h"
|
||||
#include "nsQuoteList.h"
|
||||
#include "nsCounterManager.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsIFrame.h"
|
||||
@@ -336,7 +335,9 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
#if defined(ACCESSIBILITY) || defined(MOZ_LAYOUT_DEBUGGER)
|
||||
// Exposed only for nsLayoutUtils::GetMarkerSpokenText and
|
||||
// nsLayoutDebuggingTools to use.
|
||||
const nsCounterManager* CounterManager() const { return &mCounterManager; }
|
||||
mozilla::ContainStyleScopeManager& GetContainStyleScopeManager() {
|
||||
return mContainStyleScopeManager;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -453,20 +454,22 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
* @param aContentIndex is the index of the content item to create
|
||||
*/
|
||||
already_AddRefed<nsIContent> CreateGeneratedContent(
|
||||
nsFrameConstructorState& aState, const Element& aOriginatingElement,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
ComputedStyle& aComputedStyle, uint32_t aContentIndex);
|
||||
|
||||
/**
|
||||
* Create child content nodes for a ::marker from its 'list-style-*' values.
|
||||
*/
|
||||
void CreateGeneratedContentFromListStyle(
|
||||
nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
const ComputedStyle& aPseudoStyle,
|
||||
const mozilla::FunctionRef<void(nsIContent*)> aAddChild);
|
||||
/**
|
||||
* Create child content nodes for a ::marker from its 'list-style-type'.
|
||||
*/
|
||||
void CreateGeneratedContentFromListStyleType(
|
||||
nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle,
|
||||
nsFrameConstructorState& aState, Element& aOriginatingElement,
|
||||
const ComputedStyle& aPseudoStyle,
|
||||
const mozilla::FunctionRef<void(nsIContent*)> aAddChild);
|
||||
|
||||
// aParentFrame may be null; this method doesn't use it directly in any case.
|
||||
@@ -2160,8 +2163,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
FreeFCItemLink* mFirstFreeFCItem;
|
||||
size_t mFCItemsInUse;
|
||||
|
||||
nsQuoteList mQuoteList;
|
||||
nsCounterManager mCounterManager;
|
||||
mozilla::ContainStyleScopeManager mContainStyleScopeManager;
|
||||
|
||||
// Current ProcessChildren depth.
|
||||
uint16_t mCurrentDepth;
|
||||
bool mQuotesDirty : 1;
|
||||
|
||||
@@ -8,19 +8,21 @@
|
||||
|
||||
#include "nsCounterManager.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/ContainStyleScopeManager.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StaticPrefs_layout.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
@@ -49,8 +51,11 @@ bool nsCounterUseNode::InitTextFrame(nsGenConList* aList,
|
||||
// assign the correct |mValueAfter| value to a node that has been inserted
|
||||
// Should be called immediately after calling |Insert|.
|
||||
void nsCounterUseNode::Calc(nsCounterList* aList, bool aNotify) {
|
||||
NS_ASSERTION(!aList->IsDirty(), "Why are we calculating with a dirty list?");
|
||||
NS_ASSERTION(aList->IsRecalculatingAll() || !aList->IsDirty(),
|
||||
"Why are we calculating with a dirty list?");
|
||||
|
||||
mValueAfter = nsCounterList::ValueBefore(this);
|
||||
|
||||
if (mText) {
|
||||
nsAutoString contentString;
|
||||
GetText(contentString);
|
||||
@@ -61,7 +66,8 @@ void nsCounterUseNode::Calc(nsCounterList* aList, bool aNotify) {
|
||||
// assign the correct |mValueAfter| value to a node that has been inserted
|
||||
// Should be called immediately after calling |Insert|.
|
||||
void nsCounterChangeNode::Calc(nsCounterList* aList) {
|
||||
NS_ASSERTION(!aList->IsDirty(), "Why are we calculating with a dirty list?");
|
||||
NS_ASSERTION(aList->IsRecalculatingAll() || !aList->IsDirty(),
|
||||
"Why are we calculating with a dirty list?");
|
||||
if (IsContentBasedReset()) {
|
||||
// RecalcAll takes care of this case.
|
||||
} else if (mType == RESET || mType == SET) {
|
||||
@@ -147,6 +153,14 @@ static const nsIContent* GetParentContentForScope(nsIFrame* frame) {
|
||||
return content;
|
||||
}
|
||||
|
||||
bool nsCounterList::IsDirty() const {
|
||||
return mScope->GetScopeManager().CounterDirty(mCounterName);
|
||||
}
|
||||
|
||||
void nsCounterList::SetDirty() {
|
||||
mScope->GetScopeManager().SetCounterDirty(mCounterName);
|
||||
}
|
||||
|
||||
void nsCounterList::SetScope(nsCounterNode* aNode) {
|
||||
// This function is responsible for setting |mScopeStart| and
|
||||
// |mScopePrev| (whose purpose is described in nsCounterManager.h).
|
||||
@@ -161,12 +175,13 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
|
||||
auto setNullScopeFor = [](nsCounterNode* aNode) {
|
||||
aNode->mScopeStart = nullptr;
|
||||
aNode->mScopePrev = nullptr;
|
||||
aNode->mCrossesContainStyleBoundaries = false;
|
||||
if (aNode->IsUnitializedIncrementNode()) {
|
||||
aNode->ChangeNode()->mChangeValue = 1;
|
||||
}
|
||||
};
|
||||
|
||||
if (aNode == First()) {
|
||||
if (aNode == First() && aNode->mType != nsCounterNode::USE) {
|
||||
setNullScopeFor(aNode);
|
||||
return;
|
||||
}
|
||||
@@ -176,7 +191,7 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
|
||||
return;
|
||||
}
|
||||
if (aNode->mScopeStart->IsContentBasedReset()) {
|
||||
mDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
if (aNode->IsUnitializedIncrementNode()) {
|
||||
aNode->ChangeNode()->mChangeValue =
|
||||
@@ -202,6 +217,7 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
|
||||
}
|
||||
aNode->mScopeStart = counter;
|
||||
aNode->mScopePrev = counter;
|
||||
aNode->mCrossesContainStyleBoundaries = false;
|
||||
for (nsCounterNode* prev = Prev(aNode); prev; prev = prev->mScopePrev) {
|
||||
if (prev->mScopeStart == counter) {
|
||||
aNode->mScopePrev =
|
||||
@@ -229,44 +245,93 @@ void nsCounterList::SetScope(nsCounterNode* aNode) {
|
||||
// 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
|
||||
// reset), we want it as the scope start rather than the start
|
||||
// of its enclosing scope. Otherwise, there's no enclosing
|
||||
// scope, so the next thing in prev's scope shares its scope
|
||||
// start.
|
||||
start = (prev->mType == nsCounterNode::RESET || !prev->mScopeStart)
|
||||
? prev
|
||||
: prev->mScopeStart;
|
||||
if (SetScopeByWalkingBackwardThroughList(aNode, nodeContent, Prev(aNode))) {
|
||||
aNode->mCrossesContainStyleBoundaries = false;
|
||||
didSetScopeFor(aNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// |startContent| is analogous to |nodeContent| (see above).
|
||||
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 "
|
||||
"is for the root, startContent (which is before it) "
|
||||
"must be too");
|
||||
|
||||
// A reset's outer scope can't be a scope created by a sibling.
|
||||
if (!(aNode->mType == nsCounterNode::RESET &&
|
||||
nodeContent == startContent) &&
|
||||
// everything is inside the root (except the case above,
|
||||
// a second reset on the root)
|
||||
(!startContent ||
|
||||
nodeContent->IsInclusiveFlatTreeDescendantOf(startContent))) {
|
||||
aNode->mScopeStart = start;
|
||||
aNode->mScopePrev = prev;
|
||||
didSetScopeFor(aNode);
|
||||
return;
|
||||
// If this is a USE node there's a possibility that its counter scope starts
|
||||
// in a parent `contain: style` scope. Look upward in the `contain: style`
|
||||
// scope tree to find an appropriate node with which this node shares a
|
||||
// counter scope.
|
||||
if (aNode->mType == nsCounterNode::USE && aNode == First()) {
|
||||
for (auto* scope = mScope->GetParent(); scope; scope = scope->GetParent()) {
|
||||
if (auto* counterList =
|
||||
scope->GetCounterManager().GetCounterList(mCounterName)) {
|
||||
if (auto* node = static_cast<nsCounterNode*>(
|
||||
mScope->GetPrecedingElementInGenConList(counterList))) {
|
||||
if (SetScopeByWalkingBackwardThroughList(aNode, nodeContent, node)) {
|
||||
aNode->mCrossesContainStyleBoundaries = true;
|
||||
didSetScopeFor(aNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNullScopeFor(aNode);
|
||||
}
|
||||
|
||||
bool nsCounterList::SetScopeByWalkingBackwardThroughList(
|
||||
nsCounterNode* aNodeToSetScopeFor, const nsIContent* aNodeContent,
|
||||
nsCounterNode* aNodeToBeginLookingAt) {
|
||||
for (nsCounterNode *prev = aNodeToBeginLookingAt, *start; prev;
|
||||
prev = start->mScopePrev) {
|
||||
// There are two possibilities here:
|
||||
// 1. |prev| starts a new counter scope. This happens when:
|
||||
// a. It's a reset node.
|
||||
// b. It's an implied reset node which we know because mScopeStart is null.
|
||||
// c. It follows one or more USE nodes at the start of the list which have
|
||||
// a scope that starts in a parent `contain: style` context.
|
||||
// In all of these cases, |prev| should be the start of this node's counter
|
||||
// scope.
|
||||
// 2. |prev| does not start a new counter scope and this node should share a
|
||||
// counter scope start with |prev|.
|
||||
start =
|
||||
(prev->mType == nsCounterNode::RESET || !prev->mScopeStart ||
|
||||
(prev->mScopePrev && prev->mScopePrev->mCrossesContainStyleBoundaries))
|
||||
? prev
|
||||
: prev->mScopeStart;
|
||||
|
||||
const nsIContent* startContent =
|
||||
GetParentContentForScope(start->mPseudoFrame);
|
||||
NS_ASSERTION(aNodeContent || !startContent,
|
||||
"null check on startContent should be sufficient to "
|
||||
"null check aNodeContent as well, since if aNodeContent "
|
||||
"is for the root, startContent (which is before it) "
|
||||
"must be too");
|
||||
|
||||
// A reset's outer scope can't be a scope created by a sibling.
|
||||
if (!(aNodeToSetScopeFor->mType == nsCounterNode::RESET &&
|
||||
aNodeContent == startContent) &&
|
||||
// everything is inside the root (except the case above,
|
||||
// a second reset on the root)
|
||||
(!startContent ||
|
||||
aNodeContent->IsInclusiveFlatTreeDescendantOf(startContent))) {
|
||||
// If this node is a USE node and the previous node was also a USE node
|
||||
// which has a scope that starts in a parent `contain: style` context,
|
||||
// this node's scope shares the same scope and crosses `contain: style`
|
||||
// scope boundaries.
|
||||
if (aNodeToSetScopeFor->mType == nsCounterNode::USE) {
|
||||
aNodeToSetScopeFor->mCrossesContainStyleBoundaries =
|
||||
prev->mCrossesContainStyleBoundaries;
|
||||
}
|
||||
|
||||
aNodeToSetScopeFor->mScopeStart = start;
|
||||
aNodeToSetScopeFor->mScopePrev = prev;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void nsCounterList::RecalcAll() {
|
||||
AutoRestore<bool> restoreRecalculatingAll(mRecalculatingAll);
|
||||
mRecalculatingAll = true;
|
||||
|
||||
// Setup the scope and calculate the default start value for content-based
|
||||
// reversed() counters. We need to track the last increment for each of
|
||||
// those scopes so that we can add it in an extra time at the end.
|
||||
@@ -306,7 +371,6 @@ void nsCounterList::RecalcAll() {
|
||||
iter.Key()->mValueAfter += iter.Data();
|
||||
}
|
||||
|
||||
mDirty = false;
|
||||
for (nsCounterNode* node = First(); node; node = Next(node)) {
|
||||
node->Calc(this, /* aNotify = */ true);
|
||||
}
|
||||
@@ -318,7 +382,8 @@ static bool AddCounterChangeNode(nsCounterManager& aManager, nsIFrame* aFrame,
|
||||
nsCounterNode::Type aType) {
|
||||
auto* node = new nsCounterChangeNode(aFrame, aType, aPair.value, aIndex,
|
||||
aPair.is_reversed);
|
||||
nsCounterList* counterList = aManager.CounterListFor(aPair.name.AsAtom());
|
||||
nsCounterList* counterList =
|
||||
aManager.GetOrCreateCounterList(aPair.name.AsAtom());
|
||||
counterList->Insert(node);
|
||||
if (!counterList->IsLast(node)) {
|
||||
// Tell the caller it's responsible for recalculating the entire list.
|
||||
@@ -404,9 +469,14 @@ bool nsCounterManager::AddCounterChanges(nsIFrame* aFrame) {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
nsCounterList* nsCounterManager::CounterListFor(nsAtom* aCounterName) {
|
||||
nsCounterList* nsCounterManager::GetOrCreateCounterList(nsAtom* aCounterName) {
|
||||
MOZ_ASSERT(aCounterName);
|
||||
return mNames.GetOrInsertNew(aCounterName);
|
||||
return mNames.GetOrInsertNew(aCounterName, aCounterName, mScope);
|
||||
}
|
||||
|
||||
nsCounterList* nsCounterManager::GetCounterList(nsAtom* aCounterName) {
|
||||
MOZ_ASSERT(aCounterName);
|
||||
return mNames.Get(aCounterName);
|
||||
}
|
||||
|
||||
void nsCounterManager::RecalcAll() {
|
||||
@@ -437,37 +507,19 @@ bool nsCounterManager::DestroyNodesFor(nsIFrame* aFrame) {
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
void nsCounterManager::GetSpokenCounterText(nsIFrame* aFrame,
|
||||
nsAString& aText) const {
|
||||
CounterValue ordinal = 1;
|
||||
bool nsCounterManager::GetFirstCounterValueForFrame(
|
||||
nsIFrame* aFrame, CounterValue& aOrdinal) const {
|
||||
if (const auto* list = mNames.Get(nsGkAtoms::list_item)) {
|
||||
for (nsCounterNode* n = list->GetFirstNodeFor(aFrame);
|
||||
n && n->mPseudoFrame == aFrame; n = list->Next(n)) {
|
||||
if (n->mType == nsCounterNode::USE) {
|
||||
ordinal = n->mValueAfter;
|
||||
break;
|
||||
aOrdinal = n->mValueAfter;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
CounterStyle* counterStyle =
|
||||
aFrame->PresContext()->CounterStyleManager()->ResolveCounterStyle(
|
||||
aFrame->StyleList()->mCounterStyle);
|
||||
nsAutoString text;
|
||||
bool isBullet;
|
||||
counterStyle->GetSpokenCounterText(ordinal, aFrame->GetWritingMode(), text,
|
||||
isBullet);
|
||||
if (isBullet) {
|
||||
aText = text;
|
||||
if (!counterStyle->IsNone()) {
|
||||
aText.Append(' ');
|
||||
}
|
||||
} else {
|
||||
counterStyle->GetPrefix(aText);
|
||||
aText += text;
|
||||
nsAutoString suffix;
|
||||
counterStyle->GetSuffix(suffix);
|
||||
aText += suffix;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ class nsCounterList;
|
||||
struct nsCounterUseNode;
|
||||
struct nsCounterChangeNode;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainStyleScope;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
struct nsCounterNode : public nsGenConNode {
|
||||
enum Type {
|
||||
RESET, // a "counter number" pair in 'counter-reset'
|
||||
@@ -54,6 +60,11 @@ struct nsCounterNode : public nsGenConNode {
|
||||
// outer scope.
|
||||
nsCounterNode* mScopePrev = nullptr;
|
||||
|
||||
// Whether or not this node's scope crosses `contain: style` boundaries.
|
||||
// This can happen for USE nodes that come before any other types of
|
||||
// nodes in a `contain: style` boundary's list.
|
||||
bool mCrossesContainStyleBoundaries = false;
|
||||
|
||||
inline nsCounterUseNode* UseNode();
|
||||
inline nsCounterChangeNode* ChangeNode();
|
||||
|
||||
@@ -200,7 +211,10 @@ inline bool nsCounterNode::IsUnitializedIncrementNode() {
|
||||
|
||||
class nsCounterList : public nsGenConList {
|
||||
public:
|
||||
nsCounterList() : nsGenConList(), mDirty(false) {}
|
||||
nsCounterList(nsAtom* aCounterName, mozilla::ContainStyleScope* aScope)
|
||||
: nsGenConList(), mCounterName(aCounterName), mScope(aScope) {
|
||||
MOZ_ASSERT(aScope);
|
||||
}
|
||||
|
||||
// Return the first node for aFrame on this list, or nullptr.
|
||||
nsCounterNode* GetFirstNodeFor(nsIFrame* aFrame) const {
|
||||
@@ -228,7 +242,16 @@ class nsCounterList : public nsGenConList {
|
||||
}
|
||||
|
||||
static int32_t ValueBefore(nsCounterNode* aNode) {
|
||||
return aNode->mScopePrev ? aNode->mScopePrev->mValueAfter : 0;
|
||||
if (!aNode->mScopePrev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aNode->mType != nsCounterNode::USE &&
|
||||
aNode->mScopePrev->mCrossesContainStyleBoundaries) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return aNode->mScopePrev->mValueAfter;
|
||||
}
|
||||
|
||||
// Correctly set |aNode->mScopeStart| and |aNode->mScopePrev|
|
||||
@@ -238,11 +261,18 @@ class nsCounterList : public nsGenConList {
|
||||
// all nodes and update text in text content nodes.
|
||||
void RecalcAll();
|
||||
|
||||
bool IsDirty() { return mDirty; }
|
||||
void SetDirty() { mDirty = true; }
|
||||
bool IsDirty() const;
|
||||
void SetDirty();
|
||||
bool IsRecalculatingAll() const { return mRecalculatingAll; }
|
||||
|
||||
private:
|
||||
bool mDirty;
|
||||
bool SetScopeByWalkingBackwardThroughList(
|
||||
nsCounterNode* aNodeToSetScopeFor, const nsIContent* aNodeContent,
|
||||
nsCounterNode* aNodeToBeginLookingAt);
|
||||
|
||||
RefPtr<nsAtom> mCounterName;
|
||||
mozilla::ContainStyleScope* mScope;
|
||||
bool mRecalculatingAll = false;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -251,12 +281,18 @@ class nsCounterList : public nsGenConList {
|
||||
*/
|
||||
class nsCounterManager {
|
||||
public:
|
||||
explicit nsCounterManager(mozilla::ContainStyleScope* scope)
|
||||
: mScope(scope) {}
|
||||
|
||||
// Returns true if dirty
|
||||
bool AddCounterChanges(nsIFrame* aFrame);
|
||||
|
||||
// Gets the appropriate counter list, creating it if necessary.
|
||||
// Guaranteed to return non-null. (Uses an infallible hashtable API.)
|
||||
nsCounterList* CounterListFor(nsAtom* aCounterName);
|
||||
nsCounterList* GetOrCreateCounterList(nsAtom* aCounterName);
|
||||
|
||||
// Gets the appropriate counter list, returning null if it doesn't exist.
|
||||
nsCounterList* GetCounterList(nsAtom* aCounterName);
|
||||
|
||||
// Clean up data in any dirty counter lists.
|
||||
void RecalcAll();
|
||||
@@ -272,8 +308,11 @@ class nsCounterManager {
|
||||
void Clear() { mNames.Clear(); }
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
// Returns the spoken text for the 'list-item' counter for aFrame in aText.
|
||||
void GetSpokenCounterText(nsIFrame* aFrame, nsAString& aText) const;
|
||||
// Set |aOrdinal| to the first used counter value for the given frame and
|
||||
// return true. If no USE node for the given frame can be found, return false
|
||||
// and do not change the value of |aOrdinal|.
|
||||
bool GetFirstCounterValueForFrame(nsIFrame* aFrame,
|
||||
mozilla::CounterValue& aOrdinal) const;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
|
||||
@@ -304,6 +343,7 @@ class nsCounterManager {
|
||||
}
|
||||
|
||||
private:
|
||||
mozilla::ContainStyleScope* mScope;
|
||||
nsClassHashtable<nsRefPtrHashKey<nsAtom>, nsCounterList> mNames;
|
||||
};
|
||||
|
||||
|
||||
@@ -125,6 +125,37 @@ bool nsGenConList::NodeAfter(const nsGenConNode* aNode1,
|
||||
return cmp > 0;
|
||||
}
|
||||
|
||||
nsGenConNode* nsGenConList::BinarySearch(
|
||||
const mozilla::FunctionRef<bool(nsGenConNode*)>& aIsAfter) {
|
||||
// The range of indices at which |aNode| could end up.
|
||||
// (We already know it can't be at index mSize.)
|
||||
uint32_t first = 0, last = mSize - 1;
|
||||
|
||||
// A cursor to avoid walking more than the length of the list.
|
||||
nsGenConNode* curNode = mList.getLast();
|
||||
uint32_t curIndex = mSize - 1;
|
||||
|
||||
while (first != last) {
|
||||
uint32_t test = first + (last - first) / 2;
|
||||
if (last == curIndex) {
|
||||
for (; curIndex != test; --curIndex) curNode = Prev(curNode);
|
||||
} else {
|
||||
for (; curIndex != test; ++curIndex) curNode = Next(curNode);
|
||||
}
|
||||
|
||||
if (aIsAfter(curNode)) {
|
||||
first = test + 1;
|
||||
// if we exit the loop, we need curNode to be right
|
||||
++curIndex;
|
||||
curNode = Next(curNode);
|
||||
} else {
|
||||
last = test;
|
||||
}
|
||||
}
|
||||
|
||||
return curNode;
|
||||
}
|
||||
|
||||
void nsGenConList::Insert(nsGenConNode* aNode) {
|
||||
// Check for append.
|
||||
if (mList.isEmpty() || NodeAfter(aNode, mList.getLast())) {
|
||||
@@ -135,34 +166,11 @@ void nsGenConList::Insert(nsGenConNode* aNode) {
|
||||
// Fast path for inserting many consecutive nodes in one place
|
||||
mLastInserted->setNext(aNode);
|
||||
} else {
|
||||
// Binary search.
|
||||
|
||||
// the range of indices at which |aNode| could end up.
|
||||
// (We already know it can't be at index mSize.)
|
||||
uint32_t first = 0, last = mSize - 1;
|
||||
|
||||
// A cursor to avoid walking more than the length of the list.
|
||||
nsGenConNode* curNode = mList.getLast();
|
||||
uint32_t curIndex = mSize - 1;
|
||||
|
||||
while (first != last) {
|
||||
uint32_t test = (first + last) / 2;
|
||||
if (last == curIndex) {
|
||||
for (; curIndex != test; --curIndex) curNode = Prev(curNode);
|
||||
} else {
|
||||
for (; curIndex != test; ++curIndex) curNode = Next(curNode);
|
||||
}
|
||||
|
||||
if (NodeAfter(aNode, curNode)) {
|
||||
first = test + 1;
|
||||
// if we exit the loop, we need curNode to be right
|
||||
++curIndex;
|
||||
curNode = Next(curNode);
|
||||
} else {
|
||||
last = test;
|
||||
}
|
||||
}
|
||||
curNode->setPrevious(aNode);
|
||||
auto IsAfter = [aNode](nsGenConNode* curNode) {
|
||||
return NodeAfter(aNode, curNode);
|
||||
};
|
||||
auto* insertionNode = BinarySearch(IsAfter);
|
||||
insertionNode->setPrevious(aNode);
|
||||
}
|
||||
++mSize;
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#ifndef nsGenConList_h___
|
||||
#define nsGenConList_h___
|
||||
|
||||
#include "mozilla/FunctionRef.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsTextNode.h"
|
||||
#include <functional>
|
||||
|
||||
class nsGenConList;
|
||||
class nsIFrame;
|
||||
@@ -95,6 +97,13 @@ class nsGenConList {
|
||||
// Return true if |aNode1| is after |aNode2|.
|
||||
static bool NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2);
|
||||
|
||||
// Find the first element in the list for which the given comparator returns
|
||||
// true. This does a binary search on the list contents.
|
||||
nsGenConNode* BinarySearch(
|
||||
const mozilla::FunctionRef<bool(nsGenConNode*)>& aIsAfter);
|
||||
|
||||
nsGenConNode* GetLast() { return mList.getLast(); }
|
||||
|
||||
bool IsFirst(nsGenConNode* aNode) {
|
||||
MOZ_ASSERT(aNode, "aNode cannot be nullptr!");
|
||||
return aNode == mList.getFirst();
|
||||
|
||||
@@ -938,8 +938,8 @@ void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent,
|
||||
|
||||
frame->PresContext()
|
||||
->FrameConstructor()
|
||||
->CounterManager()
|
||||
->GetSpokenCounterText(frame, aText);
|
||||
->GetContainStyleScopeManager()
|
||||
.GetSpokenCounterText(frame, aText);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "mozilla/ContainStyleScopeManager.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/intl/Quotes.h"
|
||||
@@ -99,9 +100,21 @@ nsString nsQuoteNode::Text() {
|
||||
return result;
|
||||
}
|
||||
|
||||
static int32_t GetDepthBeforeFirstQuoteNode(ContainStyleScope* aScope) {
|
||||
for (auto* ancestor = aScope->GetParent(); ancestor;
|
||||
ancestor = ancestor->GetParent()) {
|
||||
auto& quoteList = ancestor->GetQuoteList();
|
||||
if (auto* node = static_cast<nsQuoteNode*>(
|
||||
aScope->GetPrecedingElementInGenConList("eList))) {
|
||||
return node->DepthAfter();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nsQuoteList::Calc(nsQuoteNode* aNode) {
|
||||
if (aNode == FirstNode()) {
|
||||
aNode->mDepthBefore = 0;
|
||||
aNode->mDepthBefore = GetDepthBeforeFirstQuoteNode(mScope);
|
||||
} else {
|
||||
aNode->mDepthBefore = Prev(aNode)->DepthAfter();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsGenConList.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainStyleScope;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
struct nsQuoteNode : public nsGenConNode {
|
||||
// open-quote, close-quote, no-open-quote, or no-close-quote
|
||||
const StyleContentType mType;
|
||||
@@ -69,6 +75,8 @@ class nsQuoteList : public nsGenConList {
|
||||
}
|
||||
|
||||
public:
|
||||
explicit nsQuoteList(mozilla::ContainStyleScope* aScope) : mScope(aScope) {}
|
||||
|
||||
// assign the correct |mDepthBefore| value to a node that has been inserted
|
||||
// Should be called immediately after calling |Insert|.
|
||||
void Calc(nsQuoteNode* aNode);
|
||||
@@ -84,6 +92,9 @@ class nsQuoteList : public nsGenConList {
|
||||
#ifdef DEBUG
|
||||
void PrintChain();
|
||||
#endif
|
||||
|
||||
private:
|
||||
mozilla::ContainStyleScope* mScope;
|
||||
};
|
||||
|
||||
#endif /* nsQuoteList_h___ */
|
||||
|
||||
@@ -165,6 +165,10 @@ class ComputedStyle {
|
||||
return bool(Flags() & Flag::IS_IN_PSEUDO_ELEMENT_SUBTREE);
|
||||
}
|
||||
|
||||
bool SelfOrAncestorHasContainStyle() const {
|
||||
return bool(Flags() & Flag::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
|
||||
}
|
||||
|
||||
// Is the only link whose visitedness is allowed to influence the
|
||||
// style of the node this ComputedStyle is for (which is that element
|
||||
// or its nearest ancestor that is a link) visited?
|
||||
|
||||
@@ -1521,6 +1521,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
|
||||
!IsInternalTableStyleExceptCell();
|
||||
}
|
||||
|
||||
bool IsContainStyle() const {
|
||||
return !!(EffectiveContainment() && StyleContain::STYLE);
|
||||
}
|
||||
|
||||
bool IsContainAny() const { return !!EffectiveContainment(); }
|
||||
|
||||
mozilla::ContainSizeAxes GetContainSizeAxes() const {
|
||||
@@ -1671,18 +1675,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
|
||||
StyleContain EffectiveContainment() const {
|
||||
// content-visibility and container-type values implicitly enable some
|
||||
// containment flags.
|
||||
// FIXME(dshin, bug 1463600): Add in STYLE containment flag for `auto` &
|
||||
// `hidden` when implemented
|
||||
// FIXME(dshin, bug 1764640): Add in the effect of `container-type`
|
||||
switch (mContentVisibility) {
|
||||
case StyleContentVisibility::Visible:
|
||||
// Most likely case.
|
||||
return mContain;
|
||||
case StyleContentVisibility::Auto:
|
||||
return mContain | StyleContain::LAYOUT | StyleContain::PAINT;
|
||||
return mContain | StyleContain::LAYOUT | StyleContain::PAINT |
|
||||
StyleContain::STYLE;
|
||||
case StyleContentVisibility::Hidden:
|
||||
return mContain | StyleContain::LAYOUT | StyleContain::PAINT |
|
||||
StyleContain::SIZE;
|
||||
StyleContain::SIZE | StyleContain::STYLE;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid content visibility.");
|
||||
return mContain;
|
||||
|
||||
@@ -255,7 +255,7 @@ NS_IMETHODIMP
|
||||
nsLayoutDebuggingTools::DumpCounterManager() {
|
||||
NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
|
||||
if (PresShell* presShell = GetPresShell(mDocShell)) {
|
||||
presShell->FrameConstructor()->CounterManager()->Dump();
|
||||
presShell->FrameConstructor()->GetContainStyleScopeManager().DumpCounters();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ bitflags! {
|
||||
/// A flag used to mark styles which are a pseudo-element or under one.
|
||||
const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;
|
||||
|
||||
/// A flag used to mark styles which have contain:style or under one.
|
||||
const SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE = 1 << 5;
|
||||
|
||||
/// Whether this style's `display` property depends on our parent style.
|
||||
///
|
||||
/// This is important because it may affect our optimizations to avoid
|
||||
@@ -109,7 +112,9 @@ impl ComputedValueFlags {
|
||||
Self::CAN_BE_FRAGMENTED |
|
||||
Self::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
||||
Self::HAS_TEXT_DECORATION_LINES |
|
||||
Self::IS_IN_OPACITY_ZERO_SUBTREE
|
||||
Self::IS_IN_OPACITY_ZERO_SUBTREE |
|
||||
Self::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE
|
||||
|
||||
}
|
||||
|
||||
/// Flags that may be propagated to descendants.
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
use crate::dom::TElement;
|
||||
use crate::properties::longhands::contain::SpecifiedValue;
|
||||
use crate::properties::longhands::display::computed_value::T as Display;
|
||||
use crate::properties::longhands::float::computed_value::T as Float;
|
||||
use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
|
||||
use crate::properties::longhands::position::computed_value::T as Position;
|
||||
use crate::properties::{self, ComputedValues, StyleBuilder};
|
||||
|
||||
|
||||
/// A struct that implements all the adjustment methods.
|
||||
///
|
||||
/// NOTE(emilio): If new adjustments are introduced that depend on reset
|
||||
@@ -246,6 +248,15 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||
.add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
|
||||
}
|
||||
|
||||
if self.style
|
||||
.get_box()
|
||||
.clone_contain()
|
||||
.contains(SpecifiedValue::STYLE)
|
||||
{
|
||||
self.style
|
||||
.add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo-layout-2013")]
|
||||
{
|
||||
if self.style.get_parent_column().is_multicol() {
|
||||
|
||||
@@ -1428,7 +1428,7 @@ impl TouchAction {
|
||||
|
||||
bitflags! {
|
||||
#[derive(MallocSizeOf, Parse, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
|
||||
#[css(bitflags(single = "none,strict,content", mixed="size,layout,paint,inline-size", overlapping_bits))]
|
||||
#[css(bitflags(single = "none,strict,content", mixed="size,layout,style,paint,inline-size", overlapping_bits))]
|
||||
#[repr(C)]
|
||||
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
|
||||
pub struct Contain: u8 {
|
||||
@@ -1440,14 +1440,16 @@ bitflags! {
|
||||
const BLOCK_SIZE = 1 << 1;
|
||||
/// `layout` variant, turns on layout containment
|
||||
const LAYOUT = 1 << 2;
|
||||
/// `style` variant, turns on style containment
|
||||
const STYLE = 1 << 3;
|
||||
/// `paint` variant, turns on paint containment
|
||||
const PAINT = 1 << 3;
|
||||
const PAINT = 1 << 4;
|
||||
/// 'size' variant, turns on size containment
|
||||
const SIZE = 1 << 4 | Contain::INLINE_SIZE.bits | Contain::BLOCK_SIZE.bits;
|
||||
const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits | Contain::BLOCK_SIZE.bits;
|
||||
/// `content` variant, turns on layout and paint containment
|
||||
const CONTENT = 1 << 5 | Contain::LAYOUT.bits | Contain::PAINT.bits;
|
||||
const CONTENT = 1 << 6 | Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits;
|
||||
/// `strict` variant, turns on all types of containment
|
||||
const STRICT = 1 << 6 | Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
|
||||
const STRICT = 1 << 7 | Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-body-bg-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-body-dir-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-body-overflow-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-body-t-o-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-body-w-m-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-content-011.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-html-bg-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-html-dir-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-html-overflow-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-html-t-o-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-html-w-m-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-strict-011.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-style-counters-001.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-style-counters-003.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[contain-style-counters-004.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[counter-scoping-001.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[counter-scoping-002.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[counter-scoping-003.html]
|
||||
expected: FAIL
|
||||
@@ -1,8 +1,5 @@
|
||||
[contain-computed.html]
|
||||
[Property contain value 'style paint']
|
||||
expected: FAIL
|
||||
|
||||
[Property contain value 'style']
|
||||
[Property contain value 'inline-size layout style paint']
|
||||
expected: FAIL
|
||||
|
||||
[Property contain value 'style layout paint']
|
||||
@@ -10,6 +7,3 @@
|
||||
|
||||
[Property contain value 'size style layout paint']
|
||||
expected: FAIL
|
||||
|
||||
[Property contain value 'inline-size layout style paint']
|
||||
expected: FAIL
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
[contain-valid.html]
|
||||
[e.style['contain'\] = "style" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['contain'\] = "paint style" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['contain'\] = "layout style paint" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['contain'\] = "layout paint style size" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['contain'\] = "layout inline-size" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[quote-scoping-001.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[quote-scoping-002.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[quote-scoping-003.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[quote-scoping-004.html]
|
||||
expected: FAIL
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>CSS Containment Test: 'contain: style' and counter (with 'display: contents')</title>
|
||||
|
||||
<link rel="help" href="https://www.w3.org/TR/css-contain-1/#containment-style">
|
||||
<link rel=help href="https://github.com/w3c/csswg-drafts/issues/7392">
|
||||
<link rel="match" href="reference/contain-style-counters-001-ref.html">
|
||||
|
||||
<meta content="This test checks that when an element has 'contain: style', then counters which may be affecting its subtree are reset to 0 for such scope. In this test, the div#test does not generate a principal box because of 'display: contents'. Despite that particular condition, 'contain: style' will have an effect on div#test." name="assert">
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
<title>CSS-contain test reference</title>
|
||||
<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
|
||||
|
||||
<p>Test passes if the text below is "A9Z" (not including the quotation marks).<p>
|
||||
<div>A9Z</span></div>
|
||||
<p>Test passes if the text below is "AZZ" (not including the quotation marks).<p>
|
||||
<div>AZZ</span></div>
|
||||
|
||||
Reference in New Issue
Block a user