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