Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/LayerTreeInvalidation.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "LayerTreeInvalidation.h"
8
9
#include <stdint.h>                     // for uint32_t
10
#include "ImageContainer.h"             // for ImageContainer
11
#include "ImageLayers.h"                // for ImageLayer, etc
12
#include "Layers.h"                     // for Layer, ContainerLayer, etc
13
#include "Units.h"                      // for ParentLayerIntRect
14
#include "gfxRect.h"                    // for gfxRect
15
#include "gfxUtils.h"                   // for gfxUtils
16
#include "mozilla/ArrayUtils.h"         // for ArrayEqual
17
#include "mozilla/gfx/BaseSize.h"       // for BaseSize
18
#include "mozilla/gfx/Point.h"          // for IntSize
19
#include "mozilla/mozalloc.h"           // for operator new, etc
20
#include "nsDataHashtable.h"            // for nsDataHashtable
21
#include "nsDebug.h"                    // for NS_ASSERTION
22
#include "nsHashKeys.h"                 // for nsPtrHashKey
23
#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
24
#include "nsRect.h"                     // for IntRect
25
#include "nsTArray.h"                   // for AutoTArray, nsTArray_Impl
26
#include "mozilla/Poison.h"
27
#include "mozilla/layers/ImageHost.h"
28
#include "mozilla/layers/LayerManagerComposite.h"
29
#include "TreeTraversal.h"              // for ForEachNode
30
#include "LayersLogging.h"
31
32
// LayerTreeInvalidation debugging
33
#define LTI_DEBUG 0
34
35
#if LTI_DEBUG
36
#  define LTI_DEEPER(aPrefix) nsPrintfCString("%s  ", aPrefix).get()
37
#  define LTI_DUMP(rgn, label) if (!(rgn).IsEmpty()) printf_stderr("%s%p: " label " portion is %s\n", aPrefix, mLayer.get(), Stringify(rgn).c_str());
38
#  define LTI_LOG(...) printf_stderr(__VA_ARGS__)
39
#else
40
0
#  define LTI_DEEPER(aPrefix) nullptr
41
#  define LTI_DUMP(rgn, label)
42
#  define LTI_LOG(...)
43
#endif
44
45
using namespace mozilla::gfx;
46
47
namespace mozilla {
48
namespace layers {
49
50
struct LayerPropertiesBase;
51
UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false);
52
53
/**
54
 * Get accumulated transform of from the context creating layer to the
55
 * given layer.
56
 */
57
static Matrix4x4
58
0
GetTransformIn3DContext(Layer* aLayer) {
59
0
  Matrix4x4 transform = aLayer->GetLocalTransform();
60
0
  for (Layer* layer = aLayer->GetParent();
61
0
       layer && layer->Extend3DContext();
62
0
       layer = layer->GetParent()) {
63
0
    transform = transform * layer->GetLocalTransform();
64
0
  }
65
0
  return transform;
66
0
}
67
68
/**
69
 * Get a transform for the given layer depending on extending 3D
70
 * context.
71
 *
72
 * @return local transform for layers not participating 3D rendering
73
 * context, or the accmulated transform in the context for else.
74
 */
75
static Matrix4x4Flagged
76
0
GetTransformForInvalidation(Layer* aLayer) {
77
0
  return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext() ?
78
0
          aLayer->GetLocalTransform() : GetTransformIn3DContext(aLayer));
79
0
}
80
81
static IntRect
82
TransformRect(const IntRect& aRect, const Matrix4x4Flagged& aTransform)
83
0
{
84
0
  if (aRect.IsEmpty()) {
85
0
    return IntRect();
86
0
  }
87
0
88
0
  Rect rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
89
0
  rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
90
0
  rect.RoundOut();
91
0
92
0
  IntRect intRect;
93
0
  if (!rect.ToIntRect(&intRect)) {
94
0
    intRect = IntRect::MaxIntRect();
95
0
  }
96
0
97
0
  return intRect;
98
0
}
99
100
static void
101
AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const Matrix4x4Flagged& aTransform)
102
0
{
103
0
  for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
104
0
    aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
105
0
  }
106
0
  aDest.SimplifyOutward(20);
107
0
}
108
109
static void
110
AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource)
111
0
{
112
0
  aDest.Or(aDest, aSource);
113
0
  aDest.SimplifyOutward(20);
114
0
}
115
116
/**
117
 * Walks over this layer, and all descendant layers.
118
 * If any of these are a ContainerLayer that reports invalidations to a PresShell,
119
 * then report that the entire bounds have changed.
120
 */
121
static void
122
NotifySubdocumentInvalidation(Layer* aLayer, NotifySubDocInvalidationFunc aCallback)
123
0
{
124
0
  ForEachNode<ForwardIterator>(
125
0
      aLayer,
126
0
      [aCallback] (Layer* layer)
127
0
      {
128
0
        layer->ClearInvalidRegion();
129
0
        if (layer->GetMaskLayer()) {
130
0
          NotifySubdocumentInvalidation(layer->GetMaskLayer(), aCallback);
131
0
        }
132
0
        for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
133
0
          Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
134
0
          NotifySubdocumentInvalidation(maskLayer, aCallback);
135
0
        }
136
0
      },
137
0
      [aCallback] (Layer* layer)
138
0
      {
139
0
        ContainerLayer* container = layer->AsContainerLayer();
140
0
        if (container) {
141
0
          nsIntRegion region = container->GetLocalVisibleRegion().ToUnknownRegion();
142
0
          aCallback(container, &region);
143
0
        }
144
0
      });
145
0
}
146
147
struct LayerPropertiesBase : public LayerProperties
148
{
149
  explicit LayerPropertiesBase(Layer* aLayer)
150
    : mLayer(aLayer)
151
    , mMaskLayer(nullptr)
152
    , mVisibleRegion(mLayer->GetLocalVisibleRegion().ToUnknownRegion())
153
    , mPostXScale(aLayer->GetPostXScale())
154
    , mPostYScale(aLayer->GetPostYScale())
155
    , mOpacity(aLayer->GetLocalOpacity())
156
    , mUseClipRect(!!aLayer->GetLocalClipRect())
157
0
  {
158
0
    MOZ_COUNT_CTOR(LayerPropertiesBase);
159
0
    if (aLayer->GetMaskLayer()) {
160
0
      mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
161
0
    }
162
0
    for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
163
0
      Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
164
0
      mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
165
0
    }
166
0
    if (mUseClipRect) {
167
0
      mClipRect = *aLayer->GetLocalClipRect();
168
0
    }
169
0
    mTransform = GetTransformForInvalidation(aLayer);
170
0
  }
171
  LayerPropertiesBase()
172
    : mLayer(nullptr)
173
    , mMaskLayer(nullptr)
174
    , mPostXScale(0.0)
175
    , mPostYScale(0.0)
176
    , mOpacity(0.0)
177
    , mUseClipRect(false)
178
0
  {
179
0
    MOZ_COUNT_CTOR(LayerPropertiesBase);
180
0
  }
181
  ~LayerPropertiesBase() override
182
0
  {
183
0
    MOZ_COUNT_DTOR(LayerPropertiesBase);
184
0
  }
185
186
protected:
187
  LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
188
  LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
189
190
public:
191
  bool ComputeDifferences(Layer* aRoot,
192
                          nsIntRegion& aOutRegion,
193
                          NotifySubDocInvalidationFunc aCallback) override;
194
195
  void MoveBy(const IntPoint& aOffset) override;
196
197
  bool ComputeChange(const char* aPrefix,
198
                     nsIntRegion& aOutRegion,
199
                     NotifySubDocInvalidationFunc aCallback)
200
0
  {
201
0
    // Bug 1251615: This canary is sometimes hit. We're still not sure why.
202
0
    mCanary.Check();
203
0
    bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
204
0
                             mLayer->GetPostXScale() != mPostXScale ||
205
0
                             mLayer->GetPostYScale() != mPostYScale;
206
0
    const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect();
207
0
    nsIntRegion result;
208
0
209
0
    bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
210
0
    if (!ancestorMaskChanged) {
211
0
      for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
212
0
        if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) {
213
0
          ancestorMaskChanged = true;
214
0
          break;
215
0
        }
216
0
      }
217
0
    }
218
0
219
0
    // Note that we don't bailout early in general since this function
220
0
    // clears some persistent state at the end. Instead we set an overflow
221
0
    // flag and check it right before returning.
222
0
    bool areaOverflowed = false;
223
0
224
0
    Layer* otherMask = mLayer->GetMaskLayer();
225
0
    if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
226
0
        ancestorMaskChanged ||
227
0
        (mUseClipRect != !!otherClip) ||
228
0
        mLayer->GetLocalOpacity() != mOpacity ||
229
0
        transformChanged)
230
0
    {
231
0
      Maybe<IntRect> oldBounds = OldTransformedBounds();
232
0
      Maybe<IntRect> newBounds = NewTransformedBounds();
233
0
      if (oldBounds && newBounds) {
234
0
        LTI_DUMP(oldBounds.value(), "oldtransform");
235
0
        LTI_DUMP(newBounds.value(), "newtransform");
236
0
        result = oldBounds.value();
237
0
        AddRegion(result, newBounds.value());
238
0
      } else {
239
0
        areaOverflowed = true;
240
0
      }
241
0
242
0
      // We can't bail out early because we might need to update some internal
243
0
      // layer state.
244
0
    }
245
0
246
0
    nsIntRegion internal;
247
0
    if (!ComputeChangeInternal(aPrefix, internal, aCallback)) {
248
0
      areaOverflowed = true;
249
0
    }
250
0
251
0
    LTI_DUMP(internal, "internal");
252
0
    AddRegion(result, internal);
253
0
    LTI_DUMP(mLayer->GetInvalidRegion().GetRegion(), "invalid");
254
0
    AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform);
255
0
256
0
    if (mMaskLayer && otherMask) {
257
0
      nsIntRegion mask;
258
0
      if (!mMaskLayer->ComputeChange(aPrefix, mask, aCallback)) {
259
0
        areaOverflowed = true;
260
0
      }
261
0
      LTI_DUMP(mask, "mask");
262
0
      AddTransformedRegion(result, mask, mTransform);
263
0
    }
264
0
265
0
    for (size_t i = 0;
266
0
         i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
267
0
         i++)
268
0
    {
269
0
      nsIntRegion mask;
270
0
      if (!mAncestorMaskLayers[i]->ComputeChange(aPrefix, mask, aCallback)) {
271
0
        areaOverflowed = true;
272
0
      }
273
0
      LTI_DUMP(mask, "ancestormask");
274
0
      AddTransformedRegion(result, mask, mTransform);
275
0
    }
276
0
277
0
    if (mUseClipRect && otherClip) {
278
0
      if (!mClipRect.IsEqualInterior(*otherClip)) {
279
0
        nsIntRegion tmp;
280
0
        tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
281
0
        LTI_DUMP(tmp, "clip");
282
0
        AddRegion(result, tmp);
283
0
      }
284
0
    }
285
0
286
0
    mLayer->ClearInvalidRegion();
287
0
288
0
    if (areaOverflowed) {
289
0
      return false;
290
0
    }
291
0
292
0
    aOutRegion = std::move(result);
293
0
    return true;
294
0
  }
295
296
  void CheckCanary()
297
0
  {
298
0
    mCanary.Check();
299
0
    mLayer->CheckCanary();
300
0
  }
301
302
0
  IntRect NewTransformedBoundsForLeaf() {
303
0
    return TransformRect(mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
304
0
                         GetTransformForInvalidation(mLayer));
305
0
  }
306
307
0
  IntRect OldTransformedBoundsForLeaf() {
308
0
    return TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform);
309
0
  }
310
311
  virtual Maybe<IntRect> NewTransformedBounds()
312
0
  {
313
0
    return Some(TransformRect(mLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
314
0
                              GetTransformForInvalidation(mLayer)));
315
0
  }
316
317
  virtual Maybe<IntRect> OldTransformedBounds()
318
0
  {
319
0
    return Some(TransformRect(mVisibleRegion.GetBounds().ToUnknownRect(), mTransform));
320
0
  }
321
322
  virtual bool ComputeChangeInternal(const char* aPrefix,
323
                                     nsIntRegion& aOutRegion,
324
                                     NotifySubDocInvalidationFunc aCallback)
325
0
  {
326
0
    if (mLayer->AsHostLayer() && !mLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
327
0
      IntRect result = NewTransformedBoundsForLeaf();
328
0
      result = result.Union(OldTransformedBoundsForLeaf());
329
0
      aOutRegion = result;
330
0
    }
331
0
    return true;
332
0
  }
333
334
  RefPtr<Layer> mLayer;
335
  UniquePtr<LayerPropertiesBase> mMaskLayer;
336
  nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
337
  nsIntRegion mVisibleRegion;
338
  Matrix4x4Flagged mTransform;
339
  float mPostXScale;
340
  float mPostYScale;
341
  float mOpacity;
342
  ParentLayerIntRect mClipRect;
343
  bool mUseClipRect;
344
  mozilla::CorruptionCanary mCanary;
345
};
346
347
struct ContainerLayerProperties : public LayerPropertiesBase
348
{
349
  explicit ContainerLayerProperties(ContainerLayer* aLayer)
350
    : LayerPropertiesBase(aLayer)
351
    , mPreXScale(aLayer->GetPreXScale())
352
    , mPreYScale(aLayer->GetPreYScale())
353
0
  {
354
0
    for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
355
0
      child->CheckCanary();
356
0
      mChildren.AppendElement(CloneLayerTreePropertiesInternal(child));
357
0
    }
358
0
  }
359
360
protected:
361
  ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
362
  ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
363
364
public:
365
  bool ComputeChangeInternal(const char *aPrefix,
366
                             nsIntRegion& aOutRegion,
367
                             NotifySubDocInvalidationFunc aCallback) override
368
0
  {
369
0
    // Make sure we got our virtual call right
370
0
    mSubtypeCanary.Check();
371
0
    ContainerLayer* container = mLayer->AsContainerLayer();
372
0
    nsIntRegion invalidOfLayer; // Invalid regions of this layer.
373
0
    nsIntRegion result;         // Invliad regions for children only.
374
0
375
0
    container->CheckCanary();
376
0
377
0
    bool childrenChanged = false;
378
0
    bool invalidateWholeLayer = false;
379
0
    bool areaOverflowed = false;
380
0
    if (mPreXScale != container->GetPreXScale() ||
381
0
        mPreYScale != container->GetPreYScale())
382
0
    {
383
0
      Maybe<IntRect> oldBounds = OldTransformedBounds();
384
0
      Maybe<IntRect> newBounds = NewTransformedBounds();
385
0
      if (oldBounds && newBounds) {
386
0
        invalidOfLayer = oldBounds.value();
387
0
        AddRegion(invalidOfLayer, newBounds.value());
388
0
      } else {
389
0
        areaOverflowed = true;
390
0
      }
391
0
      childrenChanged = true;
392
0
      invalidateWholeLayer = true;
393
0
394
0
      // Can't bail out early, we need to update the child container layers
395
0
    }
396
0
397
0
    // A low frame rate is especially visible to users when scrolling, so we
398
0
    // particularly want to avoid unnecessary invalidation at that time. For us
399
0
    // here, that means avoiding unnecessary invalidation of child items when
400
0
    // other children are added to or removed from our container layer, since
401
0
    // that may be caused by children being scrolled in or out of view. We are
402
0
    // less concerned with children changing order.
403
0
    // TODO: Consider how we could avoid unnecessary invalidation when children
404
0
    // change order, and whether the overhead would be worth it.
405
0
406
0
    nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length());
407
0
    for (uint32_t i = 0; i < mChildren.Length(); ++i) {
408
0
      mChildren[i]->CheckCanary();
409
0
      oldIndexMap.Put(mChildren[i]->mLayer, i);
410
0
    }
411
0
412
0
    uint32_t i = 0; // cursor into the old child list mChildren
413
0
    for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
414
0
      bool invalidateChildsCurrentArea = false;
415
0
      if (i < mChildren.Length()) {
416
0
        uint32_t childsOldIndex;
417
0
        if (oldIndexMap.Get(child, &childsOldIndex)) {
418
0
          if (childsOldIndex >= i) {
419
0
            // Invalidate the old areas of layers that used to be between the
420
0
            // current |child| and the previous |child| that was also in the
421
0
            // old list mChildren (if any of those children have been reordered
422
0
            // rather than removed, we will invalidate their new area when we
423
0
            // encounter them in the new list):
424
0
            for (uint32_t j = i; j < childsOldIndex; ++j) {
425
0
              if (Maybe<IntRect> bounds = mChildren[j]->OldTransformedBounds()) {
426
0
                LTI_DUMP(bounds.value(), "reordered child");
427
0
                AddRegion(result, bounds.value());
428
0
              } else {
429
0
                areaOverflowed = true;
430
0
              }
431
0
              childrenChanged |= true;
432
0
            }
433
0
            if (childsOldIndex >= mChildren.Length()) {
434
0
              MOZ_CRASH("Out of bounds");
435
0
            }
436
0
            // Invalidate any regions of the child that have changed:
437
0
            nsIntRegion region;
438
0
            if (!mChildren[childsOldIndex]->ComputeChange(LTI_DEEPER(aPrefix), region, aCallback)) {
439
0
              areaOverflowed = true;
440
0
            }
441
0
            i = childsOldIndex + 1;
442
0
            if (!region.IsEmpty()) {
443
0
              LTI_LOG("%s%p: child %p produced %s\n", aPrefix, mLayer.get(),
444
0
                mChildren[childsOldIndex]->mLayer.get(), Stringify(region).c_str());
445
0
              AddRegion(result, region);
446
0
              childrenChanged |= true;
447
0
            }
448
0
          } else {
449
0
            // We've already seen this child in mChildren (which means it must
450
0
            // have been reordered) and invalidated its old area. We need to
451
0
            // invalidate its new area too:
452
0
            invalidateChildsCurrentArea = true;
453
0
          }
454
0
        } else {
455
0
          // |child| is new
456
0
          invalidateChildsCurrentArea = true;
457
0
        }
458
0
      } else {
459
0
        // |child| is new, or was reordered to a higher index
460
0
        invalidateChildsCurrentArea = true;
461
0
      }
462
0
      if (invalidateChildsCurrentArea) {
463
0
        LTI_DUMP(child->GetLocalVisibleRegion().ToUnknownRegion(), "invalidateChildsCurrentArea");
464
0
        AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(),
465
0
                             GetTransformForInvalidation(child));
466
0
        if (aCallback) {
467
0
          NotifySubdocumentInvalidation(child, aCallback);
468
0
        } else {
469
0
          ClearInvalidations(child);
470
0
        }
471
0
      }
472
0
      childrenChanged |= invalidateChildsCurrentArea;
473
0
    }
474
0
475
0
    // Process remaining removed children.
476
0
    while (i < mChildren.Length()) {
477
0
      childrenChanged |= true;
478
0
      if (Maybe<IntRect> bounds = mChildren[i]->OldTransformedBounds()) {
479
0
        LTI_DUMP(bounds.value(), "removed child");
480
0
        AddRegion(result, bounds.value());
481
0
      } else {
482
0
        areaOverflowed = true;
483
0
      }
484
0
      i++;
485
0
    }
486
0
487
0
    if (aCallback) {
488
0
      aCallback(container, areaOverflowed ? nullptr : &result);
489
0
    }
490
0
491
0
    if (childrenChanged || areaOverflowed) {
492
0
      container->SetChildrenChanged(true);
493
0
    }
494
0
495
0
    if (container->UseIntermediateSurface()) {
496
0
      Maybe<IntRect> bounds;
497
0
      if (!invalidateWholeLayer && !areaOverflowed) {
498
0
        bounds = Some(result.GetBounds());
499
0
500
0
        // Process changes in the visible region.
501
0
        IntRegion newVisible = mLayer->GetLocalVisibleRegion().ToUnknownRegion();
502
0
        if (!newVisible.IsEqual(mVisibleRegion)) {
503
0
          newVisible.XorWith(mVisibleRegion);
504
0
          bounds = bounds->SafeUnion(newVisible.GetBounds());
505
0
        }
506
0
      }
507
0
      container->SetInvalidCompositeRect(bounds ? bounds.ptr() : nullptr);
508
0
    }
509
0
510
0
    // Safe to bail out early now, persistent state has been set.
511
0
    if (areaOverflowed) {
512
0
      return false;
513
0
    }
514
0
515
0
    if (!mLayer->Extend3DContext()) {
516
0
      // |result| contains invalid regions only of children.
517
0
      result.Transform(GetTransformForInvalidation(mLayer).GetMatrix());
518
0
    }
519
0
    // else, effective transforms have applied on children.
520
0
521
0
    LTI_DUMP(invalidOfLayer, "invalidOfLayer");
522
0
    result.OrWith(invalidOfLayer);
523
0
524
0
    aOutRegion = std::move(result);
525
0
    return true;
526
0
  }
527
528
  Maybe<IntRect> NewTransformedBounds() override
529
0
  {
530
0
    if (mLayer->Extend3DContext()) {
531
0
      IntRect result;
532
0
      for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
533
0
        Maybe<IntRect> childBounds = child->NewTransformedBounds();
534
0
        if (!childBounds) {
535
0
          return Nothing();
536
0
        }
537
0
        Maybe<IntRect> combined = result.SafeUnion(childBounds.value());
538
0
        if (!combined) {
539
0
          LTI_LOG("overflowed bounds of container %p accumulating child %p\n", this, child->mLayer.get());
540
0
          return Nothing();
541
0
        }
542
0
        result = combined.value();
543
0
      }
544
0
      return Some(result);
545
0
    }
546
0
547
0
    return LayerPropertiesBase::NewTransformedBounds();
548
0
  }
549
550
  Maybe<IntRect> OldTransformedBounds() override
551
0
  {
552
0
    if (mLayer->Extend3DContext()) {
553
0
      IntRect result;
554
0
      for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
555
0
        Maybe<IntRect> childBounds = child->OldTransformedBounds();
556
0
        if (!childBounds) {
557
0
          return Nothing();
558
0
        }
559
0
        Maybe<IntRect> combined = result.SafeUnion(childBounds.value());
560
0
        if (!combined) {
561
0
          LTI_LOG("overflowed bounds of container %p accumulating child %p\n", this, child->mLayer.get());
562
0
          return Nothing();
563
0
        }
564
0
        result = combined.value();
565
0
      }
566
0
      return Some(result);
567
0
    }
568
0
    return LayerPropertiesBase::OldTransformedBounds();
569
0
  }
570
571
  // The old list of children:
572
  mozilla::CorruptionCanary mSubtypeCanary;
573
  nsTArray<UniquePtr<LayerPropertiesBase>> mChildren;
574
  float mPreXScale;
575
  float mPreYScale;
576
};
577
578
struct ColorLayerProperties : public LayerPropertiesBase
579
{
580
  explicit ColorLayerProperties(ColorLayer *aLayer)
581
    : LayerPropertiesBase(aLayer)
582
    , mColor(aLayer->GetColor())
583
    , mBounds(aLayer->GetBounds())
584
0
  { }
585
586
protected:
587
  ColorLayerProperties(const ColorLayerProperties& a) = delete;
588
  ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
589
590
public:
591
  bool ComputeChangeInternal(const char* aPrefix,
592
                             nsIntRegion& aOutRegion,
593
                             NotifySubDocInvalidationFunc aCallback) override
594
0
  {
595
0
    ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
596
0
597
0
    if (mColor != color->GetColor()) {
598
0
      LTI_DUMP(NewTransformedBoundsForLeaf(), "color");
599
0
      aOutRegion = NewTransformedBoundsForLeaf();
600
0
      return true;
601
0
    }
602
0
603
0
    nsIntRegion boundsDiff;
604
0
    boundsDiff.Xor(mBounds, color->GetBounds());
605
0
    LTI_DUMP(boundsDiff, "colorbounds");
606
0
607
0
    AddTransformedRegion(aOutRegion, boundsDiff, mTransform);
608
0
    return true;
609
0
  }
610
611
  Color mColor;
612
  IntRect mBounds;
613
};
614
615
static ImageHost* GetImageHost(Layer* aLayer)
616
0
{
617
0
  HostLayer* compositor = aLayer->AsHostLayer();
618
0
  if (compositor) {
619
0
    return static_cast<ImageHost*>(compositor->GetCompositableHost());
620
0
  }
621
0
  return nullptr;
622
0
}
623
624
struct ImageLayerProperties : public LayerPropertiesBase
625
{
626
  explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
627
    : LayerPropertiesBase(aImage)
628
    , mContainer(aImage->GetContainer())
629
    , mImageHost(GetImageHost(aImage))
630
    , mSamplingFilter(aImage->GetSamplingFilter())
631
    , mScaleToSize(aImage->GetScaleToSize())
632
    , mScaleMode(aImage->GetScaleMode())
633
    , mLastProducerID(-1)
634
    , mLastFrameID(-1)
635
    , mIsMask(aIsMask)
636
0
  {
637
0
    if (mImageHost) {
638
0
      mLastProducerID = mImageHost->GetLastProducerID();
639
0
      mLastFrameID = mImageHost->GetLastFrameID();
640
0
    }
641
0
  }
642
643
  bool ComputeChangeInternal(const char* aPrefix,
644
                             nsIntRegion& aOutRegion,
645
                             NotifySubDocInvalidationFunc aCallback) override
646
0
  {
647
0
    ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
648
0
649
0
    if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
650
0
      IntRect result = NewTransformedBoundsForLeaf();
651
0
      result = result.Union(OldTransformedBoundsForLeaf());
652
0
      aOutRegion = result;
653
0
      return true;
654
0
    }
655
0
656
0
    ImageContainer* container = imageLayer->GetContainer();
657
0
    ImageHost* host = GetImageHost(imageLayer);
658
0
    if (mContainer != container ||
659
0
        mSamplingFilter != imageLayer->GetSamplingFilter() ||
660
0
        mScaleToSize != imageLayer->GetScaleToSize() ||
661
0
        mScaleMode != imageLayer->GetScaleMode() ||
662
0
        host != mImageHost ||
663
0
        (host && host->GetProducerID() != mLastProducerID) ||
664
0
        (host && host->GetFrameID() != mLastFrameID)) {
665
0
666
0
      if (mIsMask) {
667
0
        // Mask layers have an empty visible region, so we have to
668
0
        // use the image size instead.
669
0
        IntSize size;
670
0
        if (container) {
671
0
          size = container->GetCurrentSize();
672
0
        }
673
0
        if (host) {
674
0
          size = host->GetImageSize();
675
0
        }
676
0
        IntRect rect(0, 0, size.width, size.height);
677
0
        LTI_DUMP(rect, "mask");
678
0
        aOutRegion = TransformRect(rect, GetTransformForInvalidation(mLayer));
679
0
        return true;
680
0
      }
681
0
      LTI_DUMP(NewTransformedBoundsForLeaf(), "bounds");
682
0
      aOutRegion = NewTransformedBoundsForLeaf();
683
0
      return true;
684
0
    }
685
0
686
0
    return true;
687
0
  }
688
689
  RefPtr<ImageContainer> mContainer;
690
  RefPtr<ImageHost> mImageHost;
691
  SamplingFilter mSamplingFilter;
692
  gfx::IntSize mScaleToSize;
693
  ScaleMode mScaleMode;
694
  int32_t mLastProducerID;
695
  int32_t mLastFrameID;
696
  bool mIsMask;
697
};
698
699
struct CanvasLayerProperties : public LayerPropertiesBase
700
{
701
  explicit CanvasLayerProperties(CanvasLayer* aCanvas)
702
    : LayerPropertiesBase(aCanvas)
703
    , mImageHost(GetImageHost(aCanvas))
704
0
  {
705
0
    mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
706
0
  }
707
708
  bool ComputeChangeInternal(const char* aPrefix,
709
                             nsIntRegion& aOutRegion,
710
                             NotifySubDocInvalidationFunc aCallback) override
711
0
  {
712
0
    CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
713
0
714
0
    ImageHost* host = GetImageHost(canvasLayer);
715
0
    if (host && host->GetFrameID() != mFrameID) {
716
0
      LTI_DUMP(NewTransformedBoundsForLeaf(), "frameId");
717
0
      aOutRegion = NewTransformedBoundsForLeaf();
718
0
      return true;
719
0
    }
720
0
721
0
    return true;
722
0
  }
723
724
  RefPtr<ImageHost> mImageHost;
725
  int32_t mFrameID;
726
};
727
728
UniquePtr<LayerPropertiesBase>
729
CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */)
730
0
{
731
0
  if (!aRoot) {
732
0
    return MakeUnique<LayerPropertiesBase>();
733
0
  }
734
0
735
0
  MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
736
0
737
0
  aRoot->CheckCanary();
738
0
739
0
  switch (aRoot->GetType()) {
740
0
    case Layer::TYPE_CONTAINER:
741
0
    case Layer::TYPE_REF:
742
0
      return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
743
0
    case Layer::TYPE_COLOR:
744
0
      return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
745
0
    case Layer::TYPE_IMAGE:
746
0
      return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
747
0
    case Layer::TYPE_CANVAS:
748
0
      return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
749
0
    case Layer::TYPE_DISPLAYITEM:
750
0
    case Layer::TYPE_READBACK:
751
0
    case Layer::TYPE_SHADOW:
752
0
    case Layer::TYPE_PAINTED:
753
0
      return MakeUnique<LayerPropertiesBase>(aRoot);
754
0
  }
755
0
756
0
  MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
757
0
  return MakeUnique<LayerPropertiesBase>(aRoot);
758
0
}
759
760
/* static */ UniquePtr<LayerProperties>
761
LayerProperties::CloneFrom(Layer* aRoot)
762
0
{
763
0
  return CloneLayerTreePropertiesInternal(aRoot);
764
0
}
765
766
/* static */ void
767
LayerProperties::ClearInvalidations(Layer *aLayer)
768
0
{
769
0
  ForEachNode<ForwardIterator>(
770
0
        aLayer,
771
0
        [] (Layer* layer)
772
0
        {
773
0
          layer->ClearInvalidRegion();
774
0
          if (layer->GetMaskLayer()) {
775
0
            ClearInvalidations(layer->GetMaskLayer());
776
0
          }
777
0
          for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
778
0
            ClearInvalidations(layer->GetAncestorMaskLayerAt(i));
779
0
          }
780
0
781
0
        }
782
0
      );
783
0
}
784
785
bool
786
LayerPropertiesBase::ComputeDifferences(Layer* aRoot, nsIntRegion& aOutRegion, NotifySubDocInvalidationFunc aCallback)
787
0
{
788
0
  NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
789
0
  if (mLayer != aRoot) {
790
0
    if (aCallback) {
791
0
      NotifySubdocumentInvalidation(aRoot, aCallback);
792
0
    } else {
793
0
      ClearInvalidations(aRoot);
794
0
    }
795
0
    IntRect bounds = TransformRect(
796
0
      aRoot->GetLocalVisibleRegion().GetBounds().ToUnknownRect(),
797
0
      aRoot->GetLocalTransform());
798
0
    Maybe<IntRect> oldBounds = OldTransformedBounds();
799
0
    if (!oldBounds) {
800
0
      return false;
801
0
    }
802
0
    Maybe<IntRect> result = bounds.SafeUnion(oldBounds.value());
803
0
    if (!result) {
804
0
      LTI_LOG("overflowed bounds computing the union of layers %p and %p\n", mLayer.get(), aRoot);
805
0
      return false;
806
0
    }
807
0
    aOutRegion = result.value();
808
0
    return true;
809
0
  }
810
0
  return ComputeChange("  ", aOutRegion, aCallback);
811
0
}
812
813
void
814
LayerPropertiesBase::MoveBy(const IntPoint& aOffset)
815
0
{
816
0
  mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
817
0
}
818
819
} // namespace layers
820
} // namespace mozilla