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:
Martin Robinson
2022-06-22 16:16:59 +00:00
parent 4fb8d02cda
commit 797bb26bc0
45 changed files with 703 additions and 210 deletions

View File

@@ -5972,6 +5972,7 @@ exports.CSS_PROPERTIES = {
"revert-layer",
"size",
"strict",
"style",
"unset"
]
},

View 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

View 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_ */

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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();

View File

@@ -938,8 +938,8 @@ void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent,
frame->PresContext()
->FrameConstructor()
->CounterManager()
->GetSpokenCounterText(frame, aText);
->GetContainStyleScopeManager()
.GetSpokenCounterText(frame, aText);
}
#endif

View File

@@ -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(&quoteList))) {
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();
}

View File

@@ -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___ */

View File

@@ -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?

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -1,2 +0,0 @@
[contain-body-bg-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-body-dir-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-body-overflow-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-body-t-o-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-body-w-m-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-content-011.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-html-bg-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-html-dir-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-html-overflow-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-html-t-o-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-html-w-m-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-strict-011.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-style-counters-001.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-style-counters-003.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[contain-style-counters-004.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[counter-scoping-001.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[counter-scoping-002.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[counter-scoping-003.html]
expected: FAIL

View File

@@ -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

View File

@@ -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

View File

@@ -1,2 +0,0 @@
[quote-scoping-001.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[quote-scoping-002.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[quote-scoping-003.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[quote-scoping-004.html]
expected: FAIL

View File

@@ -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">

View File

@@ -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>