diff options
Diffstat (limited to 'layout/base/ContainStyleScopeManager.cpp')
-rw-r--r-- | layout/base/ContainStyleScopeManager.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/layout/base/ContainStyleScopeManager.cpp b/layout/base/ContainStyleScopeManager.cpp new file mode 100644 index 0000000000..80f3d79013 --- /dev/null +++ b/layout/base/ContainStyleScopeManager.cpp @@ -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/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 + // 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 |