Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/painting/DisplayListChecker.cpp
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
#include "DisplayListChecker.h"
8
9
#include "gfxPrefs.h"
10
#include "nsDisplayList.h"
11
12
namespace mozilla {
13
14
class DisplayItemBlueprint;
15
16
// Stack node used during tree visits, to store the path to a display item.
17
struct DisplayItemBlueprintStack
18
{
19
  const DisplayItemBlueprintStack* mPrevious;
20
  const DisplayItemBlueprint* mItem;
21
  // Output stack to aSs, with format "name#index > ... > name#index".
22
  // Returns true if anything was output, false if empty.
23
  bool Output(std::stringstream& aSs) const;
24
};
25
26
// Object representing a list of display items (either the top of the tree, or
27
// an item's children), with just enough information to compare with another
28
// tree and output useful debugging information.
29
class DisplayListBlueprint
30
{
31
public:
32
  DisplayListBlueprint(nsDisplayList* aList, const char* aName)
33
    : DisplayListBlueprint(aList, 0, aName)
34
0
  {
35
0
  }
36
37
  DisplayListBlueprint(nsDisplayList* aList,
38
                       const char* aName,
39
                       unsigned& aIndex)
40
0
  {
41
0
    processChildren(aList, aName, aIndex);
42
0
  }
43
44
  // Find a display item with the given frame and per-frame key.
45
  // Returns empty string if not found.
46
  std::string Find(const nsIFrame* aFrame, uint32_t aPerFrameKey) const
47
0
  {
48
0
    const DisplayItemBlueprintStack stack{ nullptr, nullptr };
49
0
    return Find(aFrame, aPerFrameKey, stack);
50
0
  }
51
52
  std::string Find(const nsIFrame* aFrame,
53
                   uint32_t aPerFrameKey,
54
                   const DisplayItemBlueprintStack& aStack) const;
55
56
  // Compare this list with another one, output differences between the two
57
  // into aDiff.
58
  // Differences include: Display items from one tree for which a corresponding
59
  // item (same frame and per-frame key) cannot be found under corresponding
60
  // parent items.
61
  // Returns true if trees are similar, false if different.
62
  bool CompareList(const DisplayListBlueprint& aOther,
63
                   std::stringstream& aDiff) const
64
0
  {
65
0
    const DisplayItemBlueprintStack stack{ nullptr, nullptr };
66
0
    const bool ab = CompareList(*this, aOther, aOther, aDiff, stack, stack);
67
0
    const bool ba =
68
0
      aOther.CompareList(aOther, *this, *this, aDiff, stack, stack);
69
0
    return ab && ba;
70
0
  }
71
72
  bool CompareList(const DisplayListBlueprint& aRoot,
73
                   const DisplayListBlueprint& aOther,
74
                   const DisplayListBlueprint& aOtherRoot,
75
                   std::stringstream& aDiff,
76
                   const DisplayItemBlueprintStack& aStack,
77
                   const DisplayItemBlueprintStack& aStackOther) const;
78
79
  // Output this tree to aSs.
80
0
  void Dump(std::stringstream& aSs) const { Dump(aSs, 0); }
81
82
  void Dump(std::stringstream& aSs, unsigned aDepth) const;
83
84
private:
85
  // Only used by first constructor, to call the 2nd constructor with an index
86
  // variable on the stack.
87
  DisplayListBlueprint(nsDisplayList* aList, unsigned aIndex, const char* aName)
88
    : DisplayListBlueprint(aList, aName, aIndex)
89
0
  {
90
0
  }
91
92
  void processChildren(nsDisplayList* aList,
93
                       const char* aName,
94
                       unsigned& aIndex);
95
96
  std::vector<DisplayItemBlueprint> mItems;
97
  const bool mVerifyOrder = gfxPrefs::LayoutVerifyRetainDisplayListOrder();
98
};
99
100
// Object representing one display item, with just enough information to
101
// compare with another item and output useful debugging information.
102
class DisplayItemBlueprint
103
{
104
public:
105
  DisplayItemBlueprint(nsDisplayItem& aItem,
106
                       const char* aName,
107
                       unsigned& aIndex)
108
    : mListName(aName)
109
    , mIndex(++aIndex)
110
    , mIndexString(WriteIndex(aName, aIndex))
111
    , mIndexStringFW(WriteIndexFW(aName, aIndex))
112
    , mDisplayItemPointer(WriteDisplayItemPointer(aItem))
113
    , mDescription(WriteDescription(aName, aIndex, aItem))
114
    , mFrame(aItem.HasDeletedFrame() ? nullptr : aItem.Frame())
115
    , mPerFrameKey(aItem.GetPerFrameKey())
116
    , mChildren(aItem.GetChildren(), aName, aIndex)
117
0
  {
118
0
  }
119
120
  // Compare this item with another one, based on frame and per-frame key.
121
  // Not recursive! I.e., children are not examined.
122
  bool CompareItem(const DisplayItemBlueprint& aOther,
123
                   std::stringstream& aDiff) const
124
0
  {
125
0
    return mFrame == aOther.mFrame && mPerFrameKey == aOther.mPerFrameKey;
126
0
  }
127
128
  void Dump(std::stringstream& aSs, unsigned aDepth) const;
129
130
  const char* mListName;
131
  const unsigned mIndex;
132
  const std::string mIndexString;
133
  const std::string mIndexStringFW;
134
  const std::string mDisplayItemPointer;
135
  const std::string mDescription;
136
137
  // For pointer comparison only, do not dereference!
138
  const nsIFrame* const mFrame;
139
  const uint32_t mPerFrameKey;
140
141
  const DisplayListBlueprint mChildren;
142
143
private:
144
  static std::string WriteIndex(const char* aName, unsigned aIndex)
145
0
  {
146
0
    return nsPrintfCString("%s#%u", aName, aIndex).get();
147
0
  }
148
149
  static std::string WriteIndexFW(const char* aName, unsigned aIndex)
150
0
  {
151
0
    return nsPrintfCString("%s#%4u", aName, aIndex).get();
152
0
  }
153
154
  static std::string WriteDisplayItemPointer(nsDisplayItem& aItem)
155
0
  {
156
0
    return nsPrintfCString("0x%p", &aItem).get();
157
0
  }
158
159
  static std::string WriteDescription(const char* aName,
160
                                      unsigned aIndex,
161
                                      nsDisplayItem& aItem)
162
0
  {
163
0
    if (aItem.HasDeletedFrame()) {
164
0
      return nsPrintfCString(
165
0
               "%s %s#%u 0x%p f=0x0", aItem.Name(), aName, aIndex, &aItem)
166
0
        .get();
167
0
    }
168
0
169
0
    const nsIFrame* f = aItem.Frame();
170
0
    nsAutoString contentData;
171
#ifdef DEBUG_FRAME_DUMP
172
    f->GetFrameName(contentData);
173
#endif
174
    nsIContent* content = f->GetContent();
175
0
    if (content) {
176
0
      nsString tmp;
177
0
      if (content->GetID()) {
178
0
        content->GetID()->ToString(tmp);
179
0
        contentData.AppendLiteral(" id:");
180
0
        contentData.Append(tmp);
181
0
      }
182
0
      const nsAttrValue* classes =
183
0
        content->IsElement() ? content->AsElement()->GetClasses() : nullptr;
184
0
      if (classes) {
185
0
        classes->ToString(tmp);
186
0
        contentData.AppendLiteral(" class:");
187
0
        contentData.Append(tmp);
188
0
      }
189
0
    }
190
0
    return nsPrintfCString("%s %s#%u p=0x%p f=0x%p(%s) key=%" PRIu32,
191
0
                           aItem.Name(),
192
0
                           aName,
193
0
                           aIndex,
194
0
                           &aItem,
195
0
                           f,
196
0
                           NS_ConvertUTF16toUTF8(contentData).get(),
197
0
                           aItem.GetPerFrameKey())
198
0
      .get();
199
0
  }
200
};
201
202
void
203
DisplayListBlueprint::processChildren(nsDisplayList* aList,
204
                                      const char* aName,
205
                                      unsigned& aIndex)
206
0
{
207
0
  if (!aList) {
208
0
    return;
209
0
  }
210
0
  const uint32_t n = aList->Count();
211
0
  if (n == 0) {
212
0
    return;
213
0
  }
214
0
  mItems.reserve(n);
215
0
  for (nsDisplayItem* item = aList->GetBottom(); item;
216
0
       item = item->GetAbove()) {
217
0
    mItems.emplace_back(*item, aName, aIndex);
218
0
  }
219
0
  MOZ_ASSERT(mItems.size() == n);
220
0
}
221
222
bool
223
DisplayItemBlueprintStack::Output(std::stringstream& aSs) const
224
0
{
225
0
  const bool output = mPrevious ? mPrevious->Output(aSs) : false;
226
0
  if (mItem) {
227
0
    if (output) {
228
0
      aSs << " > ";
229
0
    }
230
0
    aSs << mItem->mIndexString;
231
0
    return true;
232
0
  }
233
0
  return output;
234
0
}
235
236
std::string
237
DisplayListBlueprint::Find(const nsIFrame* aFrame,
238
                           uint32_t aPerFrameKey,
239
                           const DisplayItemBlueprintStack& aStack) const
240
0
{
241
0
  for (const DisplayItemBlueprint& item : mItems) {
242
0
    if (item.mFrame == aFrame && item.mPerFrameKey == aPerFrameKey) {
243
0
      std::stringstream ss;
244
0
      if (aStack.Output(ss)) {
245
0
        ss << " > ";
246
0
      }
247
0
      ss << item.mDescription;
248
0
      return ss.str();
249
0
    }
250
0
    const DisplayItemBlueprintStack stack = { &aStack, &item };
251
0
    std::string s = item.mChildren.Find(aFrame, aPerFrameKey, stack);
252
0
    if (!s.empty()) {
253
0
      return s;
254
0
    }
255
0
  }
256
0
  return "";
257
0
}
258
259
bool
260
DisplayListBlueprint::CompareList(
261
  const DisplayListBlueprint& aRoot,
262
  const DisplayListBlueprint& aOther,
263
  const DisplayListBlueprint& aOtherRoot,
264
  std::stringstream& aDiff,
265
  const DisplayItemBlueprintStack& aStack,
266
  const DisplayItemBlueprintStack& aStackOther) const
267
0
{
268
0
  bool same = true;
269
0
  unsigned previousFoundIndex = 0;
270
0
  const DisplayItemBlueprint* previousFoundItemBefore = nullptr;
271
0
  const DisplayItemBlueprint* previousFoundItemAfter = nullptr;
272
0
  for (const DisplayItemBlueprint& itemBefore : mItems) {
273
0
    bool found = false;
274
0
    unsigned foundIndex = 0;
275
0
    for (const DisplayItemBlueprint& itemAfter : aOther.mItems) {
276
0
      if (itemBefore.CompareItem(itemAfter, aDiff)) {
277
0
        found = true;
278
0
279
0
        if (mVerifyOrder) {
280
0
          if (foundIndex < previousFoundIndex) {
281
0
            same = false;
282
0
            aDiff << "\n";
283
0
            if (aStack.Output(aDiff)) {
284
0
              aDiff << " > ";
285
0
            }
286
0
            aDiff << itemBefore.mDescription;
287
0
            aDiff << "\n * Corresponding item in unexpected order: ";
288
0
            if (aStackOther.Output(aDiff)) {
289
0
              aDiff << " > ";
290
0
            }
291
0
            aDiff << itemAfter.mDescription;
292
0
            aDiff << "\n * Was expected after: ";
293
0
            if (aStackOther.Output(aDiff)) {
294
0
              aDiff << " > ";
295
0
            }
296
0
            MOZ_ASSERT(previousFoundItemAfter);
297
0
            aDiff << previousFoundItemAfter->mDescription;
298
0
            aDiff << "\n   which corresponds to: ";
299
0
            if (aStack.Output(aDiff)) {
300
0
              aDiff << " > ";
301
0
            }
302
0
            MOZ_ASSERT(previousFoundItemBefore);
303
0
            aDiff << previousFoundItemBefore->mDescription;
304
0
          }
305
0
          previousFoundIndex = foundIndex;
306
0
          previousFoundItemBefore = &itemBefore;
307
0
          previousFoundItemAfter = &itemAfter;
308
0
        }
309
0
310
0
        const DisplayItemBlueprintStack stack = { &aStack, &itemBefore };
311
0
        const DisplayItemBlueprintStack stackOther = { &aStackOther,
312
0
                                                       &itemAfter };
313
0
        if (!itemBefore.mChildren.CompareList(aRoot,
314
0
                                              itemAfter.mChildren,
315
0
                                              aOtherRoot,
316
0
                                              aDiff,
317
0
                                              stack,
318
0
                                              stackOther)) {
319
0
          same = false;
320
0
        }
321
0
        break;
322
0
      }
323
0
      ++foundIndex;
324
0
    }
325
0
    if (!found) {
326
0
      same = false;
327
0
      aDiff << "\n";
328
0
      if (aStack.Output(aDiff)) {
329
0
        aDiff << " > ";
330
0
      }
331
0
      aDiff << itemBefore.mDescription;
332
0
      aDiff << "\n * Cannot find corresponding item under ";
333
0
      if (!aStackOther.Output(aDiff)) {
334
0
        if (!aOtherRoot.mItems.empty()) {
335
0
          aDiff << aOtherRoot.mItems[0].mListName;
336
0
        } else {
337
0
          aDiff << "other root";
338
0
        }
339
0
      }
340
0
      std::string elsewhere =
341
0
        aOtherRoot.Find(itemBefore.mFrame, itemBefore.mPerFrameKey);
342
0
      if (!elsewhere.empty()) {
343
0
        aDiff << "\n * But found: " << elsewhere;
344
0
      }
345
0
    }
346
0
  }
347
0
  return same;
348
0
}
349
350
void
351
DisplayListBlueprint::Dump(std::stringstream& aSs, unsigned aDepth) const
352
0
{
353
0
  for (const DisplayItemBlueprint& item : mItems) {
354
0
    item.Dump(aSs, aDepth);
355
0
  }
356
0
}
357
358
void
359
DisplayItemBlueprint::Dump(std::stringstream& aSs, unsigned aDepth) const
360
0
{
361
0
  aSs << "\n" << mIndexStringFW << " ";
362
0
  for (unsigned i = 0; i < aDepth; ++i) {
363
0
    aSs << "  ";
364
0
  }
365
0
  aSs << mDescription;
366
0
  mChildren.Dump(aSs, aDepth + 1);
367
0
}
368
369
DisplayListChecker::DisplayListChecker()
370
  : mBlueprint(nullptr)
371
0
{
372
0
}
373
374
DisplayListChecker::DisplayListChecker(nsDisplayList* aList, const char* aName)
375
  : mBlueprint(MakeUnique<DisplayListBlueprint>(aList, aName))
376
0
{
377
0
}
378
379
0
DisplayListChecker::~DisplayListChecker() = default;
380
381
void
382
DisplayListChecker::Set(nsDisplayList* aList, const char* aName)
383
0
{
384
0
  mBlueprint = MakeUnique<DisplayListBlueprint>(aList, aName);
385
0
}
386
387
// Compare this list with another one, output differences between the two
388
// into aDiff.
389
// Differences include: Display items from one tree for which a corresponding
390
// item (same frame and per-frame key) cannot be found under corresponding
391
// parent items.
392
// Returns true if trees are similar, false if different.
393
bool
394
DisplayListChecker::CompareList(const DisplayListChecker& aOther,
395
                                std::stringstream& aDiff) const
396
0
{
397
0
  MOZ_ASSERT(mBlueprint);
398
0
  MOZ_ASSERT(aOther.mBlueprint);
399
0
  return mBlueprint->CompareList(*aOther.mBlueprint, aDiff);
400
0
}
401
402
void
403
DisplayListChecker::Dump(std::stringstream& aSs) const
404
0
{
405
0
  MOZ_ASSERT(mBlueprint);
406
0
  mBlueprint->Dump(aSs);
407
0
}
408
409
} // namespace mozilla