Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "ContainStyleScopeManager.h"
#include "mozilla/ServoStyleSet.h"
#include "nsIContentInlines.h"
#include "CounterStyleManager.h"
#include "nsCounterManager.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsContentUtils.h"
#include "nsQuoteList.h"
namespace mozilla {
nsGenConNode* ContainStyleScope::GetPrecedingElementInGenConList(
nsGenConList* aList) {
auto IsAfter = [this](nsGenConNode* aNode) {
return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
mContent, aNode->mPseudoFrame->GetContent(),
/* aCommonAncestor = */ nullptr) > 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
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) {
using Tag = StyleCounterStyle::Tag;
const auto& listStyleType = aFrame->StyleList()->mListStyleType;
switch (listStyleType.tag) {
case Tag::None:
return;
case Tag::String:
listStyleType.AsString().AsAtom()->ToString(aText);
return;
case Tag::Symbols:
case Tag::Name:
break;
}
CounterValue ordinal = 1;
GetFirstCounterValueForScopeAndFrame(&GetRootScope(), aFrame, ordinal);
aFrame->PresContext()->CounterStyleManager()->WithCounterStyleNameOrSymbols(
listStyleType, [&](CounterStyle* aStyle) {
nsAutoString text;
bool isBullet;
aStyle->GetSpokenCounterText(ordinal, aFrame->GetWritingMode(), text,
isBullet);
if (isBullet) {
aText = text;
aText.Append(' ');
} else {
aStyle->GetPrefix(aText);
aText += text;
nsAutoString suffix;
aStyle->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