Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/OverflowChangedTracker.h
Line
Count
Source (jump to first uncovered line)
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_OverflowChangedTracker_h
8
#define mozilla_OverflowChangedTracker_h
9
10
#include "nsIFrame.h"
11
#include "nsContainerFrame.h"
12
#include "mozilla/SplayTree.h"
13
14
namespace mozilla {
15
16
/**
17
 * Helper class that collects a list of frames that need
18
 * UpdateOverflow() called on them, and coalesces them
19
 * to avoid walking up the same ancestor tree multiple times.
20
 */
21
class OverflowChangedTracker
22
{
23
public:
24
  enum ChangeKind {
25
    /**
26
     * The frame was explicitly added as a result of
27
     * nsChangeHint_UpdatePostTransformOverflow and hence may have had a style
28
     * change that changes its geometry relative to parent, without reflowing.
29
     */
30
    TRANSFORM_CHANGED,
31
    /**
32
     * The overflow areas of children have changed
33
     * and we need to call UpdateOverflow on the frame.
34
     */
35
    CHILDREN_CHANGED,
36
  };
37
38
  OverflowChangedTracker() :
39
    mSubtreeRoot(nullptr)
40
0
  {}
41
42
  ~OverflowChangedTracker()
43
0
  {
44
0
    NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
45
0
  }
46
47
  /**
48
   * Add a frame that has had a style change, and needs its
49
   * overflow updated.
50
   *
51
   * If there are pre-transform overflow areas stored for this
52
   * frame, then we will call FinishAndStoreOverflow with those
53
   * areas instead of UpdateOverflow().
54
   *
55
   * If the overflow area changes, then UpdateOverflow will also
56
   * be called on the parent.
57
   */
58
0
  void AddFrame(nsIFrame* aFrame, ChangeKind aChangeKind) {
59
0
    uint32_t depth = aFrame->GetDepthInFrameTree();
60
0
    Entry *entry = nullptr;
61
0
    if (!mEntryList.empty()) {
62
0
      entry = mEntryList.find(Entry(aFrame, depth));
63
0
    }
64
0
    if (entry == nullptr) {
65
0
      // Add new entry.
66
0
      mEntryList.insert(new Entry(aFrame, depth, aChangeKind));
67
0
    } else {
68
0
      // Update the existing entry if the new value is stronger.
69
0
      entry->mChangeKind = std::max(entry->mChangeKind, aChangeKind);
70
0
    }
71
0
  }
72
73
  /**
74
   * Remove a frame.
75
   */
76
0
  void RemoveFrame(nsIFrame* aFrame) {
77
0
    if (mEntryList.empty()) {
78
0
      return;
79
0
    }
80
0
81
0
    uint32_t depth = aFrame->GetDepthInFrameTree();
82
0
    if (mEntryList.find(Entry(aFrame, depth))) {
83
0
      delete mEntryList.remove(Entry(aFrame, depth));
84
0
    }
85
0
  }
86
87
  /**
88
   * Set the subtree root to limit overflow updates. This must be set if and
89
   * only if currently reflowing aSubtreeRoot, to ensure overflow changes will
90
   * still propagate correctly.
91
   */
92
0
  void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
93
0
    mSubtreeRoot = aSubtreeRoot;
94
0
  }
95
96
  /**
97
   * Update the overflow of all added frames, and clear the entry list.
98
   *
99
   * Start from those deepest in the frame tree and works upwards. This stops
100
   * us from processing the same frame twice.
101
   */
102
0
  void Flush() {
103
0
    while (!mEntryList.empty()) {
104
0
      Entry *entry = mEntryList.removeMin();
105
0
      nsIFrame *frame = entry->mFrame;
106
0
107
0
      bool overflowChanged = false;
108
0
      if (entry->mChangeKind == CHILDREN_CHANGED) {
109
0
        // Need to union the overflow areas of the children.
110
0
        // Only update the parent if the overflow changes.
111
0
        overflowChanged = frame->UpdateOverflow();
112
0
      } else {
113
0
        // Take a faster path that doesn't require unioning the overflow areas
114
0
        // of our children.
115
0
116
0
        NS_ASSERTION(frame->GetProperty(
117
0
                       nsIFrame::DebugInitialOverflowPropertyApplied()),
118
0
                     "InitialOverflowProperty must be set first.");
119
0
120
0
        nsOverflowAreas* overflow =
121
0
          frame->GetProperty(nsIFrame::InitialOverflowProperty());
122
0
        if (overflow) {
123
0
          // FinishAndStoreOverflow will change the overflow areas passed in,
124
0
          // so make a copy.
125
0
          nsOverflowAreas overflowCopy = *overflow;
126
0
          frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
127
0
        } else {
128
0
          nsRect bounds(nsPoint(0, 0), frame->GetSize());
129
0
          nsOverflowAreas boundsOverflow;
130
0
          boundsOverflow.SetAllTo(bounds);
131
0
          frame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
132
0
        }
133
0
134
0
        // We can't tell if the overflow changed, so be conservative
135
0
        overflowChanged = true;
136
0
      }
137
0
138
0
      // If the frame style changed (e.g. positioning offsets)
139
0
      // then we need to update the parent with the overflow areas of its
140
0
      // children.
141
0
      if (overflowChanged) {
142
0
        nsIFrame *parent = frame->GetParent();
143
0
        while (parent &&
144
0
               parent != mSubtreeRoot &&
145
0
               parent->Combines3DTransformWithAncestors()) {
146
0
          // Passing frames in between the frame and the establisher of
147
0
          // 3D rendering context.
148
0
          parent = parent->GetParent();
149
0
          MOZ_ASSERT(parent, "Root frame should never return true for Combines3DTransformWithAncestors");
150
0
        }
151
0
        if (parent && parent != mSubtreeRoot) {
152
0
          Entry* parentEntry = mEntryList.find(Entry(parent, entry->mDepth - 1));
153
0
          if (parentEntry) {
154
0
            parentEntry->mChangeKind = std::max(parentEntry->mChangeKind, CHILDREN_CHANGED);
155
0
          } else {
156
0
            mEntryList.insert(new Entry(parent, entry->mDepth - 1, CHILDREN_CHANGED));
157
0
          }
158
0
        }
159
0
      }
160
0
      delete entry;
161
0
    }
162
0
  }
163
164
private:
165
  struct Entry : SplayTreeNode<Entry>
166
  {
167
    Entry(nsIFrame* aFrame, uint32_t aDepth, ChangeKind aChangeKind = CHILDREN_CHANGED)
168
      : mFrame(aFrame)
169
      , mDepth(aDepth)
170
      , mChangeKind(aChangeKind)
171
0
    {}
172
173
    bool operator==(const Entry& aOther) const
174
0
    {
175
0
      return mFrame == aOther.mFrame;
176
0
    }
177
178
    /**
179
     * Sort by *reverse* depth in the tree, and break ties with
180
     * the frame pointer.
181
     */
182
    bool operator<(const Entry& aOther) const
183
0
    {
184
0
      if (mDepth == aOther.mDepth) {
185
0
        return mFrame < aOther.mFrame;
186
0
      }
187
0
      return mDepth > aOther.mDepth; /* reverse, want "min" to be deepest */
188
0
    }
189
190
    static int compare(const Entry& aOne, const Entry& aTwo)
191
0
    {
192
0
      if (aOne == aTwo) {
193
0
        return 0;
194
0
      } else if (aOne < aTwo) {
195
0
        return -1;
196
0
      } else {
197
0
        return 1;
198
0
      }
199
0
    }
200
201
    nsIFrame* mFrame;
202
    /* Depth in the frame tree */
203
    uint32_t mDepth;
204
    ChangeKind mChangeKind;
205
  };
206
207
  /* A list of frames to process, sorted by their depth in the frame tree */
208
  SplayTree<Entry, Entry> mEntryList;
209
210
  /* Don't update overflow of this frame or its ancestors. */
211
  const nsIFrame* mSubtreeRoot;
212
};
213
214
} // namespace mozilla
215
216
#endif