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 RETAINEDDISPLAYLISTBUILDER_H_
8 #define RETAINEDDISPLAYLISTBUILDER_H_
9 
10 #include "nsDisplayList.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/TypedEnumBits.h"
13 
14 class nsWindowSizes;
15 
16 /**
17  * RetainedDisplayListData contains frame invalidation information. It is stored
18  * in root frames, and used by RetainedDisplayListBuilder.
19  * Currently this is implemented as a map of frame pointers to flags.
20  */
21 struct RetainedDisplayListData {
22   NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListData, RetainedDisplayListData)
23 
24   enum class FrameFlags : uint8_t {
25     None = 0,
26     Modified = 1 << 0,
27     HasProps = 1 << 1,
28     HadWillChange = 1 << 2
29   };
30 
RetainedDisplayListDataRetainedDisplayListData31   RetainedDisplayListData() : mModifiedFramesCount(0) {}
32 
33   /**
34    * Adds the frame to modified frames list.
35    */
36   void AddModifiedFrame(nsIFrame* aFrame);
37 
38   /**
39    * Removes all the frames from this RetainedDisplayListData.
40    */
ClearRetainedDisplayListData41   void Clear() {
42     mFrames.Clear();
43     mModifiedFramesCount = 0;
44   }
45 
46   /**
47    * Returns a mutable reference to flags set for the given |aFrame|. If the
48    * frame does not exist in this RetainedDisplayListData, it is added with
49    * default constructible flags FrameFlags::None.
50    */
FlagsRetainedDisplayListData51   FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.LookupOrInsert(aFrame); }
52 
53   /**
54    * Returns flags set for the given |aFrame|, or FrameFlags::None if the frame
55    * is not in this RetainedDisplayListData.
56    */
GetFlagsRetainedDisplayListData57   FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); }
58 
59   /**
60    * Returns an iterator to the underlying frame storage.
61    */
ConstIteratorRetainedDisplayListData62   auto ConstIterator() { return mFrames.ConstIter(); }
63 
64   /**
65    * Returns the count of modified frames in this RetainedDisplayListData.
66    */
ModifiedFramesCountRetainedDisplayListData67   uint32_t ModifiedFramesCount() const { return mModifiedFramesCount; }
68 
69   /**
70    * Removes the given |aFrame| from this RetainedDisplayListData.
71    */
RemoveRetainedDisplayListData72   bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); }
73 
74  private:
75   nsTHashMap<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames;
76   uint32_t mModifiedFramesCount;
77 };
78 
79 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(RetainedDisplayListData::FrameFlags)
80 
81 /**
82  * Returns RetainedDisplayListData property for the given |aRootFrame|, or
83  * nullptr if the property is not set.
84  */
85 RetainedDisplayListData* GetRetainedDisplayListData(nsIFrame* aRootFrame);
86 
87 /**
88  * Returns RetainedDisplayListData property for the given |aRootFrame|. Creates
89  * and sets a new RetainedDisplayListData property if it is not already set.
90  */
91 RetainedDisplayListData* GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame);
92 
93 enum class PartialUpdateResult { Failed, NoChange, Updated };
94 
95 enum class PartialUpdateFailReason {
96   NA,
97   EmptyList,
98   RebuildLimit,
99   FrameType,
100   Disabled,
101   Content,
102   VisibleRect,
103 };
104 
105 struct RetainedDisplayListMetrics {
RetainedDisplayListMetricsRetainedDisplayListMetrics106   RetainedDisplayListMetrics() { Reset(); }
107 
ResetRetainedDisplayListMetrics108   void Reset() {
109     mNewItems = 0;
110     mRebuiltItems = 0;
111     mRemovedItems = 0;
112     mReusedItems = 0;
113     mTotalItems = 0;
114     mPartialBuildDuration = 0;
115     mFullBuildDuration = 0;
116     mPartialUpdateFailReason = PartialUpdateFailReason::NA;
117     mPartialUpdateResult = PartialUpdateResult::NoChange;
118   }
119 
StartBuildRetainedDisplayListMetrics120   void StartBuild() { mStartTime = mozilla::TimeStamp::Now(); }
121 
EndFullBuildRetainedDisplayListMetrics122   void EndFullBuild() { mFullBuildDuration = Elapsed(); }
123 
EndPartialBuildRetainedDisplayListMetrics124   void EndPartialBuild(PartialUpdateResult aResult) {
125     mPartialBuildDuration = Elapsed();
126     mPartialUpdateResult = aResult;
127   }
128 
ElapsedRetainedDisplayListMetrics129   double Elapsed() {
130     return (mozilla::TimeStamp::Now() - mStartTime).ToMilliseconds();
131   }
132 
FailReasonStringRetainedDisplayListMetrics133   const char* FailReasonString() const {
134     switch (mPartialUpdateFailReason) {
135       case PartialUpdateFailReason::NA:
136         return "N/A";
137       case PartialUpdateFailReason::EmptyList:
138         return "Empty list";
139       case PartialUpdateFailReason::RebuildLimit:
140         return "Rebuild limit";
141       case PartialUpdateFailReason::FrameType:
142         return "Frame type";
143       case PartialUpdateFailReason::Disabled:
144         return "Disabled";
145       case PartialUpdateFailReason::Content:
146         return "Content";
147       case PartialUpdateFailReason::VisibleRect:
148         return "VisibleRect";
149       default:
150         MOZ_ASSERT_UNREACHABLE("Enum value not handled!");
151     }
152   }
153 
154   unsigned int mNewItems;
155   unsigned int mRebuiltItems;
156   unsigned int mRemovedItems;
157   unsigned int mReusedItems;
158   unsigned int mTotalItems;
159 
160   mozilla::TimeStamp mStartTime;
161   double mPartialBuildDuration;
162   double mFullBuildDuration;
163   PartialUpdateFailReason mPartialUpdateFailReason;
164   PartialUpdateResult mPartialUpdateResult;
165 };
166 
167 struct RetainedDisplayListBuilder {
RetainedDisplayListBuilderRetainedDisplayListBuilder168   RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
169                              nsDisplayListBuilderMode aMode, bool aBuildCaret)
170       : mBuilder(aReferenceFrame, aMode, aBuildCaret, true) {}
~RetainedDisplayListBuilderRetainedDisplayListBuilder171   ~RetainedDisplayListBuilder() { mList.DeleteAll(&mBuilder); }
172 
BuilderRetainedDisplayListBuilder173   nsDisplayListBuilder* Builder() { return &mBuilder; }
174 
ListRetainedDisplayListBuilder175   nsDisplayList* List() { return &mList; }
176 
MetricsRetainedDisplayListBuilder177   RetainedDisplayListMetrics* Metrics() { return &mMetrics; }
178 
179   PartialUpdateResult AttemptPartialUpdate(nscolor aBackstop);
180 
181   /**
182    * Iterates through the display list builder reference frame document and
183    * subdocuments, and clears the modified frame lists from the root frames.
184    * Also clears the frame properties set by RetainedDisplayListBuilder for all
185    * the frames in the modified frame lists.
186    */
187   void ClearFramesWithProps();
188   void AddSizeOfIncludingThis(nsWindowSizes&) const;
189 
190   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
191 
192  private:
193   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
194 
195   /**
196    * Invalidates the current and previous caret frame if they have changed.
197    */
198   void InvalidateCaretFramesIfNeeded();
199 
200   /**
201    * A simple early exit heuristic to avoid slow partial display list rebuilds.
202    * Returns true if a partial display list build should be attempted.
203    */
204   bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames);
205 
206   /**
207    * Recursively pre-processes the old display list tree before building the
208    * new partial display lists, and serializes the old list into an array,
209    * recording indices on items for fast lookup during merging. Builds an
210    * initial linear DAG for the list if we don't have an existing one. Finds
211    * items that have a different AGR from the specified one, and marks them to
212    * also be built so that we get relative ordering correct. Passes
213    * aKeepLinked=true internally for sub-lists that can't be changed to keep the
214    * original list structure linked for fast re-use.
215    */
216   bool PreProcessDisplayList(RetainedDisplayList* aList,
217                              AnimatedGeometryRoot* aAGR,
218                              PartialUpdateResult& aUpdated,
219                              nsIFrame* aOuterFrame = nullptr,
220                              uint32_t aCallerKey = 0,
221                              uint32_t aNestingDepth = 0,
222                              bool aKeepLinked = false);
223 
224   /**
225    * Merges items from aNewList into non-invalidated items from aOldList and
226    * stores the result in aOutList.
227    *
228    * aOuterItem is a pointer to an item that owns one of the lists, if
229    * available. If both lists are populated, then both outer items must not be
230    * invalidated, and identical, so either can be passed here.
231    *
232    * Returns true if changes were made, and the resulting display list (in
233    * aOutList) is different from aOldList.
234    */
235   bool MergeDisplayLists(
236       nsDisplayList* aNewList, RetainedDisplayList* aOldList,
237       RetainedDisplayList* aOutList,
238       mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
239       nsDisplayItem* aOuterItem = nullptr);
240 
241   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
242                             nsRect* aOutDirty,
243                             AnimatedGeometryRoot** aOutModifiedAGR,
244                             nsTArray<nsIFrame*>& aOutFramesWithProps);
245 
246   bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
247                     nsIFrame* aStopAtFrame,
248                     nsTArray<nsIFrame*>& aOutFramesWithProps,
249                     const bool aStopAtStackingContext, nsRect* aOutDirty,
250                     AnimatedGeometryRoot** aOutModifiedAGR);
251 
252   friend class MergeState;
253 
254   nsDisplayListBuilder mBuilder;
255   RetainedDisplayList mList;
256   nsRect mPreviousVisibleRect;
257   WeakFrame mPreviousCaret;
258   RetainedDisplayListMetrics mMetrics;
259 };
260 
261 #endif  // RETAINEDDISPLAYLISTBUILDER_H_
262