ContainStyleScopeManager.cpp (7656B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ContainStyleScopeManager.h" 8 9 #include "CounterStyleManager.h" 10 #include "mozilla/ServoStyleSet.h" 11 #include "nsContentUtils.h" 12 #include "nsCounterManager.h" 13 #include "nsIContent.h" 14 #include "nsIContentInlines.h" 15 #include "nsIFrame.h" 16 #include "nsQuoteList.h" 17 18 namespace mozilla { 19 20 nsGenConNode* ContainStyleScope::GetPrecedingElementInGenConList( 21 nsGenConList* aList) { 22 nsContentUtils::NodeIndexCache cache; 23 auto IsAfter = [this, &cache](nsGenConNode* aNode) { 24 return nsContentUtils::CompareTreePosition<TreeKind::Flat>( 25 mContent, aNode->mPseudoFrame->GetContent(), 26 /* aCommonAncestor = */ nullptr, &cache) >= 0; 27 }; 28 auto* last = aList->GetLast(); 29 if (!last || IsAfter(last)) { 30 return last; 31 } 32 return aList->BinarySearch(IsAfter)->getPrevious(); 33 } 34 35 void ContainStyleScope::RecalcAllCounters() { 36 GetCounterManager().RecalcAll(); 37 for (auto* child : mChildren) { 38 child->RecalcAllCounters(); 39 } 40 } 41 42 void ContainStyleScope::RecalcAllQuotes() { 43 GetQuoteList().RecalcAll(); 44 for (auto* child : mChildren) { 45 child->RecalcAllQuotes(); 46 } 47 } 48 49 ContainStyleScope& ContainStyleScopeManager::GetOrCreateScopeForContent( 50 nsIContent* aContent) { 51 for (; aContent; aContent = aContent->GetFlattenedTreeParent()) { 52 auto* element = dom::Element::FromNode(*aContent); 53 if (!element) { 54 continue; 55 } 56 57 // Do not allow elements which have `display: contents` to create style 58 // boundaries. See https://github.com/w3c/csswg-drafts/issues/7392. 59 if (element->IsDisplayContents()) { 60 continue; 61 } 62 63 const auto* style = Servo_Element_GetMaybeOutOfDateStyle(element); 64 if (!style) { 65 continue; 66 } 67 68 if (!style->SelfOrAncestorHasContainStyle()) { 69 return GetRootScope(); 70 } 71 72 if (!style->StyleDisplay()->IsContainStyle()) { 73 continue; 74 } 75 76 if (auto* scope = mScopes.Get(aContent)) { 77 return *scope; 78 } 79 80 auto& parentScope = 81 GetOrCreateScopeForContent(aContent->GetFlattenedTreeParent()); 82 return *mScopes.InsertOrUpdate( 83 aContent, MakeUnique<ContainStyleScope>(this, &parentScope, aContent)); 84 } 85 86 return GetRootScope(); 87 } 88 89 ContainStyleScope& ContainStyleScopeManager::GetScopeForContent( 90 nsIContent* aContent) { 91 MOZ_ASSERT(aContent); 92 93 if (auto* element = dom::Element::FromNode(*aContent)) { 94 if (const auto* style = Servo_Element_GetMaybeOutOfDateStyle(element)) { 95 if (!style->SelfOrAncestorHasContainStyle()) { 96 return GetRootScope(); 97 } 98 } 99 } 100 101 for (; aContent; aContent = aContent->GetFlattenedTreeParent()) { 102 if (auto* scope = mScopes.Get(aContent)) { 103 return *scope; 104 } 105 } 106 107 return GetRootScope(); 108 } 109 110 void ContainStyleScopeManager::Clear() { 111 GetRootScope().GetQuoteList().Clear(); 112 GetRootScope().GetCounterManager().Clear(); 113 114 DestroyScope(&GetRootScope()); 115 MOZ_DIAGNOSTIC_ASSERT(mScopes.IsEmpty(), 116 "Destroying the root scope should destroy all scopes."); 117 } 118 119 void ContainStyleScopeManager::DestroyScopesFor(nsIFrame* aFrame) { 120 if (auto* scope = mScopes.Get(aFrame->GetContent())) { 121 DestroyScope(scope); 122 } 123 } 124 125 void ContainStyleScopeManager::DestroyScope(ContainStyleScope* aScope) { 126 // Deleting a scope modifies the array of children in its parent, so we don't 127 // use an iterator here. 128 while (!aScope->GetChildren().IsEmpty()) { 129 DestroyScope(aScope->GetChildren().ElementAt(0)); 130 } 131 mScopes.Remove(aScope->GetContent()); 132 } 133 134 bool ContainStyleScopeManager::DestroyCounterNodesFor(nsIFrame* aFrame) { 135 bool result = false; 136 for (auto* scope = &GetScopeForContent(aFrame->GetContent()); scope; 137 scope = scope->GetParent()) { 138 result |= scope->GetCounterManager().DestroyNodesFor(aFrame); 139 } 140 return result; 141 } 142 143 bool ContainStyleScopeManager::AddCounterChanges(nsIFrame* aNewFrame) { 144 return GetOrCreateScopeForContent( 145 aNewFrame->GetContent()->GetFlattenedTreeParent()) 146 .GetCounterManager() 147 .AddCounterChanges(aNewFrame); 148 } 149 150 nsCounterList* ContainStyleScopeManager::GetOrCreateCounterList( 151 dom::Element& aElement, nsAtom* aCounterName) { 152 return GetOrCreateScopeForContent(&aElement) 153 .GetCounterManager() 154 .GetOrCreateCounterList(aCounterName); 155 } 156 157 bool ContainStyleScopeManager::CounterDirty(nsAtom* aCounterName) { 158 return mDirtyCounters.Contains(aCounterName); 159 } 160 161 void ContainStyleScopeManager::SetCounterDirty(nsAtom* aCounterName) { 162 mDirtyCounters.Insert(aCounterName); 163 } 164 165 void ContainStyleScopeManager::RecalcAllCounters() { 166 GetRootScope().RecalcAllCounters(); 167 mDirtyCounters.Clear(); 168 } 169 170 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER) 171 void ContainStyleScopeManager::DumpCounters() { 172 GetRootScope().GetCounterManager().Dump(); 173 for (auto& entry : mScopes) { 174 entry.GetWeak()->GetCounterManager().Dump(); 175 } 176 } 177 #endif 178 179 #ifdef ACCESSIBILITY 180 static bool GetFirstCounterValueForScopeAndFrame(ContainStyleScope* aScope, 181 nsIFrame* aFrame, 182 CounterValue& aOrdinal) { 183 if (aScope->GetCounterManager().GetFirstCounterValueForFrame(aFrame, 184 aOrdinal)) { 185 return true; 186 } 187 for (auto* child : aScope->GetChildren()) { 188 if (GetFirstCounterValueForScopeAndFrame(child, aFrame, aOrdinal)) { 189 return true; 190 } 191 } 192 193 return false; 194 } 195 196 void ContainStyleScopeManager::GetSpokenCounterText(nsIFrame* aFrame, 197 nsAString& aText) { 198 using Tag = StyleCounterStyle::Tag; 199 const auto& listStyleType = aFrame->StyleList()->mListStyleType; 200 switch (listStyleType.tag) { 201 case Tag::None: 202 return; 203 case Tag::String: 204 listStyleType.AsString().AsAtom()->ToString(aText); 205 return; 206 case Tag::Symbols: 207 case Tag::Name: 208 break; 209 } 210 211 CounterValue ordinal = 1; 212 GetFirstCounterValueForScopeAndFrame(&GetRootScope(), aFrame, ordinal); 213 214 aFrame->PresContext()->CounterStyleManager()->WithCounterStyleNameOrSymbols( 215 listStyleType, [&](CounterStyle* aStyle) { 216 nsAutoString text; 217 bool isBullet; 218 aStyle->GetSpokenCounterText(ordinal, aFrame->GetWritingMode(), text, 219 isBullet); 220 if (isBullet) { 221 aText = text; 222 aText.Append(' '); 223 } else { 224 aStyle->GetPrefix(aText); 225 aText += text; 226 nsAutoString suffix; 227 aStyle->GetSuffix(suffix); 228 aText += suffix; 229 } 230 }); 231 } 232 #endif 233 234 void ContainStyleScopeManager::SetAllCountersDirty() { 235 GetRootScope().GetCounterManager().SetAllDirty(); 236 for (auto& entry : mScopes) { 237 entry.GetWeak()->GetCounterManager().SetAllDirty(); 238 } 239 } 240 241 bool ContainStyleScopeManager::DestroyQuoteNodesFor(nsIFrame* aFrame) { 242 bool result = false; 243 for (auto* scope = &GetScopeForContent(aFrame->GetContent()); scope; 244 scope = scope->GetParent()) { 245 result |= scope->GetQuoteList().DestroyNodesFor(aFrame); 246 } 247 return result; 248 } 249 250 nsQuoteList* ContainStyleScopeManager::QuoteListFor(dom::Element& aElement) { 251 return &GetOrCreateScopeForContent(&aElement).GetQuoteList(); 252 } 253 254 void ContainStyleScopeManager::RecalcAllQuotes() { 255 GetRootScope().RecalcAllQuotes(); 256 } 257 258 } // namespace mozilla