tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebRenderCommandBuilder.cpp (120677B)


      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 "WebRenderCommandBuilder.h"
      8 
      9 #include "mozilla/AutoRestore.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/EffectCompositor.h"
     12 #include "mozilla/ProfilerLabels.h"
     13 #include "mozilla/StaticPrefs_gfx.h"
     14 #include "mozilla/SVGGeometryFrame.h"
     15 #include "mozilla/SVGImageFrame.h"
     16 #include "mozilla/UniquePtr.h"
     17 #include "mozilla/gfx/2D.h"
     18 #include "mozilla/gfx/Logging.h"
     19 #include "mozilla/gfx/Types.h"
     20 #include "mozilla/image/WebRenderImageProvider.h"
     21 #include "mozilla/layers/AnimationHelper.h"
     22 #include "mozilla/layers/ClipManager.h"
     23 #include "mozilla/layers/ImageClient.h"
     24 #include "mozilla/layers/RenderRootStateManager.h"
     25 #include "mozilla/layers/WebRenderBridgeChild.h"
     26 #include "mozilla/layers/WebRenderLayerManager.h"
     27 #include "mozilla/layers/IpcResourceUpdateQueue.h"
     28 #include "mozilla/layers/SharedSurfacesChild.h"
     29 #include "mozilla/layers/SourceSurfaceSharedData.h"
     30 #include "mozilla/layers/StackingContextHelper.h"
     31 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
     32 #include "UnitTransforms.h"
     33 #include "gfxEnv.h"
     34 #include "MediaInfo.h"
     35 #include "nsDisplayListInvalidation.h"
     36 #include "nsLayoutUtils.h"
     37 #include "nsTHashSet.h"
     38 #include "WebRenderCanvasRenderer.h"
     39 
     40 #include <cstdint>
     41 
     42 namespace mozilla::layers {
     43 
     44 using namespace gfx;
     45 using namespace image;
     46 static int sIndent;
     47 #include <stdarg.h>
     48 #include <stdio.h>
     49 
     50 static void GP(const char* fmt, ...) {
     51  va_list args;
     52  va_start(args, fmt);
     53 #if 0
     54    for (int i = 0; i < sIndent; i++) { printf(" "); }
     55    vprintf(fmt, args);
     56 #endif
     57  va_end(args);
     58 }
     59 
     60 bool FitsInt32(const float aVal) {
     61  // Although int32_t min and max can't be represented exactly with floats, the
     62  // cast truncates towards zero which is what we want here.
     63  const float min = static_cast<float>(std::numeric_limits<int32_t>::min());
     64  const float max = static_cast<float>(std::numeric_limits<int32_t>::max());
     65  return aVal > min && aVal < max;
     66 }
     67 
     68 // XXX: problems:
     69 // - How do we deal with scrolling while having only a single invalidation rect?
     70 // We can have a valid rect and an invalid rect. As we scroll the valid rect
     71 // will move and the invalid rect will be the new area
     72 
     73 struct BlobItemData;
     74 static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray);
     75 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(BlobGroupDataProperty,
     76                                    nsTArray<BlobItemData*>,
     77                                    DestroyBlobGroupDataProperty);
     78 
     79 // These are currently manually allocated and ownership is help by the
     80 // mDisplayItems hash table in DIGroup
     81 struct BlobItemData {
     82  // a weak pointer to the frame for this item.
     83  // DisplayItemData has a mFrameList to deal with merged frames. Hopefully we
     84  // don't need to worry about that.
     85  nsIFrame* mFrame;
     86 
     87  uint32_t mDisplayItemKey;
     88  nsTArray<BlobItemData*>*
     89      mArray;  // a weak pointer to the array that's owned by the frame property
     90 
     91  LayerIntRect mRect;
     92  // It would be nice to not need this. We need to be able to call
     93  // ComputeInvalidationRegion. ComputeInvalidationRegion will sometimes reach
     94  // into parent style structs to get information that can change the
     95  // invalidation region
     96  UniquePtr<nsDisplayItemGeometry> mGeometry;
     97  DisplayItemClip mClip;
     98  bool mInvisible;
     99  bool mUsed;  // initialized near construction
    100  // XXX: only used for debugging
    101  bool mInvalid;
    102 
    103  // a weak pointer to the group that owns this item
    104  // we use this to track whether group for a particular item has changed
    105  struct DIGroup* mGroup;
    106 
    107  // We need to keep a list of all the external surfaces used by the blob image.
    108  // We do this on a per-display item basis so that the lists remains correct
    109  // during invalidations.
    110  DrawEventRecorderPrivate::ExternalSurfacesHolder mExternalSurfaces;
    111 
    112  BlobItemData(DIGroup* aGroup, nsDisplayItem* aItem)
    113      : mInvisible(false), mUsed(false), mGroup(aGroup) {
    114    mInvalid = false;
    115    mDisplayItemKey = aItem->GetPerFrameKey();
    116    AddFrame(aItem->Frame());
    117  }
    118 
    119 private:
    120  void AddFrame(nsIFrame* aFrame) {
    121    mFrame = aFrame;
    122 
    123    nsTArray<BlobItemData*>* array =
    124        aFrame->GetProperty(BlobGroupDataProperty());
    125    if (!array) {
    126      array = new nsTArray<BlobItemData*>();
    127      aFrame->SetProperty(BlobGroupDataProperty(), array);
    128    }
    129    array->AppendElement(this);
    130    mArray = array;
    131  }
    132 
    133 public:
    134  void ClearFrame() {
    135    // Delete the weak pointer to this BlobItemData on the frame
    136    MOZ_RELEASE_ASSERT(mFrame);
    137    // the property may already be removed if WebRenderUserData got deleted
    138    // first so we use our own mArray pointer.
    139    mArray->RemoveElement(this);
    140 
    141    // drop the entire property if nothing's left in the array
    142    if (mArray->IsEmpty()) {
    143      // If the frame is in the process of being destroyed this will fail
    144      // but that's ok, because the the property will be removed then anyways
    145      mFrame->RemoveProperty(BlobGroupDataProperty());
    146    }
    147    mFrame = nullptr;
    148  }
    149 
    150  ~BlobItemData() {
    151    if (mFrame) {
    152      ClearFrame();
    153    }
    154  }
    155 };
    156 
    157 static BlobItemData* GetBlobItemData(nsDisplayItem* aItem) {
    158  nsIFrame* frame = aItem->Frame();
    159  uint32_t key = aItem->GetPerFrameKey();
    160  const nsTArray<BlobItemData*>* array =
    161      frame->GetProperty(BlobGroupDataProperty());
    162  if (array) {
    163    for (BlobItemData* item : *array) {
    164      if (item->mDisplayItemKey == key) {
    165        return item;
    166      }
    167    }
    168  }
    169  return nullptr;
    170 }
    171 
    172 // We keep around the BlobItemData so that when we invalidate it get properly
    173 // included in the rect
    174 static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray) {
    175  for (BlobItemData* item : *aArray) {
    176    GP("DestroyBlobGroupDataProperty: %p-%d\n", item->mFrame,
    177       item->mDisplayItemKey);
    178    item->mFrame = nullptr;
    179  }
    180  delete aArray;
    181 }
    182 
    183 static void TakeExternalSurfaces(
    184    WebRenderDrawEventRecorder* aRecorder,
    185    DrawEventRecorderPrivate::ExternalSurfacesHolder& aExternalSurfaces,
    186    RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources) {
    187  aRecorder->TakeExternalSurfaces(aExternalSurfaces);
    188 
    189  for (auto& entry : aExternalSurfaces) {
    190    // While we don't use the image key with the surface, because the blob image
    191    // renderer doesn't have easy access to the resource set, we still want to
    192    // ensure one is generated. That will ensure the surface remains alive until
    193    // at least the last epoch which the blob image could be used in.
    194    wr::ImageKey key;
    195    DebugOnly<nsresult> rv =
    196        SharedSurfacesChild::Share(entry.mSurface, aManager, aResources, key);
    197    MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED);
    198  }
    199 }
    200 
    201 struct DIGroup;
    202 struct Grouper {
    203  explicit Grouper(ClipManager& aClipManager)
    204      : mAppUnitsPerDevPixel(0),
    205        mDisplayListBuilder(nullptr),
    206        mClipManager(aClipManager) {}
    207 
    208  int32_t mAppUnitsPerDevPixel;
    209  nsDisplayListBuilder* mDisplayListBuilder;
    210  ClipManager& mClipManager;
    211  HitTestInfoManager mHitTestInfoManager;
    212  Matrix mTransform;
    213 
    214  // Paint the list of aChildren display items.
    215  void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem,
    216                          BlobItemData* aData, const IntRect& aItemBounds,
    217                          bool aDirty, nsDisplayList* aChildren,
    218                          gfxContext* aContext,
    219                          WebRenderDrawEventRecorder* aRecorder,
    220                          RenderRootStateManager* aRootManager,
    221                          wr::IpcResourceUpdateQueue& aResources);
    222 
    223  // Builds groups of display items split based on 'layer activity'
    224  void ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
    225                       WebRenderCommandBuilder* aCommandBuilder,
    226                       wr::DisplayListBuilder& aBuilder,
    227                       wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup,
    228                       nsDisplayList* aList, nsDisplayItem* aWrappingItem,
    229                       const StackingContextHelper& aSc);
    230  // Builds a group of display items without promoting anything to active.
    231  bool ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
    232                                    wr::DisplayListBuilder& aBuilder,
    233                                    wr::IpcResourceUpdateQueue& aResources,
    234                                    DIGroup* aGroup, nsDisplayList* aList,
    235                                    const StackingContextHelper& aSc);
    236  // Helper method for processing a single inactive item
    237  bool ConstructItemInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
    238                                   wr::DisplayListBuilder& aBuilder,
    239                                   wr::IpcResourceUpdateQueue& aResources,
    240                                   DIGroup* aGroup, nsDisplayItem* aItem,
    241                                   const StackingContextHelper& aSc,
    242                                   bool* aOutIsInvisible);
    243  ~Grouper() = default;
    244 };
    245 
    246 // Returns whether this is an item for which complete invalidation was
    247 // reliant on LayerTreeInvalidation in the pre-webrender world.
    248 static bool IsContainerLayerItem(nsDisplayItem* aItem) {
    249  switch (aItem->GetType()) {
    250    case DisplayItemType::TYPE_WRAP_LIST:
    251    case DisplayItemType::TYPE_CONTAINER:
    252    case DisplayItemType::TYPE_TRANSFORM:
    253    case DisplayItemType::TYPE_OPACITY:
    254    case DisplayItemType::TYPE_FILTER:
    255    case DisplayItemType::TYPE_BLEND_CONTAINER:
    256    case DisplayItemType::TYPE_BLEND_MODE:
    257    case DisplayItemType::TYPE_MASK:
    258    case DisplayItemType::TYPE_PERSPECTIVE: {
    259      return true;
    260    }
    261    default: {
    262      return false;
    263    }
    264  }
    265 }
    266 
    267 #include <sstream>
    268 
    269 static bool DetectContainerLayerPropertiesBoundsChange(
    270    nsDisplayItem* aItem, BlobItemData* aData,
    271    nsDisplayItemGeometry& aGeometry) {
    272  if (aItem->GetType() == DisplayItemType::TYPE_FILTER) {
    273    // Filters get clipped to the BuildingRect since they can
    274    // have huge bounds outside of the visible area.
    275    // This function and similar code in ComputeGeometryChange should be kept in
    276    // sync.
    277    aGeometry.mBounds = aGeometry.mBounds.Intersect(aItem->GetBuildingRect());
    278  }
    279 
    280  return !aGeometry.mBounds.IsEqualEdges(aData->mGeometry->mBounds);
    281 }
    282 
    283 /* A Display Item Group. This represents a set of diplay items that
    284 * have been grouped together for rasterization and can be partially
    285 * invalidated. It also tracks a number of properties from the environment
    286 * that when changed would cause us to repaint like mScale. */
    287 struct DIGroup {
    288  // XXX: Storing owning pointers to the BlobItemData in a hash table is not
    289  // a good choice. There are two better options:
    290  //
    291  // 1. We should just be using a linked list for this stuff.
    292  //    That we can iterate over only the used items.
    293  //    We remove from the unused list and add to the used list
    294  //    when we see an item.
    295  //
    296  //    we allocate using a free list.
    297  //
    298  // 2. We can use a Vec and use SwapRemove().
    299  //    We'll just need to be careful when iterating.
    300  //    The advantage of a Vec is that everything stays compact
    301  //    and we don't need to heap allocate the BlobItemData's
    302  nsTHashSet<BlobItemData*> mDisplayItems;
    303 
    304  LayerIntRect mInvalidRect;
    305  LayerIntRect mVisibleRect;
    306  // This is the last visible rect sent to WebRender. It's used
    307  // to compute the invalid rect and ensure that we send
    308  // the appropriate data to WebRender for merging.
    309  LayerIntRect mLastVisibleRect;
    310 
    311  // This is the intersection of mVisibleRect and mLastVisibleRect
    312  LayerIntRect mPreservedRect;
    313  // mHitTestBounds is the same as mActualBounds except for the bounds
    314  // of invisible items which are accounted for in the former but not
    315  // in the latter.
    316  LayerIntRect mHitTestBounds;
    317  LayerIntRect mActualBounds;
    318  int32_t mAppUnitsPerDevPixel;
    319  gfx::MatrixScales mScale;
    320  ScrollableLayerGuid::ViewID mScrollId;
    321  CompositorHitTestInfo mHitInfo;
    322  LayerPoint mResidualOffset;
    323  LayerIntRect mLayerBounds;  // mGroupBounds converted to Layer space
    324  // mLayerBounds clipped to the container/parent of the
    325  // current item being processed.
    326  LayerIntRect mClippedImageBounds;  // mLayerBounds with the clipping of any
    327                                     // containers applied
    328  Maybe<wr::BlobImageKey> mKey;
    329  std::vector<RefPtr<ScaledFont>> mFonts;
    330 
    331  DIGroup()
    332      : mAppUnitsPerDevPixel(0),
    333        mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID),
    334        mHitInfo(CompositorHitTestInvisibleToHit) {}
    335 
    336  void InvalidateRect(const LayerIntRect& aRect) {
    337    mInvalidRect = mInvalidRect.Union(aRect);
    338  }
    339 
    340  LayerIntRect ItemBounds(nsDisplayItem* aItem) {
    341    BlobItemData* data = GetBlobItemData(aItem);
    342    return data->mRect;
    343  }
    344 
    345  void ClearItems() {
    346    GP("items: %d\n", mDisplayItems.Count());
    347    for (BlobItemData* data : mDisplayItems) {
    348      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
    349      delete data;
    350    }
    351    mDisplayItems.Clear();
    352  }
    353 
    354  void ClearImageKey(RenderRootStateManager* aManager, bool aForce = false) {
    355    if (mKey) {
    356      MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
    357      aManager->AddBlobImageKeyForDiscard(*mKey);
    358      mKey = Nothing();
    359    }
    360    mFonts.clear();
    361  }
    362 
    363  static LayerIntRect ToDeviceSpace(const nsRect& aBounds, Matrix& aMatrix,
    364                                    int32_t aAppUnitsPerDevPixel) {
    365    // RoundedOut can convert empty rectangles to non-empty ones
    366    // so special case them here
    367    if (aBounds.IsEmpty()) {
    368      return LayerIntRect();
    369    }
    370    return LayerIntRect::FromUnknownRect(RoundedOut(aMatrix.TransformBounds(
    371        ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel)))));
    372  }
    373 
    374  bool ComputeGeometryChange(nsDisplayItem* aItem, BlobItemData* aData,
    375                             Matrix& aMatrix, nsDisplayListBuilder* aBuilder) {
    376    // If the frame is marked as invalidated, and didn't specify a rect to
    377    // invalidate then we want to invalidate both the old and new bounds,
    378    // otherwise we only want to invalidate the changed areas. If we do get an
    379    // invalid rect, then we want to add this on top of the change areas.
    380    nsRect invalid;
    381    bool invalidated = false;
    382    const DisplayItemClip& clip = aItem->GetClip();
    383 
    384    int32_t appUnitsPerDevPixel =
    385        aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
    386    MOZ_RELEASE_ASSERT(mAppUnitsPerDevPixel == appUnitsPerDevPixel);
    387    GP("\n");
    388    GP("clippedImageRect %d %d %d %d\n", mClippedImageBounds.x,
    389       mClippedImageBounds.y, mClippedImageBounds.width,
    390       mClippedImageBounds.height);
    391    LayerIntSize size = mVisibleRect.Size();
    392    GP("imageSize: %d %d\n", size.width, size.height);
    393    /*if (aItem->IsReused() && aData->mGeometry) {
    394      return;
    395    }*/
    396 
    397    GP("pre mInvalidRect: %s %p-%d - inv: %d %d %d %d\n", aItem->Name(),
    398       aItem->Frame(), aItem->GetPerFrameKey(), mInvalidRect.x, mInvalidRect.y,
    399       mInvalidRect.width, mInvalidRect.height);
    400    if (!aData->mGeometry) {
    401      // This item is being added for the first time, invalidate its entire
    402      // area.
    403      UniquePtr<nsDisplayItemGeometry> geometry(
    404          aItem->AllocateGeometry(aBuilder));
    405      nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    406          geometry->ComputeInvalidationRegion());
    407      aData->mGeometry = std::move(geometry);
    408 
    409      LayerIntRect transformedRect =
    410          ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    411      aData->mRect = transformedRect.Intersect(mClippedImageBounds);
    412      GP("CGC %s %d %d %d %d\n", aItem->Name(), clippedBounds.x,
    413         clippedBounds.y, clippedBounds.width, clippedBounds.height);
    414      GP("%d %d,  %f %f\n", mVisibleRect.TopLeft().x.value,
    415         mVisibleRect.TopLeft().y.value, aMatrix._11, aMatrix._22);
    416      GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y,
    417         aData->mRect.width, aData->mRect.height);
    418      InvalidateRect(aData->mRect);
    419      aData->mInvalid = true;
    420      invalidated = true;
    421    } else if (aItem->IsInvalid(invalid) && invalid.IsEmpty()) {
    422      UniquePtr<nsDisplayItemGeometry> geometry(
    423          aItem->AllocateGeometry(aBuilder));
    424      nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    425          geometry->ComputeInvalidationRegion());
    426      aData->mGeometry = std::move(geometry);
    427 
    428      GP("matrix: %f %f\n", aMatrix._31, aMatrix._32);
    429      GP("frame invalid invalidate: %s\n", aItem->Name());
    430      GP("old rect: %d %d %d %d\n", aData->mRect.x, aData->mRect.y,
    431         aData->mRect.width, aData->mRect.height);
    432      InvalidateRect(aData->mRect);
    433      // We want to snap to outside pixels. When should we multiply by the
    434      // matrix?
    435      // XXX: TransformBounds is expensive. We should avoid doing it if we have
    436      // no transform
    437      LayerIntRect transformedRect =
    438          ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    439      aData->mRect = transformedRect.Intersect(mClippedImageBounds);
    440      InvalidateRect(aData->mRect);
    441      GP("new rect: %d %d %d %d\n", aData->mRect.x, aData->mRect.y,
    442         aData->mRect.width, aData->mRect.height);
    443      aData->mInvalid = true;
    444      invalidated = true;
    445    } else {
    446      GP("else invalidate: %s\n", aItem->Name());
    447      nsRegion combined;
    448      // this includes situations like reflow changing the position
    449      aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(),
    450                                       &combined);
    451      if (!combined.IsEmpty()) {
    452        // There might be no point in doing this elaborate tracking here to get
    453        // smaller areas
    454        InvalidateRect(aData->mRect);  // invalidate the old area -- in theory
    455                                       // combined should take care of this
    456        UniquePtr<nsDisplayItemGeometry> geometry(
    457            aItem->AllocateGeometry(aBuilder));
    458        // invalidate the invalidated area.
    459 
    460        aData->mGeometry = std::move(geometry);
    461 
    462        nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    463            aData->mGeometry->ComputeInvalidationRegion());
    464        LayerIntRect transformedRect =
    465            ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    466        aData->mRect = transformedRect.Intersect(mClippedImageBounds);
    467        InvalidateRect(aData->mRect);
    468 
    469        aData->mInvalid = true;
    470        invalidated = true;
    471      } else {
    472        if (aData->mClip != clip) {
    473          UniquePtr<nsDisplayItemGeometry> geometry(
    474              aItem->AllocateGeometry(aBuilder));
    475          if (!IsContainerLayerItem(aItem)) {
    476            // the bounds of layer items can change on us without
    477            // ComputeInvalidationRegion returning any change. Other items
    478            // shouldn't have any hidden geometry change.
    479            MOZ_RELEASE_ASSERT(
    480                geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds));
    481          } else {
    482            aData->mGeometry = std::move(geometry);
    483          }
    484          nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    485              aData->mGeometry->ComputeInvalidationRegion());
    486          LayerIntRect transformedRect =
    487              ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    488          InvalidateRect(aData->mRect);
    489          aData->mRect = transformedRect.Intersect(mClippedImageBounds);
    490          InvalidateRect(aData->mRect);
    491          invalidated = true;
    492 
    493          GP("ClipChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
    494             aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
    495 
    496        } else if (IsContainerLayerItem(aItem)) {
    497          UniquePtr<nsDisplayItemGeometry> geometry(
    498              aItem->AllocateGeometry(aBuilder));
    499          // we need to catch bounds changes of containers so that we continue
    500          // to have the correct bounds rects in the recording
    501          if (DetectContainerLayerPropertiesBoundsChange(aItem, aData,
    502                                                         *geometry)) {
    503            nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    504                geometry->ComputeInvalidationRegion());
    505            aData->mGeometry = std::move(geometry);
    506            LayerIntRect transformedRect =
    507                ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    508            InvalidateRect(aData->mRect);
    509            aData->mRect = transformedRect.Intersect(mClippedImageBounds);
    510            InvalidateRect(aData->mRect);
    511            invalidated = true;
    512            GP("DetectContainerLayerPropertiesBoundsChange change\n");
    513          } else {
    514            // Handle changes in mClippedImageBounds
    515            nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    516                geometry->ComputeInvalidationRegion());
    517            LayerIntRect transformedRect =
    518                ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    519            auto rect = transformedRect.Intersect(mClippedImageBounds);
    520            if (!rect.IsEqualEdges(aData->mRect)) {
    521              GP("ContainerLayer image rect bounds change\n");
    522              InvalidateRect(aData->mRect);
    523              aData->mRect = rect;
    524              InvalidateRect(aData->mRect);
    525              invalidated = true;
    526            } else {
    527              GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
    528                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(),
    529                 aData->mRect.YMost());
    530            }
    531          }
    532        } else {
    533          UniquePtr<nsDisplayItemGeometry> geometry(
    534              aItem->AllocateGeometry(aBuilder));
    535          nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
    536              geometry->ComputeInvalidationRegion());
    537          LayerIntRect transformedRect =
    538              ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel);
    539          auto rect = transformedRect.Intersect(mClippedImageBounds);
    540          // Make sure we update mRect for mClippedImageBounds changes
    541          if (!rect.IsEqualEdges(aData->mRect)) {
    542            GP("ContainerLayer image rect bounds change\n");
    543            InvalidateRect(aData->mRect);
    544            aData->mRect = rect;
    545            InvalidateRect(aData->mRect);
    546            invalidated = true;
    547          } else {
    548            GP("NoChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
    549               aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
    550          }
    551        }
    552      }
    553    }
    554 
    555    if (aData->mGeometry && aItem->GetType() == DisplayItemType::TYPE_FILTER) {
    556      // This hunk DetectContainerLayerPropertiesBoundsChange should be kept in
    557      // sync.
    558      aData->mGeometry->mBounds =
    559          aData->mGeometry->mBounds.Intersect(aItem->GetBuildingRect());
    560    }
    561 
    562    mHitTestBounds.OrWith(aData->mRect);
    563    if (!aData->mInvisible) {
    564      mActualBounds.OrWith(aData->mRect);
    565    }
    566    aData->mClip = clip;
    567    GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
    568       mInvalidRect.width, mInvalidRect.height);
    569    return invalidated;
    570  }
    571 
    572  void EndGroup(WebRenderLayerManager* aWrManager,
    573                nsDisplayListBuilder* aDisplayListBuilder,
    574                wr::DisplayListBuilder& aBuilder,
    575                wr::IpcResourceUpdateQueue& aResources, Grouper* aGrouper,
    576                nsDisplayList::iterator aStartItem,
    577                nsDisplayList::iterator aEndItem) {
    578    GP("\n\n");
    579    GP("Begin EndGroup\n");
    580 
    581    auto scale = LayoutDeviceToLayerScale2D::FromUnknownScale(mScale);
    582 
    583    auto hitTestRect = mVisibleRect.Intersect(ViewAs<LayerPixel>(
    584        mHitTestBounds, PixelCastJustification::LayerIsImage));
    585    if (!hitTestRect.IsEmpty()) {
    586      auto deviceHitTestRect =
    587          (LayerRect(hitTestRect) - mResidualOffset) / scale;
    588      PushHitTest(aBuilder, deviceHitTestRect);
    589    }
    590 
    591    mVisibleRect = mVisibleRect.Intersect(ViewAs<LayerPixel>(
    592        mActualBounds, PixelCastJustification::LayerIsImage));
    593 
    594    if (mVisibleRect.IsEmpty()) {
    595      return;
    596    }
    597 
    598    // Invalidate any unused items
    599    GP("mDisplayItems\n");
    600    mDisplayItems.RemoveIf([&](BlobItemData* data) {
    601      GP("  : %p-%d\n", data->mFrame, data->mDisplayItemKey);
    602      if (!data->mUsed) {
    603        GP("Invalidate unused: %p-%d\n", data->mFrame, data->mDisplayItemKey);
    604        InvalidateRect(data->mRect);
    605        delete data;
    606        return true;
    607      }
    608 
    609      data->mUsed = false;
    610      return false;
    611    });
    612 
    613    IntSize dtSize = mVisibleRect.Size().ToUnknownSize();
    614    // The actual display item's size shouldn't have the scale factored in
    615    // Round the bounds out to leave space for unsnapped content
    616    LayoutDeviceRect itemBounds =
    617        (LayerRect(mVisibleRect) - mResidualOffset) / scale;
    618 
    619    if (mInvalidRect.IsEmpty() && mVisibleRect.IsEqualEdges(mLastVisibleRect)) {
    620      GP("Not repainting group because it's empty\n");
    621      GP("End EndGroup\n");
    622      if (mKey) {
    623        // Although the contents haven't changed, the visible area *may* have,
    624        // so request it be updated unconditionally (wr should be able to easily
    625        // detect if this is a no-op on its side, if that matters)
    626        aResources.SetBlobImageVisibleArea(
    627            *mKey, ViewAs<ImagePixel>(mVisibleRect,
    628                                      PixelCastJustification::LayerIsImage));
    629        mLastVisibleRect = mVisibleRect;
    630        PushImage(aBuilder, itemBounds);
    631      }
    632      return;
    633    }
    634 
    635    std::vector<RefPtr<ScaledFont>> fonts;
    636    bool validFonts = true;
    637    RefPtr<WebRenderDrawEventRecorder> recorder =
    638        MakeAndAddRef<WebRenderDrawEventRecorder>(
    639            [&](MemStream& aStream,
    640                std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
    641              size_t count = aScaledFonts.size();
    642              aStream.write((const char*)&count, sizeof(count));
    643              for (auto& scaled : aScaledFonts) {
    644                Maybe<wr::FontInstanceKey> key =
    645                    aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled,
    646                                                                    aResources);
    647                if (key.isNothing()) {
    648                  validFonts = false;
    649                  break;
    650                }
    651                BlobFont font = {key.value(), scaled};
    652                aStream.write((const char*)&font, sizeof(font));
    653              }
    654              fonts = std::move(aScaledFonts);
    655            });
    656 
    657    RefPtr<gfx::DrawTarget> dummyDt =
    658        gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
    659 
    660    RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(
    661        recorder, dummyDt, mLayerBounds.ToUnknownRect());
    662    if (!dt || !dt->IsValid()) {
    663      gfxCriticalNote << "Failed to create drawTarget for blob image";
    664      return;
    665    }
    666 
    667    gfxContext context(dt);
    668    context.SetMatrix(Matrix::Scaling(mScale).PostTranslate(mResidualOffset.x,
    669                                                            mResidualOffset.y));
    670 
    671    GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
    672       mInvalidRect.width, mInvalidRect.height);
    673 
    674    RenderRootStateManager* rootManager =
    675        aWrManager->GetRenderRootStateManager();
    676 
    677    bool empty = aStartItem == aEndItem;
    678    if (empty) {
    679      ClearImageKey(rootManager, true);
    680      return;
    681    }
    682 
    683    PaintItemRange(aGrouper, aStartItem, aEndItem, &context, recorder,
    684                   rootManager, aResources);
    685 
    686    // XXX: set this correctly perhaps using
    687    // aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).
    688    //   Contains(paintBounds);?
    689    wr::OpacityType opacity = wr::OpacityType::HasAlphaChannel;
    690 
    691    bool hasItems = recorder->Finish();
    692    GP("%d Finish\n", hasItems);
    693    if (!validFonts) {
    694      gfxCriticalNote << "Failed serializing fonts for blob image";
    695      return;
    696    }
    697    Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
    698                         recorder->mOutputStream.mLength);
    699    if (!mKey) {
    700      // we don't want to send a new image that doesn't have any
    701      // items in it
    702      if (!hasItems || mVisibleRect.IsEmpty()) {
    703        GP("Skipped group with no items\n");
    704        return;
    705      }
    706 
    707      wr::BlobImageKey key =
    708          wr::BlobImageKey{aWrManager->WrBridge()->GetNextImageKey()};
    709      GP("No previous key making new one %d\n", key._0.mHandle);
    710      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
    711      MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
    712      if (!aResources.AddBlobImage(
    713              key, descriptor, bytes,
    714              ViewAs<ImagePixel>(mVisibleRect,
    715                                 PixelCastJustification::LayerIsImage))) {
    716        return;
    717      }
    718      mKey = Some(key);
    719    } else {
    720      MOZ_DIAGNOSTIC_ASSERT(
    721          aWrManager->WrBridge()->MatchesNamespace(mKey.ref()),
    722          "Stale blob key for group!");
    723 
    724      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
    725 
    726      // Convert mInvalidRect to image space by subtracting the corner of the
    727      // image bounds
    728      auto dirtyRect = ViewAs<ImagePixel>(mInvalidRect,
    729                                          PixelCastJustification::LayerIsImage);
    730 
    731      auto bottomRight = dirtyRect.BottomRight();
    732      GP("check invalid %d %d - %d %d\n", bottomRight.x.value,
    733         bottomRight.y.value, dtSize.width, dtSize.height);
    734      GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
    735         mInvalidRect.width, mInvalidRect.height);
    736      if (!aResources.UpdateBlobImage(
    737              *mKey, descriptor, bytes,
    738              ViewAs<ImagePixel>(mVisibleRect,
    739                                 PixelCastJustification::LayerIsImage),
    740              dirtyRect)) {
    741        return;
    742      }
    743    }
    744    mFonts = std::move(fonts);
    745    aResources.SetBlobImageVisibleArea(
    746        *mKey,
    747        ViewAs<ImagePixel>(mVisibleRect, PixelCastJustification::LayerIsImage));
    748    mLastVisibleRect = mVisibleRect;
    749    PushImage(aBuilder, itemBounds);
    750    GP("End EndGroup\n\n");
    751  }
    752 
    753  void PushImage(wr::DisplayListBuilder& aBuilder,
    754                 const LayoutDeviceRect& bounds) {
    755    wr::LayoutRect dest = wr::ToLayoutRect(bounds);
    756    GP("PushImage: %f %f %f %f\n", dest.min.x, dest.min.y, dest.max.x,
    757       dest.max.y);
    758    // wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
    759    auto rendering = wr::ImageRendering::Auto;
    760    bool backfaceHidden = false;
    761 
    762    // XXX - clipping the item against the paint rect breaks some content.
    763    // cf. Bug 1455422.
    764    // wr::LayoutRect clip = wr::ToLayoutRect(bounds.Intersect(mVisibleRect));
    765 
    766    aBuilder.PushImage(dest, dest, !backfaceHidden, false, rendering,
    767                       wr::AsImageKey(*mKey));
    768  }
    769 
    770  void PushHitTest(wr::DisplayListBuilder& aBuilder,
    771                   const LayoutDeviceRect& bounds) {
    772    wr::LayoutRect dest = wr::ToLayoutRect(bounds);
    773    GP("PushHitTest: %f %f %f %f\n", dest.min.x, dest.min.y, dest.max.x,
    774       dest.max.y);
    775 
    776    // We don't really know the exact shape of this blob because it may contain
    777    // SVG shapes. Also mHitInfo may be a combination of hit info flags from
    778    // different shapes so generate an irregular-area hit-test region for it.
    779    CompositorHitTestInfo hitInfo = mHitInfo;
    780    if (hitInfo.contains(CompositorHitTestFlags::eVisibleToHitTest)) {
    781      hitInfo += CompositorHitTestFlags::eIrregularArea;
    782    }
    783 
    784    bool backfaceHidden = false;
    785    aBuilder.PushHitTest(dest, dest, !backfaceHidden, mScrollId, hitInfo,
    786                         SideBits::eNone);
    787  }
    788 
    789  void PaintItemRange(Grouper* aGrouper, nsDisplayList::iterator aStartItem,
    790                      nsDisplayList::iterator aEndItem, gfxContext* aContext,
    791                      WebRenderDrawEventRecorder* aRecorder,
    792                      RenderRootStateManager* aRootManager,
    793                      wr::IpcResourceUpdateQueue& aResources) {
    794    LayerIntSize size = mVisibleRect.Size();
    795    for (auto it = aStartItem; it != aEndItem; ++it) {
    796      nsDisplayItem* item = *it;
    797      MOZ_ASSERT(item);
    798 
    799      if (item->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
    800        continue;
    801      }
    802 
    803      BlobItemData* data = GetBlobItemData(item);
    804      if (data->mInvisible) {
    805        continue;
    806      }
    807 
    808      LayerIntRect bounds = data->mRect;
    809 
    810      // skip empty items
    811      if (bounds.IsEmpty()) {
    812        continue;
    813      }
    814 
    815      GP("Trying %s %p-%d %d %d %d %d\n", item->Name(), item->Frame(),
    816         item->GetPerFrameKey(), bounds.x, bounds.y, bounds.XMost(),
    817         bounds.YMost());
    818 
    819      auto bottomRight = bounds.BottomRight();
    820 
    821      GP("paint check invalid %d %d - %d %d\n", bottomRight.x.value,
    822         bottomRight.y.value, size.width, size.height);
    823 
    824      bool dirty = true;
    825      auto preservedBounds = bounds.Intersect(mPreservedRect);
    826      if (!mInvalidRect.Contains(preservedBounds)) {
    827        GP("Passing\n");
    828        dirty = false;
    829        if (data->mInvalid) {
    830          gfxCriticalError()
    831              << "DisplayItem" << item->Name() << "-should be invalid";
    832        }
    833        // if the item is invalid it needs to be fully contained
    834        MOZ_RELEASE_ASSERT(!data->mInvalid);
    835      }
    836 
    837      nsDisplayList* children = item->GetChildren();
    838      if (children) {
    839        // If we aren't dirty, we still need to iterate over the children to
    840        // ensure the blob index data is recorded the same as before to allow
    841        // the merging of the parts inside in the invalid rect. Any items that
    842        // are painted as a single item need to avoid repainting in that case.
    843        GP("doing children in EndGroup\n");
    844        aGrouper->PaintContainerItem(this, item, data, bounds.ToUnknownRect(),
    845                                     dirty, children, aContext, aRecorder,
    846                                     aRootManager, aResources);
    847        continue;
    848      }
    849      nsPaintedDisplayItem* paintedItem = item->AsPaintedDisplayItem();
    850      if (!paintedItem) {
    851        continue;
    852      }
    853      if (dirty) {
    854        // What should the clip settting strategy be? We can set the full
    855        // clip everytime. this is probably easiest for now. An alternative
    856        // would be to put the push and the pop into separate items and let
    857        // invalidation handle it that way.
    858        DisplayItemClip currentClip = paintedItem->GetClip();
    859 
    860        if (currentClip.HasClip()) {
    861          aContext->Save();
    862          currentClip.ApplyTo(aContext, aGrouper->mAppUnitsPerDevPixel);
    863        }
    864        aContext->NewPath();
    865        GP("painting %s %p-%d\n", paintedItem->Name(), paintedItem->Frame(),
    866           paintedItem->GetPerFrameKey());
    867        if (aGrouper->mDisplayListBuilder->IsPaintingToWindow()) {
    868          paintedItem->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES);
    869        }
    870 
    871        paintedItem->Paint(aGrouper->mDisplayListBuilder, aContext);
    872        TakeExternalSurfaces(aRecorder, data->mExternalSurfaces, aRootManager,
    873                             aResources);
    874 
    875        if (currentClip.HasClip()) {
    876          aContext->Restore();
    877        }
    878      }
    879      aContext->GetDrawTarget()->FlushItem(bounds.ToUnknownRect());
    880    }
    881  }
    882 
    883  ~DIGroup() {
    884    GP("Group destruct\n");
    885    for (BlobItemData* data : mDisplayItems) {
    886      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
    887      delete data;
    888    }
    889  }
    890 };
    891 
    892 // If we have an item we need to make sure it matches the current group
    893 // otherwise it means the item switched groups and we need to invalidate
    894 // it and recreate the data.
    895 static BlobItemData* GetBlobItemDataForGroup(nsDisplayItem* aItem,
    896                                             DIGroup* aGroup) {
    897  BlobItemData* data = GetBlobItemData(aItem);
    898  if (data) {
    899    MOZ_ASSERT(data->mGroup->mDisplayItems.Contains(data));
    900    if (data->mGroup != aGroup) {
    901      GP("group don't match %p %p\n", data->mGroup, aGroup);
    902      data->ClearFrame();
    903      // the item is for another group
    904      // it should be cleared out as being unused at the end of this paint
    905      data = nullptr;
    906    }
    907  }
    908  if (!data) {
    909    GP("Allocating blob data\n");
    910    data = new BlobItemData(aGroup, aItem);
    911    aGroup->mDisplayItems.Insert(data);
    912  }
    913  data->mUsed = true;
    914  return data;
    915 }
    916 
    917 void Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem,
    918                                 BlobItemData* aData,
    919                                 const IntRect& aItemBounds, bool aDirty,
    920                                 nsDisplayList* aChildren, gfxContext* aContext,
    921                                 WebRenderDrawEventRecorder* aRecorder,
    922                                 RenderRootStateManager* aRootManager,
    923                                 wr::IpcResourceUpdateQueue& aResources) {
    924  switch (aItem->GetType()) {
    925    case DisplayItemType::TYPE_TRANSFORM: {
    926      DisplayItemClip currentClip = aItem->GetClip();
    927 
    928      gfxContextMatrixAutoSaveRestore saveMatrix;
    929      if (currentClip.HasClip()) {
    930        aContext->Save();
    931        currentClip.ApplyTo(aContext, this->mAppUnitsPerDevPixel);
    932        aContext->GetDrawTarget()->FlushItem(aItemBounds);
    933      } else {
    934        saveMatrix.SetContext(aContext);
    935      }
    936 
    937      auto transformItem = static_cast<nsDisplayTransform*>(aItem);
    938      Matrix4x4Flagged trans = transformItem->GetTransform();
    939      Matrix trans2d;
    940      if (!trans.Is2D(&trans2d)) {
    941        // Painting will cause us to include the item's recording in the blob.
    942        // We only want to do that if it is dirty, because otherwise the
    943        // recording might change (e.g. due to factor of 2 scaling of images
    944        // giving different results) and the merging will discard it because it
    945        // is outside the invalid rect.
    946        if (aDirty) {
    947          // We don't currently support doing invalidation inside 3d transforms.
    948          // For now just paint it as a single item.
    949          aItem->AsPaintedDisplayItem()->Paint(mDisplayListBuilder, aContext);
    950          TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces,
    951                               aRootManager, aResources);
    952        }
    953        aContext->GetDrawTarget()->FlushItem(aItemBounds);
    954      } else if (!trans2d.IsSingular()) {
    955        aContext->Multiply(ThebesMatrix(trans2d));
    956        aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
    957                               aContext, aRecorder, aRootManager, aResources);
    958      }
    959 
    960      if (currentClip.HasClip()) {
    961        aContext->Restore();
    962        aContext->GetDrawTarget()->FlushItem(aItemBounds);
    963      }
    964      break;
    965    }
    966    case DisplayItemType::TYPE_OPACITY: {
    967      auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
    968      float opacity = opacityItem->GetOpacity();
    969      if (opacity == 0.0f) {
    970        return;
    971      }
    972 
    973      aContext->GetDrawTarget()->PushLayer(false, opacityItem->GetOpacity(),
    974                                           nullptr, mozilla::gfx::Matrix(),
    975                                           aItemBounds);
    976      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
    977         aItem->GetPerFrameKey());
    978      aContext->GetDrawTarget()->FlushItem(aItemBounds);
    979      aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
    980                             aContext, aRecorder, aRootManager, aResources);
    981      aContext->GetDrawTarget()->PopLayer();
    982      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
    983         aItem->GetPerFrameKey());
    984      aContext->GetDrawTarget()->FlushItem(aItemBounds);
    985      break;
    986    }
    987    case DisplayItemType::TYPE_BLEND_MODE: {
    988      auto blendItem = static_cast<nsDisplayBlendMode*>(aItem);
    989      auto blendMode = blendItem->BlendMode();
    990      aContext->GetDrawTarget()->PushLayerWithBlend(
    991          false, 1.0, nullptr, mozilla::gfx::Matrix(), aItemBounds, false,
    992          blendMode);
    993      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
    994         aItem->GetPerFrameKey());
    995      aContext->GetDrawTarget()->FlushItem(aItemBounds);
    996      aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
    997                             aContext, aRecorder, aRootManager, aResources);
    998      aContext->GetDrawTarget()->PopLayer();
    999      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
   1000         aItem->GetPerFrameKey());
   1001      aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1002      break;
   1003    }
   1004    case DisplayItemType::TYPE_BLEND_CONTAINER: {
   1005      auto* bc = static_cast<nsDisplayBlendContainer*>(aItem);
   1006      const bool flatten = bc->ShouldFlattenAway(mDisplayListBuilder);
   1007      if (!flatten) {
   1008        aContext->GetDrawTarget()->PushLayer(
   1009            false, 1.0, nullptr, mozilla::gfx::Matrix(), aItemBounds);
   1010        GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
   1011           aItem->GetPerFrameKey());
   1012        aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1013      }
   1014      aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
   1015                             aContext, aRecorder, aRootManager, aResources);
   1016      if (!flatten) {
   1017        aContext->GetDrawTarget()->PopLayer();
   1018        GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
   1019           aItem->GetPerFrameKey());
   1020        aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1021      }
   1022      break;
   1023    }
   1024    case DisplayItemType::TYPE_MASK: {
   1025      GP("Paint Mask\n");
   1026      auto maskItem = static_cast<nsDisplayMasksAndClipPaths*>(aItem);
   1027      if (maskItem->IsValidMask()) {
   1028        maskItem->PaintWithContentsPaintCallback(
   1029            mDisplayListBuilder, aContext, [&] {
   1030              GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
   1031                 aItem->GetPerFrameKey());
   1032              aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1033              aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
   1034                                     aContext, aRecorder, aRootManager,
   1035                                     aResources);
   1036              GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(),
   1037                 aItem->GetPerFrameKey());
   1038            });
   1039        TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces, aRootManager,
   1040                             aResources);
   1041        aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1042      }
   1043      break;
   1044    }
   1045    case DisplayItemType::TYPE_FILTER: {
   1046      GP("Paint Filter\n");
   1047      // Painting will cause us to include the item's recording in the blob. We
   1048      // only want to do that if it is dirty, because otherwise the recording
   1049      // might change (e.g. due to factor of 2 scaling of images giving
   1050      // different results) and the merging will discard it because it is
   1051      // outside the invalid rect.
   1052      if (aDirty) {
   1053        auto filterItem = static_cast<nsDisplayFilters*>(aItem);
   1054        filterItem->Paint(mDisplayListBuilder, aContext);
   1055        TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces, aRootManager,
   1056                             aResources);
   1057      }
   1058      aContext->GetDrawTarget()->FlushItem(aItemBounds);
   1059      break;
   1060    }
   1061 
   1062    default:
   1063      aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(),
   1064                             aContext, aRecorder, aRootManager, aResources);
   1065      break;
   1066  }
   1067 }
   1068 
   1069 class WebRenderGroupData : public WebRenderUserData {
   1070 public:
   1071  WebRenderGroupData(RenderRootStateManager* aWRManager, nsDisplayItem* aItem);
   1072  WebRenderGroupData(RenderRootStateManager* aWRManager,
   1073                     uint32_t aDisplayItemKey, nsIFrame* aFrame);
   1074  virtual ~WebRenderGroupData();
   1075 
   1076  WebRenderGroupData* AsGroupData() override { return this; }
   1077  UserDataType GetType() override { return UserDataType::eGroup; }
   1078  static UserDataType Type() { return UserDataType::eGroup; }
   1079 
   1080  DIGroup mSubGroup;
   1081  DIGroup mFollowingGroup;
   1082 };
   1083 
   1084 enum class ItemActivity : uint8_t {
   1085  /// Item must not be active.
   1086  No = 0,
   1087  /// Could be active if it has no layerization cost.
   1088  /// Typically active if first of an item group.
   1089  Could = 1,
   1090  /// Should be active unless something external makes that less useful.
   1091  /// For example if the item is affected by a complex mask, it remains
   1092  /// inactive.
   1093  Should = 2,
   1094  /// Must be active regardless of external factors.
   1095  Must = 3,
   1096 };
   1097 
   1098 ItemActivity CombineActivity(ItemActivity a, ItemActivity b) {
   1099  return a > b ? a : b;
   1100 }
   1101 
   1102 bool ActivityAtLeast(ItemActivity rhs, ItemActivity atLeast) {
   1103  return rhs >= atLeast;
   1104 }
   1105 
   1106 static ItemActivity IsItemProbablyActive(
   1107    nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
   1108    mozilla::wr::IpcResourceUpdateQueue& aResources,
   1109    const mozilla::layers::StackingContextHelper& aSc,
   1110    mozilla::layers::RenderRootStateManager* aManager,
   1111    nsDisplayListBuilder* aDisplayListBuilder, bool aSiblingActive,
   1112    bool aUniformlyScaled);
   1113 
   1114 static ItemActivity HasActiveChildren(
   1115    const nsDisplayList& aList, mozilla::wr::DisplayListBuilder& aBuilder,
   1116    mozilla::wr::IpcResourceUpdateQueue& aResources,
   1117    const mozilla::layers::StackingContextHelper& aSc,
   1118    mozilla::layers::RenderRootStateManager* aManager,
   1119    nsDisplayListBuilder* aDisplayListBuilder, bool aUniformlyScaled) {
   1120  ItemActivity activity = ItemActivity::No;
   1121  for (nsDisplayItem* item : aList) {
   1122    // Here we only want to know if a child must be active, so we don't specify
   1123    // when the item is first or last, which can cause an item that could be
   1124    // either decide to be active. This is a bit conservative and avoids some
   1125    // extra layers. It's a good tradeoff until we get to the point where most
   1126    // items could have been active but none *had* to. Right now this is
   1127    // unlikely but as more svg items get webrenderized it will be better to
   1128    // make them active more aggressively.
   1129    auto childActivity =
   1130        IsItemProbablyActive(item, aBuilder, aResources, aSc, aManager,
   1131                             aDisplayListBuilder, false, aUniformlyScaled);
   1132    activity = CombineActivity(activity, childActivity);
   1133    if (activity == ItemActivity::Must) {
   1134      return activity;
   1135    }
   1136  }
   1137  return activity;
   1138 }
   1139 
   1140 static ItemActivity AssessBounds(const StackingContextHelper& aSc,
   1141                                 nsDisplayListBuilder* aDisplayListBuilder,
   1142                                 nsDisplayItem* aItem,
   1143                                 bool aHasActivePrecedingSibling) {
   1144  // Arbitrary threshold up for adjustments. What we want to avoid here
   1145  // is alternating between active and non active items and create a lot
   1146  // of overlapping blobs, so we only make images active if they are
   1147  // costly enough that it's worth the risk of having more layers. As we
   1148  // move more blob items into wr display items it will become less of a
   1149  // concern.
   1150  constexpr float largeish = 512;
   1151 
   1152  bool snap = false;
   1153  nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &snap);
   1154 
   1155  float appUnitsPerDevPixel =
   1156      static_cast<float>(aItem->Frame()->PresContext()->AppUnitsPerDevPixel());
   1157 
   1158  float width =
   1159      static_cast<float>(bounds.width) * aSc.GetInheritedScale().xScale;
   1160  float height =
   1161      static_cast<float>(bounds.height) * aSc.GetInheritedScale().yScale;
   1162 
   1163  // Webrender doesn't handle primitives smaller than a pixel well, so
   1164  // avoid making them active.
   1165  if (width >= appUnitsPerDevPixel && height >= appUnitsPerDevPixel) {
   1166    if (aHasActivePrecedingSibling || width > largeish || height > largeish) {
   1167      return ItemActivity::Should;
   1168    }
   1169 
   1170    return ItemActivity::Could;
   1171  }
   1172 
   1173  return ItemActivity::No;
   1174 }
   1175 
   1176 // This function decides whether we want to treat this item as "active", which
   1177 // means that it's a container item which we will turn into a WebRender
   1178 // StackingContext, or whether we treat it as "inactive" and include it inside
   1179 // the parent blob image.
   1180 //
   1181 // We can't easily use GetLayerState because it wants a bunch of layers related
   1182 // information.
   1183 static ItemActivity IsItemProbablyActive(
   1184    nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
   1185    mozilla::wr::IpcResourceUpdateQueue& aResources,
   1186    const mozilla::layers::StackingContextHelper& aSc,
   1187    mozilla::layers::RenderRootStateManager* aManager,
   1188    nsDisplayListBuilder* aDisplayListBuilder, bool aHasActivePrecedingSibling,
   1189    bool aUniformlyScaled) {
   1190  switch (aItem->GetType()) {
   1191    case DisplayItemType::TYPE_TRANSFORM: {
   1192      nsDisplayTransform* transformItem =
   1193          static_cast<nsDisplayTransform*>(aItem);
   1194      const Matrix4x4Flagged& t = transformItem->GetTransform();
   1195      Matrix t2d;
   1196      bool is2D = t.Is2D(&t2d);
   1197      if (!is2D) {
   1198        return ItemActivity::Must;
   1199      }
   1200 
   1201      auto activity = HasActiveChildren(*transformItem->GetChildren(), aBuilder,
   1202                                        aResources, aSc, aManager,
   1203                                        aDisplayListBuilder, aUniformlyScaled);
   1204 
   1205      if (transformItem->MayBeAnimated(aDisplayListBuilder)) {
   1206        activity = CombineActivity(activity, ItemActivity::Should);
   1207      }
   1208 
   1209      return activity;
   1210    }
   1211    case DisplayItemType::TYPE_OPACITY: {
   1212      nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
   1213      if (opacityItem->NeedsActiveLayer(aDisplayListBuilder,
   1214                                        opacityItem->Frame())) {
   1215        return ItemActivity::Must;
   1216      }
   1217      return HasActiveChildren(*opacityItem->GetChildren(), aBuilder,
   1218                               aResources, aSc, aManager, aDisplayListBuilder,
   1219                               aUniformlyScaled);
   1220    }
   1221    case DisplayItemType::TYPE_FOREIGN_OBJECT: {
   1222      return ItemActivity::Must;
   1223    }
   1224    case DisplayItemType::TYPE_SVG_GEOMETRY: {
   1225      auto* svgItem = static_cast<DisplaySVGGeometry*>(aItem);
   1226      if (StaticPrefs::gfx_webrender_svg_shapes() && aUniformlyScaled &&
   1227          svgItem->ShouldBeActive(aBuilder, aResources, aSc, aManager,
   1228                                  aDisplayListBuilder)) {
   1229        return AssessBounds(aSc, aDisplayListBuilder, aItem,
   1230                            aHasActivePrecedingSibling);
   1231      }
   1232 
   1233      return ItemActivity::No;
   1234    }
   1235    case DisplayItemType::TYPE_SVG_IMAGE: {
   1236      auto* svgItem = static_cast<DisplaySVGImage*>(aItem);
   1237      if (StaticPrefs::gfx_webrender_svg_images() && aUniformlyScaled &&
   1238          svgItem->ShouldBeActive(aBuilder, aResources, aSc, aManager,
   1239                                  aDisplayListBuilder)) {
   1240        return AssessBounds(aSc, aDisplayListBuilder, aItem,
   1241                            aHasActivePrecedingSibling);
   1242      }
   1243 
   1244      return ItemActivity::No;
   1245    }
   1246    case DisplayItemType::TYPE_BLEND_MODE: {
   1247      /* BLEND_MODE needs to be active if it might have a previous sibling
   1248       * that is active so that it's able to blend with that content. */
   1249      if (aHasActivePrecedingSibling) {
   1250        return ItemActivity::Must;
   1251      }
   1252 
   1253      return HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, aSc,
   1254                               aManager, aDisplayListBuilder, aUniformlyScaled);
   1255    }
   1256    case DisplayItemType::TYPE_MASK: {
   1257      if (aItem->GetChildren()) {
   1258        auto activity =
   1259            HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, aSc,
   1260                              aManager, aDisplayListBuilder, aUniformlyScaled);
   1261        // For masked items, don't bother with making children active since we
   1262        // are going to have to need to paint and upload a large mask anyway.
   1263        if (activity < ItemActivity::Must) {
   1264          return ItemActivity::No;
   1265        }
   1266        return activity;
   1267      }
   1268      return ItemActivity::No;
   1269    }
   1270    case DisplayItemType::TYPE_WRAP_LIST:
   1271    case DisplayItemType::TYPE_CONTAINER:
   1272    case DisplayItemType::TYPE_PERSPECTIVE: {
   1273      if (aItem->GetChildren()) {
   1274        return HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources,
   1275                                 aSc, aManager, aDisplayListBuilder,
   1276                                 aUniformlyScaled);
   1277      }
   1278      return ItemActivity::No;
   1279    }
   1280    case DisplayItemType::TYPE_FILTER: {
   1281      nsDisplayFilters* filters = static_cast<nsDisplayFilters*>(aItem);
   1282      if (filters->CanCreateWebRenderCommands()) {
   1283        // Items are usually expensive enough on the CPU that we want to
   1284        // make them active whenever we can.
   1285        return ItemActivity::Must;
   1286      }
   1287      return ItemActivity::No;
   1288    }
   1289    default:
   1290      // TODO: handle other items?
   1291      return ItemActivity::No;
   1292  }
   1293 }
   1294 
   1295 // This does a pass over the display lists and will join the display items
   1296 // into groups as well as paint them
   1297 void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
   1298                              WebRenderCommandBuilder* aCommandBuilder,
   1299                              wr::DisplayListBuilder& aBuilder,
   1300                              wr::IpcResourceUpdateQueue& aResources,
   1301                              DIGroup* aGroup, nsDisplayList* aList,
   1302                              nsDisplayItem* aWrappingItem,
   1303                              const StackingContextHelper& aSc) {
   1304  RenderRootStateManager* manager =
   1305      aCommandBuilder->mManager->GetRenderRootStateManager();
   1306 
   1307  nsDisplayList::iterator startOfCurrentGroup = aList->end();
   1308  DIGroup* currentGroup = aGroup;
   1309 
   1310  // We need to track whether we have active siblings for mixed blend mode.
   1311  bool encounteredActiveItem = false;
   1312  bool isFirstGroup = true;
   1313  // Track whether the item is the first (visible) of its group in which case
   1314  // making it active won't add extra layers.
   1315  bool isFirst = true;
   1316 
   1317  for (auto it = aList->begin(); it != aList->end(); ++it) {
   1318    nsDisplayItem* item = *it;
   1319    MOZ_ASSERT(item);
   1320 
   1321    if (item->HasHitTestInfo()) {
   1322      // Accumulate the hit-test info flags. In cases where there are multiple
   1323      // hittest-info display items with different flags, mHitInfo will have
   1324      // the union of all those flags. If that is the case, we will
   1325      // additionally set eIrregularArea (at the site that we use mHitInfo)
   1326      // so that downstream consumers of this (primarily APZ) will know that
   1327      // the exact shape of what gets hit with what is unknown.
   1328      currentGroup->mHitInfo += item->GetHitTestInfo().Info();
   1329    }
   1330 
   1331    if (startOfCurrentGroup == aList->end()) {
   1332      startOfCurrentGroup = it;
   1333      if (!isFirstGroup) {
   1334        mClipManager.SwitchItem(aDisplayListBuilder, aWrappingItem);
   1335      }
   1336    }
   1337 
   1338    bool isLast = it.HasNext();
   1339 
   1340    // WebRender's anti-aliasing approximation is not very good under
   1341    // non-uniform scales.
   1342    bool uniformlyScaled =
   1343        fabs(aGroup->mScale.xScale - aGroup->mScale.yScale) < 0.1;
   1344 
   1345    auto activity = IsItemProbablyActive(
   1346        item, aBuilder, aResources, aSc, manager, mDisplayListBuilder,
   1347        encounteredActiveItem, uniformlyScaled);
   1348    auto threshold =
   1349        isFirst || isLast ? ItemActivity::Could : ItemActivity::Should;
   1350 
   1351    if (activity >= threshold) {
   1352      encounteredActiveItem = true;
   1353      // We're going to be starting a new group.
   1354      RefPtr<WebRenderGroupData> groupData =
   1355          aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(
   1356              item);
   1357 
   1358      groupData->mFollowingGroup.mInvalidRect.SetEmpty();
   1359 
   1360      // Initialize groupData->mFollowingGroup with data from currentGroup.
   1361      // We want to copy out this information before calling EndGroup because
   1362      // EndGroup will set mLastVisibleRect depending on whether
   1363      // we send something to WebRender.
   1364 
   1365      // TODO: compute the group bounds post-grouping, so that they can be
   1366      // tighter for just the sublist that made it into this group.
   1367      // We want to ensure the tight bounds are still clipped by area
   1368      // that we're building the display list for.
   1369      if (groupData->mFollowingGroup.mScale != currentGroup->mScale ||
   1370          groupData->mFollowingGroup.mAppUnitsPerDevPixel !=
   1371              currentGroup->mAppUnitsPerDevPixel ||
   1372          groupData->mFollowingGroup.mResidualOffset !=
   1373              currentGroup->mResidualOffset) {
   1374        if (groupData->mFollowingGroup.mAppUnitsPerDevPixel !=
   1375            currentGroup->mAppUnitsPerDevPixel) {
   1376          GP("app unit change following: %d %d\n",
   1377             groupData->mFollowingGroup.mAppUnitsPerDevPixel,
   1378             currentGroup->mAppUnitsPerDevPixel);
   1379        }
   1380        // The group changed size
   1381        GP("Inner group size change\n");
   1382        groupData->mFollowingGroup.ClearItems();
   1383        groupData->mFollowingGroup.ClearImageKey(
   1384            aCommandBuilder->mManager->GetRenderRootStateManager());
   1385      }
   1386      groupData->mFollowingGroup.mAppUnitsPerDevPixel =
   1387          currentGroup->mAppUnitsPerDevPixel;
   1388      groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
   1389      groupData->mFollowingGroup.mClippedImageBounds =
   1390          currentGroup->mClippedImageBounds;
   1391      groupData->mFollowingGroup.mScale = currentGroup->mScale;
   1392      groupData->mFollowingGroup.mResidualOffset =
   1393          currentGroup->mResidualOffset;
   1394      groupData->mFollowingGroup.mVisibleRect = currentGroup->mVisibleRect;
   1395      groupData->mFollowingGroup.mPreservedRect =
   1396          groupData->mFollowingGroup.mVisibleRect.Intersect(
   1397              groupData->mFollowingGroup.mLastVisibleRect);
   1398      groupData->mFollowingGroup.mActualBounds = LayerIntRect();
   1399      groupData->mFollowingGroup.mHitTestBounds = LayerIntRect();
   1400      groupData->mFollowingGroup.mHitInfo = currentGroup->mHitInfo;
   1401 
   1402      currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder,
   1403                             aBuilder, aResources, this, startOfCurrentGroup,
   1404                             it);
   1405 
   1406      {
   1407        auto spaceAndClipChain =
   1408            mClipManager.SwitchItem(aDisplayListBuilder, item);
   1409        wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
   1410        bool hasHitTest = mHitTestInfoManager.ProcessItem(item, aBuilder,
   1411                                                          aDisplayListBuilder);
   1412        // XXX - This is hacky. Some items have hit testing info on them but we
   1413        // also have dedicated hit testing items, the flags of which apply to
   1414        // the the group that contains them. We don't want layerization to
   1415        // affect that so if the item didn't emit any hit testing then we still
   1416        // push a hit test item if the previous group had some hit test flags
   1417        // set. This is obviously not great. Hit testing should be independent
   1418        // from how we layerize.
   1419        if (!hasHitTest &&
   1420            currentGroup->mHitInfo != gfx::CompositorHitTestInvisibleToHit) {
   1421          auto hitTestRect = item->GetBuildingRect();
   1422          if (!hitTestRect.IsEmpty()) {
   1423            currentGroup->PushHitTest(
   1424                aBuilder, LayoutDeviceRect::FromAppUnits(
   1425                              hitTestRect, currentGroup->mAppUnitsPerDevPixel));
   1426          }
   1427        }
   1428 
   1429        sIndent++;
   1430        // Note: this call to CreateWebRenderCommands can recurse back into
   1431        // this function.
   1432        bool createdWRCommands = item->CreateWebRenderCommands(
   1433            aBuilder, aResources, aSc, manager, mDisplayListBuilder);
   1434        MOZ_RELEASE_ASSERT(
   1435            createdWRCommands,
   1436            "active transforms should always succeed at creating "
   1437            "WebRender commands");
   1438        sIndent--;
   1439      }
   1440 
   1441      isFirstGroup = false;
   1442      startOfCurrentGroup = aList->end();
   1443      currentGroup = &groupData->mFollowingGroup;
   1444      isFirst = true;
   1445    } else {  // inactive item
   1446      bool isInvisible = false;
   1447      ConstructItemInsideInactive(aCommandBuilder, aBuilder, aResources,
   1448                                  currentGroup, item, aSc, &isInvisible);
   1449      if (!isInvisible) {
   1450        // Invisible items don't count.
   1451        isFirst = false;
   1452      }
   1453    }
   1454  }
   1455 
   1456  currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder,
   1457                         aBuilder, aResources, this, startOfCurrentGroup,
   1458                         aList->end());
   1459 }
   1460 
   1461 // This does a pass over the display lists and will join the display items
   1462 // into a single group.
   1463 bool Grouper::ConstructGroupInsideInactive(
   1464    WebRenderCommandBuilder* aCommandBuilder, wr::DisplayListBuilder& aBuilder,
   1465    wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup,
   1466    nsDisplayList* aList, const StackingContextHelper& aSc) {
   1467  bool invalidated = false;
   1468  for (nsDisplayItem* item : *aList) {
   1469    if (item->HasHitTestInfo()) {
   1470      // Accumulate the hit-test info flags. In cases where there are multiple
   1471      // hittest-info display items with different flags, mHitInfo will have
   1472      // the union of all those flags. If that is the case, we will
   1473      // additionally set eIrregularArea (at the site that we use mHitInfo)
   1474      // so that downstream consumers of this (primarily APZ) will know that
   1475      // the exact shape of what gets hit with what is unknown.
   1476      aGroup->mHitInfo += item->GetHitTestInfo().Info();
   1477    }
   1478 
   1479    bool invisible = false;
   1480    invalidated |= ConstructItemInsideInactive(
   1481        aCommandBuilder, aBuilder, aResources, aGroup, item, aSc, &invisible);
   1482  }
   1483  return invalidated;
   1484 }
   1485 
   1486 bool Grouper::ConstructItemInsideInactive(
   1487    WebRenderCommandBuilder* aCommandBuilder, wr::DisplayListBuilder& aBuilder,
   1488    wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup,
   1489    nsDisplayItem* aItem, const StackingContextHelper& aSc,
   1490    bool* aOutIsInvisible) {
   1491  nsDisplayList* children = aItem->GetChildren();
   1492  BlobItemData* data = GetBlobItemDataForGroup(aItem, aGroup);
   1493 
   1494  /* mInvalid unfortunately persists across paints. Clear it so that if we don't
   1495   * set it to 'true' we ensure that we're not using the value from the last
   1496   * time that we painted */
   1497  data->mInvalid = false;
   1498  data->mInvisible = aItem->IsInvisible();
   1499  *aOutIsInvisible = data->mInvisible;
   1500 
   1501  // we compute the geometry change here because we have the transform around
   1502  // still
   1503  bool invalidated = aGroup->ComputeGeometryChange(aItem, data, mTransform,
   1504                                                   mDisplayListBuilder);
   1505 
   1506  // Temporarily restrict the image bounds to the bounds of the container so
   1507  // that clipped children within the container know about the clip. This
   1508  // ensures that the bounds passed to FlushItem are contained in the bounds of
   1509  // the clip so that we don't include items in the recording without including
   1510  // their corresponding clipping items.
   1511  auto oldClippedImageBounds = aGroup->mClippedImageBounds;
   1512  aGroup->mClippedImageBounds =
   1513      aGroup->mClippedImageBounds.Intersect(data->mRect);
   1514 
   1515  if (aItem->GetType() == DisplayItemType::TYPE_FILTER) {
   1516    // If ConstructGroupInsideInactive finds any change, we invalidate the
   1517    // entire container item. This is needed because blob merging requires the
   1518    // entire item to be within the invalid region.
   1519    Matrix m = mTransform;
   1520    mTransform = Matrix();
   1521    sIndent++;
   1522    if (ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources,
   1523                                     aGroup, children, aSc)) {
   1524      data->mInvalid = true;
   1525      aGroup->InvalidateRect(data->mRect);
   1526      invalidated = true;
   1527    }
   1528    sIndent--;
   1529    mTransform = m;
   1530  } else if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
   1531    Matrix m = mTransform;
   1532    nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(aItem);
   1533    const Matrix4x4Flagged& t = transformItem->GetTransform();
   1534    Matrix t2d;
   1535    bool is2D = t.CanDraw2D(&t2d);
   1536    if (!is2D) {
   1537      // If ConstructGroupInsideInactive finds any change, we invalidate the
   1538      // entire container item. This is needed because blob merging requires the
   1539      // entire item to be within the invalid region.
   1540      mTransform = Matrix();
   1541      sIndent++;
   1542      if (ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources,
   1543                                       aGroup, children, aSc)) {
   1544        data->mInvalid = true;
   1545        aGroup->InvalidateRect(data->mRect);
   1546        invalidated = true;
   1547      }
   1548      sIndent--;
   1549    } else {
   1550      GP("t2d: %f %f\n", t2d._31, t2d._32);
   1551      mTransform.PreMultiply(t2d);
   1552      GP("mTransform: %f %f\n", mTransform._31, mTransform._32);
   1553      sIndent++;
   1554      invalidated |= ConstructGroupInsideInactive(
   1555          aCommandBuilder, aBuilder, aResources, aGroup, children, aSc);
   1556      sIndent--;
   1557    }
   1558    mTransform = m;
   1559  } else if (children) {
   1560    sIndent++;
   1561    invalidated |= ConstructGroupInsideInactive(
   1562        aCommandBuilder, aBuilder, aResources, aGroup, children, aSc);
   1563    sIndent--;
   1564  }
   1565 
   1566  GP("Including %s of %d\n", aItem->Name(), aGroup->mDisplayItems.Count());
   1567  aGroup->mClippedImageBounds = oldClippedImageBounds;
   1568  return invalidated;
   1569 }
   1570 
   1571 /* This is just a copy of nsRect::ScaleToOutsidePixels with an offset added in.
   1572 * The offset is applied just before the rounding. It's in the scaled space. */
   1573 static mozilla::LayerIntRect ScaleToOutsidePixelsOffset(
   1574    nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel,
   1575    LayerPoint aOffset) {
   1576  mozilla::LayerIntRect rect;
   1577  rect.SetNonEmptyBox(
   1578      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x, float(aAppUnitsPerPixel)) *
   1579                       aXScale +
   1580                   aOffset.x),
   1581      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y, float(aAppUnitsPerPixel)) *
   1582                       aYScale +
   1583                   aOffset.y),
   1584      NSToIntCeil(
   1585          NSAppUnitsToFloatPixels(aRect.XMost(), float(aAppUnitsPerPixel)) *
   1586              aXScale +
   1587          aOffset.x),
   1588      NSToIntCeil(
   1589          NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) *
   1590              aYScale +
   1591          aOffset.y));
   1592  return rect;
   1593 }
   1594 
   1595 /* This function is the same as the above except that it rounds to the
   1596 * nearest instead of rounding out. We use it for attempting to compute the
   1597 * actual pixel bounds of opaque items */
   1598 static mozilla::gfx::IntRect ScaleToNearestPixelsOffset(
   1599    nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel,
   1600    LayerPoint aOffset) {
   1601  mozilla::gfx::IntRect rect;
   1602  rect.SetNonEmptyBox(
   1603      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x, float(aAppUnitsPerPixel)) *
   1604                       aXScale +
   1605                   aOffset.x + 0.5),
   1606      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y, float(aAppUnitsPerPixel)) *
   1607                       aYScale +
   1608                   aOffset.y + 0.5),
   1609      NSToIntFloor(
   1610          NSAppUnitsToFloatPixels(aRect.XMost(), float(aAppUnitsPerPixel)) *
   1611              aXScale +
   1612          aOffset.x + 0.5),
   1613      NSToIntFloor(
   1614          NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) *
   1615              aYScale +
   1616          aOffset.y + 0.5));
   1617  return rect;
   1618 }
   1619 
   1620 RenderRootStateManager* WebRenderCommandBuilder::GetRenderRootStateManager() {
   1621  return mManager->GetRenderRootStateManager();
   1622 }
   1623 
   1624 void WebRenderCommandBuilder::DoGroupingForDisplayList(
   1625    nsDisplayList* aList, nsDisplayItem* aWrappingItem,
   1626    nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
   1627    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources) {
   1628  if (!aList->GetBottom()) {
   1629    return;
   1630  }
   1631 
   1632  GP("DoGroupingForDisplayList\n");
   1633 
   1634  mClipManager.BeginList(aSc);
   1635  mHitTestInfoManager.Reset();
   1636  Grouper g(mClipManager);
   1637 
   1638  int32_t appUnitsPerDevPixel =
   1639      aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   1640 
   1641  g.mDisplayListBuilder = aDisplayListBuilder;
   1642  RefPtr<WebRenderGroupData> groupData =
   1643      CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
   1644 
   1645  nsRect groupBounds =
   1646      aWrappingItem->GetUntransformedBounds(aDisplayListBuilder);
   1647  DIGroup& group = groupData->mSubGroup;
   1648 
   1649  auto scale = aSc.GetInheritedScale();
   1650  GP("Inherited scale %f %f\n", scale.xScale, scale.yScale);
   1651 
   1652  auto trans =
   1653      ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
   1654  auto snappedTrans = LayerIntPoint::Floor(trans);
   1655  LayerPoint residualOffset = trans - snappedTrans;
   1656 
   1657  auto layerBounds =
   1658      ScaleToOutsidePixelsOffset(groupBounds, scale.xScale, scale.yScale,
   1659                                 appUnitsPerDevPixel, residualOffset);
   1660 
   1661  const nsRect& untransformedPaintRect =
   1662      aWrappingItem->GetUntransformedPaintRect();
   1663 
   1664  auto visibleRect = ScaleToOutsidePixelsOffset(
   1665                         untransformedPaintRect, scale.xScale, scale.yScale,
   1666                         appUnitsPerDevPixel, residualOffset)
   1667                         .Intersect(layerBounds);
   1668 
   1669  GP("LayerBounds: %d %d %d %d\n", layerBounds.x, layerBounds.y,
   1670     layerBounds.width, layerBounds.height);
   1671  GP("VisibleRect: %d %d %d %d\n", visibleRect.x, visibleRect.y,
   1672     visibleRect.width, visibleRect.height);
   1673 
   1674  GP("Inherited scale %f %f\n", scale.xScale, scale.yScale);
   1675 
   1676  group.mInvalidRect.SetEmpty();
   1677  if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel ||
   1678      group.mScale != scale || group.mResidualOffset != residualOffset) {
   1679    GP("Property change. Deleting blob\n");
   1680 
   1681    if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel) {
   1682      GP(" App unit change %d -> %d\n", group.mAppUnitsPerDevPixel,
   1683         appUnitsPerDevPixel);
   1684    }
   1685 
   1686    if (group.mScale != scale) {
   1687      GP(" Scale %f %f -> %f %f\n", group.mScale.xScale, group.mScale.yScale,
   1688         scale.xScale, scale.yScale);
   1689    }
   1690 
   1691    if (group.mResidualOffset != residualOffset) {
   1692      GP(" Residual Offset %f %f -> %f %f\n", group.mResidualOffset.x.value,
   1693         group.mResidualOffset.y.value, residualOffset.x.value,
   1694         residualOffset.y.value);
   1695    }
   1696 
   1697    group.ClearItems();
   1698    group.ClearImageKey(mManager->GetRenderRootStateManager());
   1699  }
   1700 
   1701  ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
   1702  if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) {
   1703    scrollId = asr->GetNearestScrollASRViewId();
   1704  }
   1705 
   1706  g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   1707  group.mResidualOffset = residualOffset;
   1708  group.mLayerBounds = layerBounds;
   1709  group.mVisibleRect = visibleRect;
   1710  group.mActualBounds = LayerIntRect();
   1711  group.mHitTestBounds = LayerIntRect();
   1712  group.mPreservedRect = group.mVisibleRect.Intersect(group.mLastVisibleRect);
   1713  group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   1714  group.mClippedImageBounds = layerBounds;
   1715 
   1716  g.mTransform =
   1717      Matrix::Scaling(scale).PostTranslate(residualOffset.x, residualOffset.y);
   1718  group.mScale = scale;
   1719  group.mScrollId = scrollId;
   1720  g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group,
   1721                    aList, aWrappingItem, aSc);
   1722  mClipManager.EndList(aSc);
   1723 }
   1724 
   1725 WebRenderCommandBuilder::WebRenderCommandBuilder(
   1726    WebRenderLayerManager* aManager)
   1727    : mManager(aManager),
   1728      mLastAsr(nullptr),
   1729      mBuilderDumpIndex(0),
   1730      mDumpIndent(0),
   1731      mApzEnabled(true),
   1732      mComputingOpaqueRegion(XRE_IsParentProcess()),
   1733      mDoGrouping(false),
   1734      mContainsSVGGroup(false) {}
   1735 
   1736 void WebRenderCommandBuilder::Destroy() {
   1737  mLastCanvasDatas.Clear();
   1738  ClearCachedResources();
   1739 }
   1740 
   1741 void WebRenderCommandBuilder::EmptyTransaction() {
   1742  // We need to update canvases that might have changed.
   1743  for (RefPtr<WebRenderCanvasData> canvasData : mLastCanvasDatas) {
   1744    WebRenderCanvasRendererAsync* canvas = canvasData->GetCanvasRenderer();
   1745    if (canvas) {
   1746      canvas->UpdateCompositableClientForEmptyTransaction();
   1747    }
   1748  }
   1749 }
   1750 
   1751 bool WebRenderCommandBuilder::NeedsEmptyTransaction() {
   1752  return !mLastCanvasDatas.IsEmpty();
   1753 }
   1754 
   1755 void WebRenderCommandBuilder::BuildWebRenderCommands(
   1756    wr::DisplayListBuilder& aBuilder,
   1757    wr::IpcResourceUpdateQueue& aResourceUpdates, nsDisplayList* aDisplayList,
   1758    nsDisplayListBuilder* aDisplayListBuilder, WebRenderScrollData& aScrollData,
   1759    WrFiltersHolder&& aFilters) {
   1760  AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_WRDisplayList);
   1761 
   1762  StackingContextHelper sc;
   1763  aScrollData = WebRenderScrollData(mManager, aDisplayListBuilder);
   1764  MOZ_ASSERT(mLayerScrollData.empty());
   1765  mClipManager.BeginBuild(mManager, aBuilder);
   1766  mHitTestInfoManager.Reset();
   1767 
   1768  mBuilderDumpIndex = 0;
   1769  mLastCanvasDatas.Clear();
   1770  mLastAsr = nullptr;
   1771  mContainsSVGGroup = false;
   1772  MOZ_ASSERT(mDumpIndent == 0);
   1773 
   1774  {
   1775    wr::StackingContextParams params;
   1776    params.mRootReferenceFrame = aDisplayListBuilder->RootReferenceFrame();
   1777    params.mFilters = std::move(aFilters.filters);
   1778    params.mFilterDatas = std::move(aFilters.filter_datas);
   1779    params.clip =
   1780        wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
   1781 
   1782    StackingContextHelper pageRootSc(sc, nullptr, nullptr, nullptr, aBuilder,
   1783                                     params);
   1784    if (ShouldDumpDisplayList(aDisplayListBuilder)) {
   1785      mBuilderDumpIndex =
   1786          aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   1787    }
   1788    CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr,
   1789                                           aDisplayListBuilder, pageRootSc,
   1790                                           aBuilder, aResourceUpdates);
   1791  }
   1792 
   1793  // Make a "root" layer data that has everything else as descendants
   1794  mLayerScrollData.emplace_back();
   1795  mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1);
   1796  auto callback =
   1797      [&aScrollData](ScrollableLayerGuid::ViewID aScrollId) -> bool {
   1798    return aScrollData.HasMetadataFor(aScrollId).isSome();
   1799  };
   1800  Maybe<ScrollMetadata> rootMetadata =
   1801      nsLayoutUtils::GetRootMetadata(aDisplayListBuilder, mManager, callback);
   1802  if (rootMetadata) {
   1803    // Put the fallback root metadata on the rootmost layer that is
   1804    // a matching async zoom container, or the root layer that we just
   1805    // created above.
   1806    size_t rootMetadataTarget = mLayerScrollData.size() - 1;
   1807    for (size_t i = rootMetadataTarget; i > 0; i--) {
   1808      if (auto zoomContainerId =
   1809              mLayerScrollData[i - 1].GetAsyncZoomContainerId()) {
   1810        if (*zoomContainerId == rootMetadata->GetMetrics().GetScrollId()) {
   1811          rootMetadataTarget = i - 1;
   1812          break;
   1813        }
   1814      }
   1815    }
   1816    mLayerScrollData[rootMetadataTarget].AppendScrollMetadata(
   1817        aScrollData, rootMetadata.ref());
   1818  }
   1819 
   1820  // Append the WebRenderLayerScrollData items into WebRenderScrollData
   1821  // in reverse order, from topmost to bottommost. This is in keeping with
   1822  // the semantics of WebRenderScrollData.
   1823  for (auto it = mLayerScrollData.rbegin(); it != mLayerScrollData.rend();
   1824       it++) {
   1825    aScrollData.AddLayerData(std::move(*it));
   1826  }
   1827  mLayerScrollData.clear();
   1828  mClipManager.EndBuild();
   1829 
   1830  // Remove the user data those are not displayed on the screen and
   1831  // also reset the data to unused for next transaction.
   1832  RemoveUnusedAndResetWebRenderUserData();
   1833 }
   1834 
   1835 bool WebRenderCommandBuilder::ShouldDumpDisplayList(
   1836    nsDisplayListBuilder* aBuilder) {
   1837  return aBuilder && aBuilder->IsInActiveDocShell() &&
   1838         ((XRE_IsParentProcess() &&
   1839           StaticPrefs::gfx_webrender_debug_dl_dump_parent()) ||
   1840          (XRE_IsContentProcess() &&
   1841           StaticPrefs::gfx_webrender_debug_dl_dump_content()));
   1842 }
   1843 
   1844 void WebRenderCommandBuilder::CreateWebRenderCommands(
   1845    nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
   1846    mozilla::wr::IpcResourceUpdateQueue& aResources,
   1847    const StackingContextHelper& aSc,
   1848    nsDisplayListBuilder* aDisplayListBuilder) {
   1849  mHitTestInfoManager.ProcessItem(aItem, aBuilder, aDisplayListBuilder);
   1850  if (aItem->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
   1851    // The hit test information was processed above.
   1852    return;
   1853  }
   1854 
   1855  auto* item = aItem->AsPaintedDisplayItem();
   1856  MOZ_RELEASE_ASSERT(item, "Tried to paint item that cannot be painted");
   1857 
   1858  if (aBuilder.ReuseItem(item)) {
   1859    // No further processing should be needed, since the item was reused.
   1860    return;
   1861  }
   1862 
   1863  RenderRootStateManager* manager = mManager->GetRenderRootStateManager();
   1864 
   1865  // Note: this call to CreateWebRenderCommands can recurse back into
   1866  // this function if the |item| is a wrapper for a sublist.
   1867  const bool createdWRCommands = aItem->CreateWebRenderCommands(
   1868      aBuilder, aResources, aSc, manager, aDisplayListBuilder);
   1869 
   1870  if (!createdWRCommands) {
   1871    PushItemAsImage(aItem, aBuilder, aResources, aSc, aDisplayListBuilder);
   1872  }
   1873 }
   1874 
   1875 // A helper struct to store information needed when creating a new
   1876 // WebRenderLayerScrollData in CreateWebRenderCommandsFromDisplayList().
   1877 // This information is gathered before the recursion, and then used to
   1878 // emit the new layer after the recursion.
   1879 struct NewLayerData {
   1880  size_t mLayerCountBeforeRecursing = 0;
   1881  const ActiveScrolledRoot* mStopAtAsr = nullptr;
   1882 
   1883  // Information pertaining to the deferred transform.
   1884  nsDisplayTransform* mDeferredItem = nullptr;
   1885  ScrollableLayerGuid::ViewID mDeferredId = ScrollableLayerGuid::NULL_SCROLL_ID;
   1886  bool mTransformShouldGetOwnLayer = false;
   1887 
   1888  void ComputeDeferredTransformInfo(const StackingContextHelper& aSc,
   1889                                    nsDisplayItem* aItem) {
   1890    // See the comments on StackingContextHelper::mDeferredTransformItem
   1891    // for an overview of what deferred transforms are.
   1892    // In the case where we deferred a transform, but have a child display
   1893    // item with a different ASR than the deferred transform item, we cannot
   1894    // put the transform on the WebRenderLayerScrollData item for the child.
   1895    // We cannot do this because it will not conform to APZ's expectations
   1896    // with respect to how the APZ tree ends up structured. In particular,
   1897    // the GetTransformToThis() for the child APZ (which is created for the
   1898    // child item's ASR) will not include the transform when we actually do
   1899    // want it to.
   1900    // When we run into this scenario, we solve it by creating two
   1901    // WebRenderLayerScrollData items; one that just holds the transform,
   1902    // that we deferred, and a child WebRenderLayerScrollData item that
   1903    // holds the scroll metadata for the child's ASR.
   1904    mDeferredItem = aSc.GetDeferredTransformItem();
   1905    if (mDeferredItem) {
   1906      // It's possible the transform's ASR is not only an ancestor of
   1907      // the item's ASR, but an ancestor of stopAtAsr. In such cases,
   1908      // don't use the transform at all at this level (it would be
   1909      // scrolled by stopAtAsr which is incorrect). The transform will
   1910      // instead be emitted as part of the ancestor WebRenderLayerScrollData
   1911      // node (the one with stopAtAsr as its item ASR), or one of its
   1912      // ancetors in turn.
   1913      if (ActiveScrolledRoot::IsProperAncestor(
   1914              mDeferredItem->GetActiveScrolledRoot(), mStopAtAsr)) {
   1915        mDeferredItem = nullptr;
   1916      }
   1917    }
   1918    if (mDeferredItem) {
   1919      if (const auto* asr = mDeferredItem->GetActiveScrolledRoot()) {
   1920        mDeferredId = asr->GetNearestScrollASRViewId();
   1921      }
   1922      if (mDeferredItem->GetActiveScrolledRoot() !=
   1923          aItem->GetActiveScrolledRoot()) {
   1924        mTransformShouldGetOwnLayer = true;
   1925      } else if (aItem->GetType() == DisplayItemType::TYPE_SCROLL_INFO_LAYER) {
   1926        // A scroll info layer has its own scroll id that's not reflected
   1927        // in item->GetActiveScrolledRoot(), but will be added to the
   1928        // WebRenderLayerScrollData node, so it needs to be treated as
   1929        // having a distinct ASR from the deferred transform item.
   1930        mTransformShouldGetOwnLayer = true;
   1931      }
   1932    }
   1933  }
   1934 };
   1935 
   1936 static Maybe<nsPoint> AllowComputingOpaqueRegionAcross(
   1937    nsDisplayItem* aWrappingItem, nsDisplayListBuilder* aBuilder) {
   1938  MOZ_ASSERT(aWrappingItem);
   1939  if (aWrappingItem->GetType() != DisplayItemType::TYPE_TRANSFORM) {
   1940    return {};
   1941  }
   1942  auto* transformItem = static_cast<nsDisplayTransform*>(aWrappingItem);
   1943  if (transformItem->MayBeAnimated(aBuilder)) {
   1944    return {};
   1945  }
   1946  const auto& transform = transformItem->GetTransform();
   1947  if (!transform.Is2D()) {
   1948    return {};
   1949  }
   1950  const auto transform2d = transform.GetMatrix().As2D();
   1951  if (!transform2d.IsTranslation()) {
   1952    return {};
   1953  }
   1954  return Some(LayoutDevicePoint::ToAppUnits(
   1955      LayoutDevicePoint::FromUnknownPoint(transform2d.GetTranslation()),
   1956      transformItem->Frame()->PresContext()->AppUnitsPerDevPixel()));
   1957 }
   1958 
   1959 struct MOZ_STACK_CLASS WebRenderCommandBuilder::AutoOpaqueRegionStateTracker {
   1960  WebRenderCommandBuilder& mBuilder;
   1961  const bool mWasComputingOpaqueRegion;
   1962  bool mThroughWrapper = false;
   1963 
   1964  AutoOpaqueRegionStateTracker(WebRenderCommandBuilder& aBuilder,
   1965                               nsDisplayListBuilder* aDlBuilder,
   1966                               nsDisplayItem* aWrappingItem)
   1967      : mBuilder(aBuilder),
   1968        mWasComputingOpaqueRegion(aBuilder.mComputingOpaqueRegion) {
   1969    if (!mBuilder.mComputingOpaqueRegion || !aWrappingItem) {
   1970      return;
   1971    }
   1972    Maybe<nsPoint> offset =
   1973        AllowComputingOpaqueRegionAcross(aWrappingItem, aDlBuilder);
   1974    if (!offset) {
   1975      aBuilder.mComputingOpaqueRegion = false;
   1976    } else {
   1977      mThroughWrapper = true;
   1978      aBuilder.mOpaqueRegionWrappers.AppendElement(
   1979          std::make_pair(aWrappingItem, *offset));
   1980    }
   1981  }
   1982 
   1983  ~AutoOpaqueRegionStateTracker() {
   1984    if (!mWasComputingOpaqueRegion) {
   1985      return;
   1986    }
   1987    if (mThroughWrapper) {
   1988      mBuilder.mOpaqueRegionWrappers.RemoveLastElement();
   1989    }
   1990    mBuilder.mComputingOpaqueRegion = mWasComputingOpaqueRegion;
   1991  }
   1992 };
   1993 
   1994 void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
   1995    nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem,
   1996    nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
   1997    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
   1998    bool aNewClipList) {
   1999  if (mDoGrouping) {
   2000    MOZ_RELEASE_ASSERT(
   2001        aWrappingItem,
   2002        "Only the root list should have a null wrapping item, and mDoGrouping "
   2003        "should never be true for the root list.");
   2004    GP("actually entering the grouping code\n");
   2005    DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder,
   2006                             aSc, aBuilder, aResources);
   2007    return;
   2008  }
   2009 
   2010  const bool dumpEnabled = ShouldDumpDisplayList(aDisplayListBuilder);
   2011  if (dumpEnabled) {
   2012    // If we're inside a nested display list, print the WR DL items from the
   2013    // wrapper item before we start processing the nested items.
   2014    mBuilderDumpIndex =
   2015        aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   2016  }
   2017 
   2018  FlattenedDisplayListIterator iter(aDisplayListBuilder, aDisplayList);
   2019  if (!iter.HasNext()) {
   2020    return;
   2021  }
   2022 
   2023  mDumpIndent++;
   2024  if (aNewClipList) {
   2025    mClipManager.BeginList(aSc);
   2026  }
   2027 
   2028  AutoOpaqueRegionStateTracker tracker(*this, aDisplayListBuilder,
   2029                                       aWrappingItem);
   2030  do {
   2031    nsDisplayItem* item = iter.GetNextItem();
   2032 
   2033    const DisplayItemType itemType = item->GetType();
   2034 
   2035    // If this is a new (not retained/reused) item, then we need to disable
   2036    // the display item cache for descendants, since it's possible that some of
   2037    // them got cached with a flattened opacity values., which may no longer be
   2038    // applied.
   2039    Maybe<AutoDisplayItemCacheSuppressor> cacheSuppressor;
   2040 
   2041    if (itemType == DisplayItemType::TYPE_OPACITY) {
   2042      nsDisplayOpacity* opacity = static_cast<nsDisplayOpacity*>(item);
   2043 
   2044      if (!opacity->IsReused()) {
   2045        cacheSuppressor.emplace(aBuilder.GetDisplayItemCache());
   2046      }
   2047 
   2048      if (opacity->CanApplyOpacityToChildren(
   2049              mManager->GetRenderRootStateManager()->LayerManager(),
   2050              aDisplayListBuilder, aBuilder.GetInheritedOpacity())) {
   2051        // If all our children support handling the opacity directly, then push
   2052        // the opacity and clip onto the builder and skip creating a stacking
   2053        // context.
   2054        float oldOpacity = aBuilder.GetInheritedOpacity();
   2055        const DisplayItemClipChain* oldClip = aBuilder.GetInheritedClipChain();
   2056        aBuilder.SetInheritedOpacity(oldOpacity * opacity->GetOpacity());
   2057        aBuilder.PushInheritedClipChain(aDisplayListBuilder,
   2058                                        opacity->GetClipChain());
   2059 
   2060        CreateWebRenderCommandsFromDisplayList(opacity->GetChildren(), item,
   2061                                               aDisplayListBuilder, aSc,
   2062                                               aBuilder, aResources, false);
   2063 
   2064        aBuilder.SetInheritedOpacity(oldOpacity);
   2065        aBuilder.SetInheritedClipChain(oldClip);
   2066        continue;
   2067      }
   2068    }
   2069 
   2070    // If this is an unscrolled background item, in the root display list
   2071    // for the parent process, consider doing opaque checks.
   2072    if (mComputingOpaqueRegion &&
   2073        (itemType == DisplayItemType::TYPE_BACKGROUND_COLOR ||
   2074         itemType == DisplayItemType::TYPE_SOLID_COLOR ||
   2075         itemType == DisplayItemType::TYPE_BACKGROUND) &&
   2076        !item->GetActiveScrolledRoot()) {
   2077      bool snap;
   2078      nsRegion opaque = item->GetOpaqueRegion(aDisplayListBuilder, &snap);
   2079      if (opaque.GetNumRects() == 1) {
   2080        nsRect result =
   2081            item->GetClip().ApproximateIntersectInward(opaque.GetBounds());
   2082        if (!result.IsEmpty()) {
   2083          for (auto& [item, offset] : Reversed(mOpaqueRegionWrappers)) {
   2084            result =
   2085                item->GetClip().ApproximateIntersectInward(result + offset);
   2086            if (result.IsEmpty()) {
   2087              break;
   2088            }
   2089          }
   2090          if (!result.IsEmpty()) {
   2091            aDisplayListBuilder->AddWindowOpaqueRegion(item->Frame(), result);
   2092          }
   2093        }
   2094      }
   2095    }
   2096 
   2097    AutoRestore<bool> restoreApzEnabled(mApzEnabled);
   2098    mApzEnabled = mApzEnabled && mManager->AsyncPanZoomEnabled() &&
   2099                  itemType != DisplayItemType::TYPE_VT_CAPTURE;
   2100 
   2101    Maybe<NewLayerData> newLayerData;
   2102    if (mApzEnabled) {
   2103      // For some types of display items we want to force a new
   2104      // WebRenderLayerScrollData object, to ensure we preserve the APZ-relevant
   2105      // data that is in the display item.
   2106      if (item->UpdateScrollData(nullptr, nullptr)) {
   2107        newLayerData = Some(NewLayerData());
   2108      }
   2109 
   2110      // Anytime the ASR changes we also want to force a new layer data because
   2111      // the stack of scroll metadata is going to be different for this
   2112      // display item than previously, so we can't squash the display items
   2113      // into the same "layer".
   2114      const ActiveScrolledRoot* asr = item->GetActiveScrolledRoot();
   2115      if (asr != mLastAsr) {
   2116        mLastAsr = asr;
   2117        newLayerData = Some(NewLayerData());
   2118      }
   2119 
   2120      // Refer to the comment on StackingContextHelper::mDeferredTransformItem
   2121      // for an overview of what this is about. This bit of code applies to the
   2122      // case where we are deferring a transform item, and we then need to defer
   2123      // another transform with a different ASR. In such a case we cannot just
   2124      // merge the deferred transforms, but need to force a new
   2125      // WebRenderLayerScrollData item to flush the old deferred transform, so
   2126      // that we can then start deferring the new one.
   2127      if (!newLayerData && item->CreatesStackingContextHelper() &&
   2128          aSc.GetDeferredTransformItem() &&
   2129          aSc.GetDeferredTransformItem()->GetActiveScrolledRoot() != asr) {
   2130        newLayerData = Some(NewLayerData());
   2131      }
   2132 
   2133      // If we're going to create a new layer data for this item, stash the
   2134      // ASR so that if we recurse into a sublist they will know where to stop
   2135      // walking up their ASR chain when building scroll metadata.
   2136      if (newLayerData) {
   2137        newLayerData->mLayerCountBeforeRecursing = mLayerScrollData.size();
   2138        newLayerData->mStopAtAsr =
   2139            mAsrStack.empty() ? nullptr : mAsrStack.back();
   2140        newLayerData->mStopAtAsr = ActiveScrolledRoot::LowestCommonAncestor(
   2141            asr, newLayerData->mStopAtAsr);
   2142        newLayerData->ComputeDeferredTransformInfo(aSc, item);
   2143 
   2144        // Our children's |stopAtAsr| must not be an ancestor of our
   2145        // |stopAtAsr|, otherwise we could get cyclic scroll metadata
   2146        // annotations.
   2147        MOZ_ASSERT(
   2148            ActiveScrolledRoot::IsAncestor(newLayerData->mStopAtAsr, asr));
   2149        const ActiveScrolledRoot* stopAtAsrForChildren = asr;
   2150        // Additionally, while unusual and probably indicative of a poorly
   2151        // behaved display list, it's possible to have a deferred transform item
   2152        // which we will emit as its own layer on the way out of the recursion,
   2153        // whose ASR (let's call it T) is a *descendant* of the current item's
   2154        // ASR. In such cases, make sure our children have stopAtAsr=T,
   2155        // otherwise ASRs in the range [T, asr) may be emitted in duplicate,
   2156        // leading again to cylic scroll metadata annotations.
   2157        if (newLayerData->mTransformShouldGetOwnLayer) {
   2158          stopAtAsrForChildren = ActiveScrolledRoot::PickDescendant(
   2159              stopAtAsrForChildren,
   2160              newLayerData->mDeferredItem->GetActiveScrolledRoot());
   2161        }
   2162        mAsrStack.push_back(stopAtAsrForChildren);
   2163 
   2164        // If we're going to emit a deferred transform onto this layer,
   2165        // clear the deferred transform from the StackingContextHelper
   2166        // while we are building the subtree of descendant layers.
   2167        // This ensures that the deferred transform is not applied in
   2168        // duplicate to any of our descendant layers.
   2169        if (newLayerData->mDeferredItem) {
   2170          aSc.ClearDeferredTransformItem();
   2171        }
   2172      }
   2173    }
   2174 
   2175    // This is where we emulate the clip/scroll stack that was previously
   2176    // implemented on the WR display list side.
   2177    auto spaceAndClipChain = mClipManager.SwitchItem(aDisplayListBuilder, item);
   2178    wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
   2179 
   2180    {  // scope restoreDoGrouping
   2181      AutoRestore<bool> restoreDoGrouping(mDoGrouping);
   2182      if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
   2183        // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
   2184        // display items (like animated transforms / opacity) share the same
   2185        // animated geometry root, so we can combine subsequent items of that
   2186        // type into the same image.
   2187        mContainsSVGGroup = mDoGrouping = true;
   2188        GP("attempting to enter the grouping code\n");
   2189      }
   2190 
   2191      if (dumpEnabled) {
   2192        std::stringstream ss;
   2193        nsIFrame::PrintDisplayItem(aDisplayListBuilder, item, ss,
   2194                                   static_cast<uint32_t>(mDumpIndent));
   2195        printf_stderr("%s", ss.str().c_str());
   2196      }
   2197 
   2198      CreateWebRenderCommands(item, aBuilder, aResources, aSc,
   2199                              aDisplayListBuilder);
   2200 
   2201      if (dumpEnabled) {
   2202        mBuilderDumpIndex =
   2203            aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   2204      }
   2205    }
   2206 
   2207    if (newLayerData) {
   2208      // Pop the thing we pushed before the recursion, so the topmost item on
   2209      // the stack is enclosing display item's ASR (or the stack is empty)
   2210      mAsrStack.pop_back();
   2211 
   2212      if (newLayerData->mDeferredItem) {
   2213        aSc.RestoreDeferredTransformItem(newLayerData->mDeferredItem);
   2214      }
   2215 
   2216      const ActiveScrolledRoot* stopAtAsr = newLayerData->mStopAtAsr;
   2217 
   2218      int32_t descendants =
   2219          mLayerScrollData.size() - newLayerData->mLayerCountBeforeRecursing;
   2220 
   2221      nsDisplayTransform* deferred = newLayerData->mDeferredItem;
   2222      ScrollableLayerGuid::ViewID deferredId = newLayerData->mDeferredId;
   2223 
   2224      if (newLayerData->mTransformShouldGetOwnLayer) {
   2225        // This creates the child WebRenderLayerScrollData for |item|, but
   2226        // omits the transform (hence the Nothing() as the last argument to
   2227        // Initialize(...)). We also need to make sure that the ASR from
   2228        // the deferred transform item is not on this node, so we use that
   2229        // ASR as the "stop at" ASR for this WebRenderLayerScrollData.
   2230        mLayerScrollData.emplace_back();
   2231        mLayerScrollData.back().Initialize(
   2232            mManager->GetScrollData(), item, descendants,
   2233            deferred->GetActiveScrolledRoot(), Nothing(),
   2234            ScrollableLayerGuid::NULL_SCROLL_ID);
   2235 
   2236        // The above WebRenderLayerScrollData will also be a descendant of
   2237        // the transform-holding WebRenderLayerScrollData we create below.
   2238        descendants++;
   2239 
   2240        // This creates the WebRenderLayerScrollData for the deferred
   2241        // transform item. This holds the transform matrix and the remaining
   2242        // ASRs needed to complete the ASR chain (i.e. the ones from the
   2243        // stopAtAsr down to the deferred transform item's ASR, which must be
   2244        // "between" stopAtAsr and |item|'s ASR in the ASR tree).
   2245        mLayerScrollData.emplace_back();
   2246        mLayerScrollData.back().Initialize(
   2247            mManager->GetScrollData(), deferred, descendants, stopAtAsr,
   2248            aSc.GetDeferredTransformMatrix(), deferredId);
   2249      } else {
   2250        // This is the "simple" case where we don't need to create two
   2251        // WebRenderLayerScrollData items; we can just create one that also
   2252        // holds the deferred transform matrix, if any.
   2253        mLayerScrollData.emplace_back();
   2254        mLayerScrollData.back().Initialize(
   2255            mManager->GetScrollData(), item, descendants, stopAtAsr,
   2256            deferred ? aSc.GetDeferredTransformMatrix() : Nothing(),
   2257            deferredId);
   2258      }
   2259    }
   2260  } while (iter.HasNext());
   2261 
   2262  mDumpIndent--;
   2263  if (aNewClipList) {
   2264    mClipManager.EndList(aSc);
   2265  }
   2266 }
   2267 
   2268 void WebRenderCommandBuilder::PushOverrideForASR(
   2269    const ActiveScrolledRoot* aASR, const wr::WrSpatialId& aSpatialId) {
   2270  mClipManager.PushOverrideForASR(aASR, aSpatialId);
   2271 }
   2272 
   2273 void WebRenderCommandBuilder::PopOverrideForASR(
   2274    const ActiveScrolledRoot* aASR) {
   2275  mClipManager.PopOverrideForASR(aASR);
   2276 }
   2277 
   2278 static wr::WrRotation ToWrRotation(VideoRotation aRotation) {
   2279  switch (aRotation) {
   2280    case VideoRotation::kDegree_0:
   2281      return wr::WrRotation::Degree0;
   2282    case VideoRotation::kDegree_90:
   2283      return wr::WrRotation::Degree90;
   2284    case VideoRotation::kDegree_180:
   2285      return wr::WrRotation::Degree180;
   2286    case VideoRotation::kDegree_270:
   2287      return wr::WrRotation::Degree270;
   2288  }
   2289  return wr::WrRotation::Degree0;
   2290 }
   2291 
   2292 Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageKey(
   2293    nsDisplayItem* aItem, ImageContainer* aContainer,
   2294    mozilla::wr::DisplayListBuilder& aBuilder,
   2295    mozilla::wr::IpcResourceUpdateQueue& aResources,
   2296    mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc,
   2297    gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds) {
   2298  RefPtr<WebRenderImageData> imageData =
   2299      CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem);
   2300  MOZ_ASSERT(imageData);
   2301 
   2302  if (aContainer->IsAsync()) {
   2303    MOZ_ASSERT(aAsyncImageBounds);
   2304 
   2305    LayoutDeviceRect rect = aAsyncImageBounds.value();
   2306    LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size());
   2307    // TODO!
   2308    // We appear to be using the image bridge for a lot (most/all?) of
   2309    // layers-free image handling and that breaks frame consistency.
   2310    imageData->CreateAsyncImageWebRenderCommands(
   2311        aBuilder, aContainer, aSc, rect, scBounds,
   2312        ToWrRotation(aContainer->GetRotation()), aRendering,
   2313        wr::MixBlendMode::Normal, !aItem->BackfaceIsHidden());
   2314    return Nothing();
   2315  }
   2316 
   2317  AutoLockImage autoLock(aContainer);
   2318  if (!autoLock.HasImage()) {
   2319    return Nothing();
   2320  }
   2321  mozilla::layers::Image* image = autoLock.GetImage();
   2322  aSize = image->GetSize();
   2323 
   2324  return imageData->UpdateImageKey(aContainer, aResources);
   2325 }
   2326 
   2327 bool WebRenderCommandBuilder::PushImage(
   2328    nsDisplayItem* aItem, ImageContainer* aContainer,
   2329    mozilla::wr::DisplayListBuilder& aBuilder,
   2330    mozilla::wr::IpcResourceUpdateQueue& aResources,
   2331    const StackingContextHelper& aSc, const LayoutDeviceRect& aRect,
   2332    const LayoutDeviceRect& aClip) {
   2333  auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
   2334  gfx::IntSize size;
   2335  Maybe<wr::ImageKey> key =
   2336      CreateImageKey(aItem, aContainer, aBuilder, aResources, rendering, aSc,
   2337                     size, Some(aRect));
   2338  if (aContainer->IsAsync()) {
   2339    // Async ImageContainer does not create ImageKey, instead it uses Pipeline.
   2340    MOZ_ASSERT(key.isNothing());
   2341    return true;
   2342  }
   2343  if (!key) {
   2344    return false;
   2345  }
   2346 
   2347  auto r = wr::ToLayoutRect(aRect);
   2348  auto c = wr::ToLayoutRect(aClip);
   2349  aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), false, rendering,
   2350                     key.value());
   2351 
   2352  return true;
   2353 }
   2354 
   2355 Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageProviderKey(
   2356    nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider,
   2357    image::ImgDrawResult aDrawResult,
   2358    mozilla::wr::IpcResourceUpdateQueue& aResources) {
   2359  RefPtr<WebRenderImageProviderData> imageData =
   2360      CreateOrRecycleWebRenderUserData<WebRenderImageProviderData>(aItem);
   2361  MOZ_ASSERT(imageData);
   2362  return imageData->UpdateImageKey(aProvider, aDrawResult, aResources);
   2363 }
   2364 
   2365 bool WebRenderCommandBuilder::PushImageProvider(
   2366    nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider,
   2367    image::ImgDrawResult aDrawResult, mozilla::wr::DisplayListBuilder& aBuilder,
   2368    mozilla::wr::IpcResourceUpdateQueue& aResources,
   2369    const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) {
   2370  Maybe<wr::ImageKey> key =
   2371      CreateImageProviderKey(aItem, aProvider, aDrawResult, aResources);
   2372  if (!key) {
   2373    return false;
   2374  }
   2375 
   2376  bool antialiased = aItem->GetType() == DisplayItemType::TYPE_SVG_GEOMETRY;
   2377 
   2378  auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
   2379  auto r = wr::ToLayoutRect(aRect);
   2380  auto c = wr::ToLayoutRect(aClip);
   2381  aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), antialiased, rendering,
   2382                     key.value());
   2383 
   2384  return true;
   2385 }
   2386 
   2387 static void PaintItemByDrawTarget(nsDisplayItem* aItem, gfx::DrawTarget* aDT,
   2388                                  const LayoutDevicePoint& aOffset,
   2389                                  const IntRect& visibleRect,
   2390                                  nsDisplayListBuilder* aDisplayListBuilder,
   2391                                  const gfx::MatrixScales& aScale,
   2392                                  Maybe<gfx::DeviceColor>& aHighlight) {
   2393  MOZ_ASSERT(aDT && aDT->IsValid());
   2394 
   2395  // XXX Why is this ClearRect() needed?
   2396  aDT->ClearRect(Rect(visibleRect));
   2397  gfxContext context(aDT);
   2398 
   2399  switch (aItem->GetType()) {
   2400    case DisplayItemType::TYPE_SVG_WRAPPER:
   2401    case DisplayItemType::TYPE_MASK: {
   2402      // These items should be handled by other code paths
   2403      MOZ_RELEASE_ASSERT(0);
   2404      break;
   2405    }
   2406    default:
   2407      if (!aItem->AsPaintedDisplayItem()) {
   2408        break;
   2409      }
   2410 
   2411      context.SetMatrix(context.CurrentMatrix().PreScale(aScale).PreTranslate(
   2412          -aOffset.x, -aOffset.y));
   2413      if (aDisplayListBuilder->IsPaintingToWindow()) {
   2414        aItem->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES);
   2415      }
   2416      aItem->AsPaintedDisplayItem()->Paint(aDisplayListBuilder, &context);
   2417      break;
   2418  }
   2419 
   2420  if (aHighlight && aItem->GetType() != DisplayItemType::TYPE_MASK) {
   2421    // Apply highlight fills, if the appropriate prefs are set.
   2422    // We don't do this for masks because we'd be filling the A8 mask surface,
   2423    // which isn't very useful.
   2424    aDT->SetTransform(gfx::Matrix());
   2425    aDT->FillRect(Rect(visibleRect), gfx::ColorPattern(aHighlight.value()));
   2426  }
   2427 }
   2428 
   2429 bool WebRenderCommandBuilder::ComputeInvalidationForDisplayItem(
   2430    nsDisplayListBuilder* aBuilder, const nsPoint& aShift,
   2431    nsDisplayItem* aItem) {
   2432  RefPtr<WebRenderFallbackData> fallbackData =
   2433      CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
   2434 
   2435  nsRect invalid;
   2436  if (!fallbackData->mGeometry || aItem->IsInvalid(invalid)) {
   2437    fallbackData->mGeometry = WrapUnique(aItem->AllocateGeometry(aBuilder));
   2438    return true;
   2439  }
   2440 
   2441  fallbackData->mGeometry->MoveBy(aShift);
   2442  nsRegion combined;
   2443  aItem->ComputeInvalidationRegion(aBuilder, fallbackData->mGeometry.get(),
   2444                                   &combined);
   2445 
   2446  UniquePtr<nsDisplayItemGeometry> geometry;
   2447  if (!combined.IsEmpty() || aItem->NeedsGeometryUpdates()) {
   2448    geometry = WrapUnique(aItem->AllocateGeometry(aBuilder));
   2449  }
   2450 
   2451  fallbackData->mClip.AddOffsetAndComputeDifference(
   2452      aShift, fallbackData->mGeometry->ComputeInvalidationRegion(),
   2453      aItem->GetClip(),
   2454      geometry ? geometry->ComputeInvalidationRegion()
   2455               : fallbackData->mGeometry->ComputeInvalidationRegion(),
   2456      &combined);
   2457 
   2458  if (geometry) {
   2459    fallbackData->mGeometry = std::move(geometry);
   2460  }
   2461  fallbackData->mClip = aItem->GetClip();
   2462 
   2463  if (!combined.IsEmpty()) {
   2464    return true;
   2465  } else if (aItem->GetChildren()) {
   2466    return ComputeInvalidationForDisplayList(aBuilder, aShift,
   2467                                             aItem->GetChildren());
   2468  }
   2469  return false;
   2470 }
   2471 
   2472 bool WebRenderCommandBuilder::ComputeInvalidationForDisplayList(
   2473    nsDisplayListBuilder* aBuilder, const nsPoint& aShift,
   2474    nsDisplayList* aList) {
   2475  FlattenedDisplayListIterator iter(aBuilder, aList);
   2476  while (iter.HasNext()) {
   2477    if (ComputeInvalidationForDisplayItem(aBuilder, aShift,
   2478                                          iter.GetNextItem())) {
   2479      return true;
   2480    }
   2481  }
   2482  return false;
   2483 }
   2484 
   2485 // When drawing fallback images we create either
   2486 // a real image or a blob image that will contain the display item.
   2487 // In the case of a blob image we paint the item at 0,0 instead
   2488 // of trying to keep at aItem->GetBounds().TopLeft() like we do
   2489 // with SVG. We do this because there's not necessarily a reference frame
   2490 // between us and the rest of the world so the the coordinates
   2491 // that we get for the bounds are not necessarily stable across scrolling
   2492 // or other movement.
   2493 already_AddRefed<WebRenderFallbackData>
   2494 WebRenderCommandBuilder::GenerateFallbackData(
   2495    nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
   2496    wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
   2497    nsDisplayListBuilder* aDisplayListBuilder, LayoutDeviceRect& aImageRect) {
   2498  Maybe<gfx::DeviceColor> highlight;
   2499  if (StaticPrefs::gfx_webrender_debug_highlight_painted_layers()) {
   2500    highlight.emplace(gfx::DeviceColor(1.0, 0.0, 0.0, 0.5));
   2501  }
   2502 
   2503  RefPtr<WebRenderFallbackData> fallbackData =
   2504      CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
   2505 
   2506  // Blob images will only draw the visible area of the blob so we don't need to
   2507  // clip them here and can just rely on the webrender clipping.
   2508  // TODO We also don't clip native themed widget to avoid over-invalidation
   2509  // during scrolling. It would be better to support a sort of streaming/tiling
   2510  // scheme for large ones but the hope is that we should not have large native
   2511  // themed items.
   2512  bool snap;
   2513  nsRect paintBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
   2514  nsRect buildingRect = aItem->GetBuildingRect();
   2515 
   2516  const int32_t appUnitsPerDevPixel =
   2517      aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   2518  auto bounds =
   2519      LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
   2520  if (bounds.IsEmpty()) {
   2521    return nullptr;
   2522  }
   2523 
   2524  MatrixScales scale = aSc.GetInheritedScale();
   2525  MatrixScales oldScale = fallbackData->mScale;
   2526  // We tolerate slight changes in scale so that we don't, for example,
   2527  // rerasterize on MotionMark
   2528  bool differentScale = gfx::FuzzyEqual(scale.xScale, oldScale.xScale, 1e-6f) &&
   2529                        gfx::FuzzyEqual(scale.yScale, oldScale.yScale, 1e-6f);
   2530 
   2531  auto layerScale = LayoutDeviceToLayerScale2D::FromUnknownScale(scale);
   2532 
   2533  auto trans =
   2534      ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
   2535 
   2536  if (!FitsInt32(trans.X()) || !FitsInt32(trans.Y())) {
   2537    // The translation overflowed int32_t.
   2538    return nullptr;
   2539  }
   2540 
   2541  auto snappedTrans = LayerIntPoint::Floor(trans);
   2542  LayerPoint residualOffset = trans - snappedTrans;
   2543 
   2544  nsRegion opaqueRegion = aItem->GetOpaqueRegion(aDisplayListBuilder, &snap);
   2545  wr::OpacityType opacity = opaqueRegion.Contains(paintBounds)
   2546                                ? wr::OpacityType::Opaque
   2547                                : wr::OpacityType::HasAlphaChannel;
   2548 
   2549  LayerIntRect dtRect, visibleRect;
   2550  // If we think the item is opaque we round the bounds
   2551  // to the nearest pixel instead of rounding them out. If we rounded
   2552  // out we'd potentially introduce transparent pixels.
   2553  //
   2554  // Ideally we'd be able to ask an item its bounds in pixels and whether
   2555  // they're all opaque. Unfortunately no such API exists so we currently
   2556  // just hope that we get it right.
   2557  if (aBuilder.GetInheritedOpacity() == 1.0f &&
   2558      opacity == wr::OpacityType::Opaque && snap) {
   2559    dtRect = LayerIntRect::FromUnknownRect(
   2560        ScaleToNearestPixelsOffset(paintBounds, scale.xScale, scale.yScale,
   2561                                   appUnitsPerDevPixel, residualOffset));
   2562 
   2563    visibleRect =
   2564        LayerIntRect::FromUnknownRect(
   2565            ScaleToNearestPixelsOffset(buildingRect, scale.xScale, scale.yScale,
   2566                                       appUnitsPerDevPixel, residualOffset))
   2567            .Intersect(dtRect);
   2568  } else {
   2569    dtRect = ScaleToOutsidePixelsOffset(paintBounds, scale.xScale, scale.yScale,
   2570                                        appUnitsPerDevPixel, residualOffset);
   2571 
   2572    visibleRect =
   2573        ScaleToOutsidePixelsOffset(buildingRect, scale.xScale, scale.yScale,
   2574                                   appUnitsPerDevPixel, residualOffset)
   2575            .Intersect(dtRect);
   2576  }
   2577 
   2578  auto visibleSize = visibleRect.Size();
   2579  // these rectangles can overflow from scaling so try to
   2580  // catch that with IsEmpty() checks. See bug 1622126.
   2581  if (visibleSize.IsEmpty() || dtRect.IsEmpty()) {
   2582    return nullptr;
   2583  }
   2584 
   2585  // Display item bounds should be unscaled
   2586  aImageRect = visibleRect / layerScale;
   2587 
   2588  // We always paint items at 0,0 so the visibleRect that we use inside the blob
   2589  // is needs to be adjusted by the display item bounds top left.
   2590  visibleRect -= dtRect.TopLeft();
   2591 
   2592  nsDisplayItemGeometry* geometry = fallbackData->mGeometry.get();
   2593 
   2594  bool needPaint = true;
   2595 
   2596  MOZ_RELEASE_ASSERT(aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER);
   2597  if (geometry && !fallbackData->IsInvalid() &&
   2598      aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER && differentScale) {
   2599    nsRect invalid;
   2600    if (!aItem->IsInvalid(invalid)) {
   2601      nsPoint shift = paintBounds.TopLeft() - geometry->mBounds.TopLeft();
   2602      geometry->MoveBy(shift);
   2603 
   2604      nsRegion invalidRegion;
   2605      aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry,
   2606                                       &invalidRegion);
   2607 
   2608      nsRect lastBounds = fallbackData->mBounds;
   2609      lastBounds.MoveBy(shift);
   2610 
   2611      if (lastBounds.IsEqualInterior(paintBounds) && invalidRegion.IsEmpty() &&
   2612          aBuilder.GetInheritedOpacity() == fallbackData->mOpacity) {
   2613        if (aItem->GetType() == DisplayItemType::TYPE_FILTER) {
   2614          needPaint = ComputeInvalidationForDisplayList(
   2615              aDisplayListBuilder, shift, aItem->GetChildren());
   2616          if (!buildingRect.IsEqualInterior(fallbackData->mBuildingRect)) {
   2617            needPaint = true;
   2618          }
   2619        } else {
   2620          needPaint = false;
   2621        }
   2622      }
   2623    }
   2624  }
   2625 
   2626  if (needPaint || !fallbackData->GetImageKey()) {
   2627    fallbackData->mGeometry =
   2628        WrapUnique(aItem->AllocateGeometry(aDisplayListBuilder));
   2629 
   2630    gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK
   2631                                    ? gfx::SurfaceFormat::A8
   2632                                    : (opacity == wr::OpacityType::Opaque
   2633                                           ? gfx::SurfaceFormat::B8G8R8X8
   2634                                           : gfx::SurfaceFormat::B8G8R8A8);
   2635    MOZ_ASSERT(!opaqueRegion.IsComplex());
   2636 
   2637    std::vector<RefPtr<ScaledFont>> fonts;
   2638    bool validFonts = true;
   2639    RefPtr<WebRenderDrawEventRecorder> recorder =
   2640        MakeAndAddRef<WebRenderDrawEventRecorder>(
   2641            [&](MemStream& aStream,
   2642                std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
   2643              size_t count = aScaledFonts.size();
   2644              aStream.write((const char*)&count, sizeof(count));
   2645              for (auto& scaled : aScaledFonts) {
   2646                Maybe<wr::FontInstanceKey> key =
   2647                    mManager->WrBridge()->GetFontKeyForScaledFont(scaled,
   2648                                                                  aResources);
   2649                if (key.isNothing()) {
   2650                  validFonts = false;
   2651                  break;
   2652                }
   2653                BlobFont font = {key.value(), scaled};
   2654                aStream.write((const char*)&font, sizeof(font));
   2655              }
   2656              fonts = std::move(aScaledFonts);
   2657            });
   2658    RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
   2659        gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
   2660    RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(
   2661        recorder, dummyDt, (dtRect - dtRect.TopLeft()).ToUnknownRect());
   2662    if (aBuilder.GetInheritedOpacity() != 1.0f) {
   2663      dt->PushLayer(false, aBuilder.GetInheritedOpacity(), nullptr,
   2664                    gfx::Matrix());
   2665    }
   2666    PaintItemByDrawTarget(aItem, dt, (dtRect / layerScale).TopLeft(),
   2667                          /*aVisibleRect: */ dt->GetRect(), aDisplayListBuilder,
   2668                          scale, highlight);
   2669    if (aBuilder.GetInheritedOpacity() != 1.0f) {
   2670      dt->PopLayer();
   2671    }
   2672 
   2673    // the item bounds are relative to the blob origin which is
   2674    // dtRect.TopLeft()
   2675    recorder->FlushItem((dtRect - dtRect.TopLeft()).ToUnknownRect());
   2676    recorder->Finish();
   2677 
   2678    if (!validFonts) {
   2679      gfxCriticalNote << "Failed serializing fonts for blob image";
   2680      return nullptr;
   2681    }
   2682 
   2683    Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
   2684                         recorder->mOutputStream.mLength);
   2685    wr::BlobImageKey key =
   2686        wr::BlobImageKey{mManager->WrBridge()->GetNextImageKey()};
   2687    wr::ImageDescriptor descriptor(visibleSize.ToUnknownSize(), 0,
   2688                                   dt->GetFormat(), opacity);
   2689    if (!aResources.AddBlobImage(
   2690            key, descriptor, bytes,
   2691            ViewAs<ImagePixel>(visibleRect,
   2692                               PixelCastJustification::LayerIsImage))) {
   2693      return nullptr;
   2694    }
   2695    TakeExternalSurfaces(recorder, fallbackData->mExternalSurfaces,
   2696                         mManager->GetRenderRootStateManager(), aResources);
   2697    fallbackData->SetBlobImageKey(key);
   2698    fallbackData->SetFonts(fonts);
   2699 
   2700    fallbackData->mScale = scale;
   2701    fallbackData->mOpacity = aBuilder.GetInheritedOpacity();
   2702    fallbackData->SetInvalid(false);
   2703  }
   2704 
   2705  MOZ_DIAGNOSTIC_ASSERT(mManager->WrBridge()->MatchesNamespace(
   2706                            fallbackData->GetBlobImageKey().ref()),
   2707                        "Stale blob key for fallback!");
   2708 
   2709  aResources.SetBlobImageVisibleArea(
   2710      fallbackData->GetBlobImageKey().value(),
   2711      ViewAs<ImagePixel>(visibleRect, PixelCastJustification::LayerIsImage));
   2712 
   2713  // Update current bounds to fallback data
   2714  fallbackData->mBounds = paintBounds;
   2715  fallbackData->mBuildingRect = buildingRect;
   2716 
   2717  MOZ_ASSERT(fallbackData->GetImageKey());
   2718 
   2719  return fallbackData.forget();
   2720 }
   2721 
   2722 void WebRenderMaskData::ClearImageKey() {
   2723  if (mBlobKey) {
   2724    mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
   2725  }
   2726  mBlobKey.reset();
   2727 }
   2728 
   2729 void WebRenderMaskData::Invalidate() {
   2730  mMaskStyle = nsStyleImageLayers(nsStyleImageLayers::LayerType::Mask);
   2731 }
   2732 
   2733 Maybe<wr::ImageMask> WebRenderCommandBuilder::BuildWrMaskImage(
   2734    nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder,
   2735    wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
   2736    nsDisplayListBuilder* aDisplayListBuilder,
   2737    const LayoutDeviceRect& aBounds) {
   2738  RefPtr<WebRenderMaskData> maskData =
   2739      CreateOrRecycleWebRenderUserData<WebRenderMaskData>(aMaskItem);
   2740 
   2741  if (!maskData) {
   2742    return Nothing();
   2743  }
   2744 
   2745  bool snap;
   2746  nsRect bounds = aMaskItem->GetBounds(aDisplayListBuilder, &snap);
   2747 
   2748  const int32_t appUnitsPerDevPixel =
   2749      aMaskItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   2750 
   2751  MatrixScales scale = aSc.GetInheritedScale();
   2752  MatrixScales oldScale = maskData->mScale;
   2753  // This scale determination should probably be done using
   2754  // ChooseScaleAndSetTransform but for now we just fake it.
   2755  // We tolerate slight changes in scale so that we don't, for example,
   2756  // rerasterize on MotionMark
   2757  bool sameScale = FuzzyEqual(scale.xScale, oldScale.xScale, 1e-6f) &&
   2758                   FuzzyEqual(scale.yScale, oldScale.yScale, 1e-6f);
   2759 
   2760  LayerIntRect itemRect =
   2761      LayerIntRect::FromUnknownRect(bounds.ScaleToOutsidePixels(
   2762          scale.xScale, scale.yScale, appUnitsPerDevPixel));
   2763 
   2764  LayerIntRect visibleRect =
   2765      LayerIntRect::FromUnknownRect(
   2766          aMaskItem->GetBuildingRect().ScaleToOutsidePixels(
   2767              scale.xScale, scale.yScale, appUnitsPerDevPixel))
   2768          .SafeIntersect(itemRect);
   2769 
   2770  if (visibleRect.IsEmpty()) {
   2771    return Nothing();
   2772  }
   2773 
   2774  LayoutDeviceToLayerScale2D layerScale(scale.xScale, scale.yScale);
   2775  LayoutDeviceRect imageRect = LayerRect(visibleRect) / layerScale;
   2776 
   2777  nsPoint maskOffset = aMaskItem->ToReferenceFrame() - bounds.TopLeft();
   2778 
   2779  bool shouldHandleOpacity = aBuilder.GetInheritedOpacity() != 1.0f;
   2780 
   2781  nsRect dirtyRect;
   2782  // If this mask item is being painted for the first time, some members of
   2783  // WebRenderMaskData are still default initialized. This is intentional.
   2784  if (aMaskItem->IsInvalid(dirtyRect) ||
   2785      !itemRect.IsEqualInterior(maskData->mItemRect) ||
   2786      !(aMaskItem->Frame()->StyleSVGReset()->mMask == maskData->mMaskStyle) ||
   2787      maskOffset != maskData->mMaskOffset || !sameScale ||
   2788      shouldHandleOpacity != maskData->mShouldHandleOpacity) {
   2789    IntSize size = itemRect.Size().ToUnknownSize();
   2790 
   2791    if (!Factory::AllowedSurfaceSize(size)) {
   2792      return Nothing();
   2793    }
   2794 
   2795    std::vector<RefPtr<ScaledFont>> fonts;
   2796    bool validFonts = true;
   2797    RefPtr<WebRenderDrawEventRecorder> recorder =
   2798        MakeAndAddRef<WebRenderDrawEventRecorder>(
   2799            [&](MemStream& aStream,
   2800                std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
   2801              size_t count = aScaledFonts.size();
   2802              aStream.write((const char*)&count, sizeof(count));
   2803 
   2804              for (auto& scaled : aScaledFonts) {
   2805                Maybe<wr::FontInstanceKey> key =
   2806                    mManager->WrBridge()->GetFontKeyForScaledFont(scaled,
   2807                                                                  aResources);
   2808                if (key.isNothing()) {
   2809                  validFonts = false;
   2810                  break;
   2811                }
   2812                BlobFont font = {key.value(), scaled};
   2813                aStream.write((const char*)&font, sizeof(font));
   2814              }
   2815 
   2816              fonts = std::move(aScaledFonts);
   2817            });
   2818 
   2819    RefPtr<DrawTarget> dummyDt = Factory::CreateDrawTarget(
   2820        BackendType::SKIA, IntSize(1, 1), SurfaceFormat::A8);
   2821    RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget(
   2822        recorder, dummyDt, IntRect(IntPoint(0, 0), size));
   2823    if (!dt || !dt->IsValid()) {
   2824      gfxCriticalNote << "Failed to create drawTarget for blob mask image";
   2825      return Nothing();
   2826    }
   2827 
   2828    gfxContext context(dt);
   2829    context.SetMatrix(context.CurrentMatrix()
   2830                          .PreTranslate(-itemRect.x, -itemRect.y)
   2831                          .PreScale(scale));
   2832 
   2833    bool maskPainted = false;
   2834    bool maskIsComplete = aMaskItem->PaintMask(
   2835        aDisplayListBuilder, &context, shouldHandleOpacity, &maskPainted);
   2836    if (!maskPainted) {
   2837      return Nothing();
   2838    }
   2839 
   2840    // If a mask is incomplete or missing (e.g. it's display: none) the proper
   2841    // behaviour depends on the masked frame being html or svg.
   2842    //
   2843    // For an HTML frame:
   2844    //   According to css-masking spec, always create a mask surface when
   2845    //   we have any item in maskFrame even if all of those items are
   2846    //   non-resolvable <mask-sources> or <images> so continue with the
   2847    //   painting code. Note that in a common case of no layer of the mask being
   2848    //   complete or even partially complete then the mask surface will be
   2849    //   transparent black so this results in hiding the frame.
   2850    // For an SVG frame:
   2851    //   SVG 1.1 say that if we fail to resolve a mask, we should draw the
   2852    //   object unmasked so return Nothing().
   2853    if (!maskIsComplete &&
   2854        aMaskItem->Frame()->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   2855      return Nothing();
   2856    }
   2857 
   2858    recorder->FlushItem(IntRect(0, 0, size.width, size.height));
   2859    recorder->Finish();
   2860 
   2861    if (!validFonts) {
   2862      gfxCriticalNote << "Failed serializing fonts for blob mask image";
   2863      return Nothing();
   2864    }
   2865 
   2866    Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
   2867                         recorder->mOutputStream.mLength);
   2868    wr::BlobImageKey key =
   2869        wr::BlobImageKey{mManager->WrBridge()->GetNextImageKey()};
   2870    wr::ImageDescriptor descriptor(size, 0, dt->GetFormat(),
   2871                                   wr::OpacityType::HasAlphaChannel);
   2872    if (!aResources.AddBlobImage(key, descriptor, bytes,
   2873                                 ImageIntRect(0, 0, size.width, size.height))) {
   2874      return Nothing();
   2875    }
   2876    maskData->ClearImageKey();
   2877    maskData->mBlobKey = Some(key);
   2878    maskData->mFonts = fonts;
   2879    TakeExternalSurfaces(recorder, maskData->mExternalSurfaces,
   2880                         mManager->GetRenderRootStateManager(), aResources);
   2881    if (maskIsComplete) {
   2882      maskData->mItemRect = itemRect;
   2883      maskData->mMaskOffset = maskOffset;
   2884      maskData->mScale = scale;
   2885      maskData->mMaskStyle = aMaskItem->Frame()->StyleSVGReset()->mMask;
   2886      maskData->mShouldHandleOpacity = shouldHandleOpacity;
   2887    }
   2888  }
   2889 
   2890  aResources.SetBlobImageVisibleArea(
   2891      maskData->mBlobKey.value(),
   2892      ViewAs<ImagePixel>(visibleRect - itemRect.TopLeft(),
   2893                         PixelCastJustification::LayerIsImage));
   2894 
   2895  MOZ_DIAGNOSTIC_ASSERT(
   2896      mManager->WrBridge()->MatchesNamespace(maskData->mBlobKey.ref()),
   2897      "Stale blob key for mask!");
   2898 
   2899  wr::ImageMask imageMask;
   2900  imageMask.image = wr::AsImageKey(maskData->mBlobKey.value());
   2901  imageMask.rect = wr::ToLayoutRect(imageRect);
   2902  return Some(imageMask);
   2903 }
   2904 
   2905 bool WebRenderCommandBuilder::PushItemAsImage(
   2906    nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
   2907    wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
   2908    nsDisplayListBuilder* aDisplayListBuilder) {
   2909  LayoutDeviceRect imageRect;
   2910  RefPtr<WebRenderFallbackData> fallbackData = GenerateFallbackData(
   2911      aItem, aBuilder, aResources, aSc, aDisplayListBuilder, imageRect);
   2912  if (!fallbackData) {
   2913    return false;
   2914  }
   2915 
   2916  wr::LayoutRect dest = wr::ToLayoutRect(imageRect);
   2917  auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
   2918  mHitTestInfoManager.ProcessItemAsImage(aItem, dest, aBuilder,
   2919                                         aDisplayListBuilder);
   2920  aBuilder.PushImage(dest, dest, !aItem->BackfaceIsHidden(), false, rendering,
   2921                     fallbackData->GetImageKey().value());
   2922  return true;
   2923 }
   2924 
   2925 void WebRenderCommandBuilder::RemoveUnusedAndResetWebRenderUserData() {
   2926  mWebRenderUserDatas.RemoveIf([&](WebRenderUserData* data) {
   2927    if (!data->IsUsed()) {
   2928      nsIFrame* frame = data->GetFrame();
   2929 
   2930      MOZ_ASSERT(frame->HasProperty(WebRenderUserDataProperty::Key()));
   2931 
   2932      WebRenderUserDataTable* userDataTable =
   2933          frame->GetProperty(WebRenderUserDataProperty::Key());
   2934 
   2935      MOZ_ASSERT(userDataTable->Count());
   2936 
   2937      userDataTable->Remove(
   2938          WebRenderUserDataKey(data->GetDisplayItemKey(), data->GetType()));
   2939 
   2940      if (!userDataTable->Count()) {
   2941        frame->RemoveProperty(WebRenderUserDataProperty::Key());
   2942        userDataTable = nullptr;
   2943      }
   2944 
   2945      switch (data->GetType()) {
   2946        case WebRenderUserData::UserDataType::eCanvas:
   2947          mLastCanvasDatas.Remove(data->AsCanvasData());
   2948          break;
   2949        case WebRenderUserData::UserDataType::eAnimation:
   2950          EffectCompositor::ClearIsRunningOnCompositor(
   2951              frame, GetDisplayItemTypeFromKey(data->GetDisplayItemKey()));
   2952          break;
   2953        default:
   2954          break;
   2955      }
   2956 
   2957      return true;
   2958    }
   2959 
   2960    data->SetUsed(false);
   2961    return false;
   2962  });
   2963 }
   2964 
   2965 void WebRenderCommandBuilder::ClearCachedResources() {
   2966  RemoveUnusedAndResetWebRenderUserData();
   2967  // UserDatas should only be in the used state during a call to
   2968  // WebRenderCommandBuilder::BuildWebRenderCommands The should always be false
   2969  // upon return from BuildWebRenderCommands().
   2970  MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
   2971 }
   2972 
   2973 WebRenderGroupData::WebRenderGroupData(
   2974    RenderRootStateManager* aRenderRootStateManager, nsDisplayItem* aItem)
   2975    : WebRenderGroupData(aRenderRootStateManager, aItem->GetPerFrameKey(),
   2976                         aItem->Frame()) {}
   2977 
   2978 WebRenderGroupData::WebRenderGroupData(
   2979    RenderRootStateManager* aRenderRootStateManager, uint32_t aDisplayItemKey,
   2980    nsIFrame* aFrame)
   2981    : WebRenderUserData(aRenderRootStateManager, aDisplayItemKey, aFrame) {
   2982  MOZ_COUNT_CTOR(WebRenderGroupData);
   2983 }
   2984 
   2985 WebRenderGroupData::~WebRenderGroupData() {
   2986  MOZ_COUNT_DTOR(WebRenderGroupData);
   2987  GP("Group data destruct\n");
   2988  mSubGroup.ClearImageKey(mManager, true);
   2989  mFollowingGroup.ClearImageKey(mManager, true);
   2990 }
   2991 
   2992 }  // namespace mozilla::layers