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 #ifndef mozilla_ServoRestyleManager_h 8 #define mozilla_ServoRestyleManager_h 9 10 #include "mozilla/EventStates.h" 11 #include "mozilla/RestyleManager.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/ServoElementSnapshot.h" 14 #include "mozilla/ServoElementSnapshotTable.h" 15 #include "nsChangeHint.h" 16 #include "nsPresContext.h" 17 18 namespace mozilla { 19 namespace dom { 20 class Element; 21 } // namespace dom 22 } // namespace mozilla 23 class nsAttrValue; 24 class nsAtom; 25 class nsIContent; 26 class nsIFrame; 27 class nsStyleChangeList; 28 29 namespace mozilla { 30 31 /** 32 * A stack class used to pass some common restyle state in a slightly more 33 * comfortable way than a bunch of individual arguments, and that also checks 34 * that the change hint used for optimization is correctly used in debug mode. 35 */ 36 class ServoRestyleState { 37 public: ServoRestyleState(ServoStyleSet & aStyleSet,nsStyleChangeList & aChangeList,nsTArray<nsIFrame * > & aPendingWrapperRestyles)38 ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList, 39 nsTArray<nsIFrame*>& aPendingWrapperRestyles) 40 : mStyleSet(aStyleSet), 41 mChangeList(aChangeList), 42 mPendingWrapperRestyles(aPendingWrapperRestyles), 43 mPendingWrapperRestyleOffset(aPendingWrapperRestyles.Length()), 44 mChangesHandled(nsChangeHint(0)) 45 #ifdef DEBUG 46 // If !mOwner, then we wouldn't have processed our wrapper restyles, 47 // because we only process those when handling an element with a frame. 48 // But that's OK, because if we started our traversal at an element with 49 // no frame (e.g. it's display:contents), that means the wrapper frames 50 // in our list actually inherit from one of its ancestors, not from it, 51 // and hence not restyling them is OK. 52 , 53 mAssertWrapperRestyleLength(false) 54 #endif // DEBUG 55 { 56 } 57 58 // We shouldn't assume that changes handled from our parent are handled for 59 // our children too if we're out of flow since they aren't necessarily 60 // parented in DOM order, and thus a change handled by a DOM ancestor doesn't 61 // necessarily mean that it's handled for an ancestor frame. 62 enum class Type { 63 InFlow, 64 OutOfFlow, 65 }; 66 67 ServoRestyleState(const nsIFrame& aOwner, ServoRestyleState& aParentState, 68 nsChangeHint aHintForThisFrame, Type aType, 69 bool aAssertWrapperRestyleLength = true) 70 : mStyleSet(aParentState.mStyleSet), 71 mChangeList(aParentState.mChangeList), 72 mPendingWrapperRestyles(aParentState.mPendingWrapperRestyles), 73 mPendingWrapperRestyleOffset( 74 aParentState.mPendingWrapperRestyles.Length()), 75 mChangesHandled(aType == Type::InFlow 76 ? aParentState.mChangesHandled | aHintForThisFrame 77 : aHintForThisFrame) 78 #ifdef DEBUG 79 , 80 mOwner(&aOwner), 81 mAssertWrapperRestyleLength(aAssertWrapperRestyleLength) 82 #endif 83 { 84 if (aType == Type::InFlow) { 85 AssertOwner(aParentState); 86 } 87 } 88 ~ServoRestyleState()89 ~ServoRestyleState() { 90 MOZ_ASSERT( 91 !mAssertWrapperRestyleLength || 92 mPendingWrapperRestyles.Length() == mPendingWrapperRestyleOffset, 93 "Someone forgot to call ProcessWrapperRestyles!"); 94 } 95 ChangeList()96 nsStyleChangeList& ChangeList() { return mChangeList; } StyleSet()97 ServoStyleSet& StyleSet() { return mStyleSet; } 98 99 #ifdef DEBUG 100 void AssertOwner(const ServoRestyleState& aParentState) const; 101 nsChangeHint ChangesHandledFor(const nsIFrame&) const; 102 #else AssertOwner(const ServoRestyleState &)103 void AssertOwner(const ServoRestyleState&) const {} ChangesHandledFor(const nsIFrame &)104 nsChangeHint ChangesHandledFor(const nsIFrame&) const { 105 return mChangesHandled; 106 } 107 #endif 108 109 // Add a pending wrapper restyle. We don't have to do anything if the thing 110 // being added is already last in the list, but otherwise we do want to add 111 // it, in order for ProcessWrapperRestyles to work correctly. 112 void AddPendingWrapperRestyle(nsIFrame* aWrapperFrame); 113 114 // Process wrapper restyles for this restyle state. This should be done 115 // before it comes off the stack. 116 void ProcessWrapperRestyles(nsIFrame* aParentFrame); 117 118 // Get the table-aware parent for the given child. This will walk through 119 // outer table and cellcontent frames. 120 static nsIFrame* TableAwareParentFor(const nsIFrame* aChild); 121 122 private: 123 // Process a wrapper restyle at the given index, and restyles for any 124 // wrappers nested in it. Returns the number of entries from 125 // mPendingWrapperRestyles that we processed. The return value is always at 126 // least 1. 127 size_t ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, size_t aIndex); 128 129 ServoStyleSet& mStyleSet; 130 nsStyleChangeList& mChangeList; 131 132 // A list of pending wrapper restyles. Anonymous box wrapper frames that need 133 // restyling are added to this list when their non-anonymous kids are 134 // restyled. This avoids us having to do linear searches along the frame tree 135 // for these anonymous boxes. The problem then becomes that we can have 136 // multiple kids all with the same anonymous parent, and we don't want to 137 // restyle it more than once. We use mPendingWrapperRestyles to track which 138 // anonymous wrapper boxes we've requested be restyled and which of them have 139 // already been restyled. We use a single array propagated through 140 // ServoRestyleStates by reference, because in a situation like this: 141 // 142 // <div style="display: table"><span></span></div> 143 // 144 // We have multiple wrappers to restyle (cell, row, table-row-group) and we 145 // want to add them in to the list all at once but restyle them using 146 // different ServoRestyleStates with different owners. When this situation 147 // occurs, the relevant frames will be placed in the array with ancestors 148 // before descendants. 149 nsTArray<nsIFrame*>& mPendingWrapperRestyles; 150 151 // Since we're given a possibly-nonempty mPendingWrapperRestyles to start 152 // with, we need to keep track of where the part of it we're responsible for 153 // starts. 154 size_t mPendingWrapperRestyleOffset; 155 156 const nsChangeHint mChangesHandled; 157 158 // We track the "owner" frame of this restyle state, that is, the frame that 159 // generated the last change that is stored in mChangesHandled, in order to 160 // verify that we only use mChangesHandled for actual descendants of that 161 // frame (given DOM order isn't always frame order, and that there are a few 162 // special cases for stuff like wrapper frames, ::backdrop, and so on). 163 #ifdef DEBUG 164 const nsIFrame* mOwner{nullptr}; 165 #endif 166 167 // Whether we should assert in our destructor that we've processed all of the 168 // relevant wrapper restyles. 169 #ifdef DEBUG 170 const bool mAssertWrapperRestyleLength; 171 #endif // DEBUG 172 }; 173 174 enum class ServoPostTraversalFlags : uint32_t; 175 176 /** 177 * Restyle manager for a Servo-backed style system. 178 */ 179 class ServoRestyleManager : public RestyleManager { 180 friend class ServoStyleSet; 181 182 public: 183 typedef ServoElementSnapshotTable SnapshotTable; 184 typedef RestyleManager base_type; 185 186 explicit ServoRestyleManager(nsPresContext* aPresContext); 187 188 void PostRestyleEvent(dom::Element* aElement, nsRestyleHint aRestyleHint, 189 nsChangeHint aMinChangeHint); 190 void PostRestyleEventForCSSRuleChanges(); 191 void RebuildAllStyleData(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint); 192 void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint, 193 nsRestyleHint aRestyleHint); 194 void ProcessPendingRestyles(); 195 void ProcessAllPendingAttributeAndStateInvalidations(); 196 bool HasPendingRestyleAncestor(dom::Element* aElement) const; 197 198 /** 199 * Performs a Servo animation-only traversal to compute style for all nodes 200 * with the animation-only dirty bit in the document. 201 * 202 * This processes just the traversal for animation-only restyles and skips the 203 * normal traversal for other restyles unrelated to animations. 204 * This is used to bring throttled animations up-to-date such as when we need 205 * to get correct position for transform animations that are throttled because 206 * they are running on the compositor. 207 * 208 * This will traverse all of the document's style roots (that is, its document 209 * element, and the roots of the document-level native anonymous content). 210 */ 211 void UpdateOnlyAnimationStyles(); 212 213 void ContentStateChanged(nsIContent* aContent, EventStates aStateMask); 214 void AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, 215 nsAtom* aAttribute, int32_t aModType, 216 const nsAttrValue* aNewValue); 217 void ClassAttributeWillBeChangedBySMIL(dom::Element* aElement); 218 219 void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, 220 nsAtom* aAttribute, int32_t aModType, 221 const nsAttrValue* aOldValue); 222 223 // This is only used to reparent things when moving them in/out of the 224 // ::first-line. Once we get rid of the Gecko style system, we should rename 225 // this method accordingly (e.g. to ReparentStyleContextForFirstLine). 226 nsresult ReparentStyleContext(nsIFrame* aFrame); 227 228 private: 229 /** 230 * Reparent the descendants of aFrame. This is used by ReparentStyleContext 231 * and shouldn't be called by anyone else. aProviderChild, if non-null, is a 232 * child that was the style parent for aFrame and hence shouldn't be 233 * reparented. 234 */ 235 void ReparentFrameDescendants(nsIFrame* aFrame, nsIFrame* aProviderChild, 236 ServoStyleSet& aStyleSet); 237 238 public: 239 /** 240 * Whether to clear all the style data (including the element itself), or just 241 * the descendants' data. 242 */ 243 enum class IncludeRoot { 244 Yes, 245 No, 246 }; 247 248 /** 249 * Clears the ServoElementData and HasDirtyDescendants from all elements 250 * in the subtree rooted at aElement. 251 */ 252 static void ClearServoDataFromSubtree(Element*, 253 IncludeRoot = IncludeRoot::Yes); 254 255 /** 256 * Clears HasDirtyDescendants and RestyleData from all elements in the 257 * subtree rooted at aElement. 258 */ 259 static void ClearRestyleStateFromSubtree(Element* aElement); 260 261 /** 262 * Posts restyle hints for animations. 263 * This is only called for the second traversal for CSS animations during 264 * updating CSS animations in a SequentialTask. 265 * This function does neither register a refresh observer nor flag that a 266 * style flush is needed since this function is supposed to be called during 267 * restyling process and this restyle event will be processed in the second 268 * traversal of the same restyling process. 269 */ 270 void PostRestyleEventForAnimations(dom::Element* aElement, 271 CSSPseudoElementType aPseudoType, 272 nsRestyleHint aRestyleHint); 273 274 protected: ~ServoRestyleManager()275 ~ServoRestyleManager() override { MOZ_ASSERT(!mReentrantChanges); } 276 277 private: 278 /** 279 * Performs post-Servo-traversal processing on this element and its 280 * descendants. 281 * 282 * Returns whether any style did actually change. There may be cases where we 283 * didn't need to change any style after all, for example, when a content 284 * attribute changes that happens not to have any effect on the style of that 285 * element or any descendant or sibling. 286 */ 287 bool ProcessPostTraversal(Element* aElement, 288 ServoStyleContext* aParentContext, 289 ServoRestyleState& aRestyleState, 290 ServoPostTraversalFlags aFlags); 291 292 struct TextPostTraversalState; 293 bool ProcessPostTraversalForText(nsIContent* aTextNode, 294 TextPostTraversalState& aState, 295 ServoRestyleState& aRestyleState, 296 ServoPostTraversalFlags aFlags); 297 StyleSet()298 inline ServoStyleSet* StyleSet() const { 299 MOZ_ASSERT(PresContext()->StyleSet()->IsServo(), 300 "ServoRestyleManager should only be used with a Servo-flavored " 301 "style backend"); 302 return PresContext()->StyleSet()->AsServo(); 303 } 304 Snapshots()305 const SnapshotTable& Snapshots() const { return mSnapshots; } 306 void ClearSnapshots(); 307 ServoElementSnapshot& SnapshotFor(mozilla::dom::Element* aElement); 308 void TakeSnapshotForAttributeChange(mozilla::dom::Element* aElement, 309 int32_t aNameSpaceID, nsAtom* aAttribute); 310 311 void DoProcessPendingRestyles(ServoTraversalFlags aFlags); 312 313 // Function to do the actual (recursive) work of ReparentStyleContext, once we 314 // have asserted the invariants that only hold on the initial call. 315 void DoReparentStyleContext(nsIFrame* aFrame, ServoStyleSet& aStyleSet); 316 317 // We use a separate data structure from nsStyleChangeList because we need a 318 // frame to create nsStyleChangeList entries, and the primary frame may not be 319 // attached yet. 320 struct ReentrantChange { 321 nsCOMPtr<nsIContent> mContent; 322 nsChangeHint mHint; 323 }; 324 typedef AutoTArray<ReentrantChange, 10> ReentrantChangeList; 325 326 // Only non-null while processing change hints. See the comment in 327 // ProcessPendingRestyles. 328 ReentrantChangeList* mReentrantChanges; 329 330 // We use this flag to track if the current restyle contains any non-animation 331 // update, which triggers a normal restyle, and so there might be any new 332 // transition created later. Therefore, if this flag is true, we need to 333 // increase mAnimationGeneration before creating new transitions, so their 334 // creation sequence will be correct. 335 bool mHaveNonAnimationRestyles = false; 336 337 // Set to true when posting restyle events triggered by CSS rule changes. 338 // This flag is cleared once ProcessPendingRestyles has completed. 339 // When we process a traversal all descendants elements of the document 340 // triggered by CSS rule changes, we will need to update all elements with 341 // CSS animations. We propagate TraversalRestyleBehavior::ForCSSRuleChanges 342 // to traversal function if this flag is set. 343 bool mRestyleForCSSRuleChanges = false; 344 345 // A hashtable with the elements that have changed state or attributes, in 346 // order to calculate restyle hints during the traversal. 347 SnapshotTable mSnapshots; 348 }; 349 350 } // namespace mozilla 351 352 #endif // mozilla_ServoRestyleManager_h 353