tor-browser

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

WebRenderScrollData.cpp (19614B)


      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 "mozilla/layers/WebRenderScrollData.h"
      8 
      9 #include <ostream>
     10 
     11 #include "Units.h"
     12 #include "mozilla/layers/LayersMessageUtils.h"
     13 #include "mozilla/layers/WebRenderLayerManager.h"
     14 #include "mozilla/ScrollContainerFrame.h"
     15 #include "nsDisplayList.h"
     16 #include "nsTArray.h"
     17 #include "UnitTransforms.h"
     18 
     19 namespace mozilla {
     20 namespace layers {
     21 
     22 WebRenderLayerScrollData::WebRenderLayerScrollData()
     23    : mDescendantCount(-1),
     24      mAncestorTransformId(ScrollableLayerGuid::NULL_SCROLL_ID),
     25      mTransformIsPerspective(false),
     26      mEventRegionsOverride(EventRegionsOverride::NoOverride),
     27      mFixedPositionSides(mozilla::SideBits::eNone),
     28      mFixedPosScrollContainerId(ScrollableLayerGuid::NULL_SCROLL_ID),
     29      mStickyPosScrollContainerId(ScrollableLayerGuid::NULL_SCROLL_ID) {}
     30 
     31 WebRenderLayerScrollData::~WebRenderLayerScrollData() = default;
     32 
     33 void WebRenderLayerScrollData::InitializeRoot(int32_t aDescendantCount) {
     34  mDescendantCount = aDescendantCount;
     35 }
     36 
     37 void WebRenderLayerScrollData::InitializeForTest(int32_t aDescendantCount) {
     38  mDescendantCount = aDescendantCount;
     39 }
     40 
     41 void WebRenderLayerScrollData::Initialize(
     42    WebRenderScrollData& aOwner, nsDisplayItem* aItem, int32_t aDescendantCount,
     43    const ActiveScrolledRoot* aStopAtAsr,
     44    const Maybe<gfx::Matrix4x4>& aAncestorTransform,
     45    const ViewID& aAncestorTransformId) {
     46  MOZ_ASSERT(aDescendantCount >= 0);  // Ensure value is valid
     47  MOZ_ASSERT(mDescendantCount ==
     48             -1);  // Don't allow re-setting an already set value
     49  mDescendantCount = aDescendantCount;
     50 
     51 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
     52  mInitializedFrom = aItem;
     53 #endif
     54 
     55  MOZ_ASSERT(aItem);
     56  aItem->UpdateScrollData(&aOwner, this);
     57 
     58  const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
     59  if (ActiveScrolledRoot::IsAncestor(asr, aStopAtAsr)) {
     60    // If the item's ASR is an ancestor of the stop-at ASR, then we don't need
     61    // any more metrics information because we'll end up duplicating what the
     62    // ancestor WebRenderLayerScrollData already has.
     63    asr = nullptr;
     64  }
     65 
     66  while (asr && asr != aStopAtAsr) {
     67    MOZ_ASSERT(aOwner.GetManager());
     68    if (asr->mKind != ActiveScrolledRoot::ASRKind::Scroll) {
     69      asr = asr->mParent;
     70      continue;
     71    }
     72    ScrollableLayerGuid::ViewID scrollId = asr->GetViewId();
     73    if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
     74      mScrollIds.AppendElement(index.ref());
     75    } else {
     76      Maybe<ScrollMetadata> metadata =
     77          asr->ScrollFrame()->ComputeScrollMetadata(
     78              aOwner.GetManager(), aItem->Frame(), aItem->ToReferenceFrame());
     79      aOwner.GetBuilder()->AddScrollContainerFrameToNotify(asr->ScrollFrame());
     80      if (metadata) {
     81        MOZ_ASSERT(metadata->GetMetrics().GetScrollId() == scrollId);
     82        mScrollIds.AppendElement(aOwner.AddMetadata(metadata.ref()));
     83      } else {
     84        MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
     85      }
     86    }
     87    asr = asr->mParent;
     88  }
     89 
     90 #ifdef DEBUG
     91  // Sanity check: if we have an ancestor transform, its scroll id should
     92  // match one of the scroll metadatas on this node (WebRenderScrollDataWrapper
     93  // will then use the ancestor transform at the level of that scroll metadata).
     94  // One exception to this is if we have no scroll metadatas, which can happen
     95  // if the scroll id of the transform is on an enclosing node.
     96  if (aAncestorTransformId != ScrollableLayerGuid::NULL_SCROLL_ID &&
     97      !mScrollIds.IsEmpty()) {
     98    bool seenAncestorTransformId = false;
     99    for (size_t scrollIdIndex : mScrollIds) {
    100      if (aAncestorTransformId ==
    101          aOwner.GetScrollMetadata(scrollIdIndex).GetMetrics().GetScrollId()) {
    102        seenAncestorTransformId = true;
    103      }
    104    }
    105    MOZ_ASSERT(
    106        seenAncestorTransformId,
    107        "The ancestor transform's view ID should match one of the metrics "
    108        "on this node");
    109  }
    110 #endif
    111 
    112  // See the comments on StackingContextHelper::mDeferredTransformItem for an
    113  // overview of what deferred transforms are.
    114  // aAncestorTransform, if present, is the transform from a deferred transform
    115  // item that is an ancestor of |aItem|. We store this transform value
    116  // separately from mTransform because in the case where we have multiple
    117  // scroll metadata on this layer item, the mAncestorTransform is associated
    118  // with the "topmost" scroll metadata, and the mTransform is associated with
    119  // the "bottommost" scroll metadata. The code in
    120  // WebRenderScrollDataWrapper::GetTransform() is responsible for combining
    121  // these transforms and exposing them appropriately. Also, we don't save the
    122  // ancestor transform for thumb layers, because those are a special case in
    123  // APZ; we need to keep the ancestor transform for the scrollable content that
    124  // the thumb scrolls, but not for the thumb itself, as it will result in
    125  // incorrect visual positioning of the thumb.
    126  if (aAncestorTransform &&
    127      mScrollbarData.mScrollbarLayerType != ScrollbarLayerType::Thumb) {
    128    mAncestorTransform = *aAncestorTransform;
    129    mAncestorTransformId = aAncestorTransformId;
    130  }
    131 }
    132 
    133 int32_t WebRenderLayerScrollData::GetDescendantCount() const {
    134  MOZ_ASSERT(mDescendantCount >= 0);  // check that it was set
    135  return mDescendantCount;
    136 }
    137 
    138 size_t WebRenderLayerScrollData::GetScrollMetadataCount() const {
    139  return mScrollIds.Length();
    140 }
    141 
    142 void WebRenderLayerScrollData::AppendScrollMetadata(
    143    WebRenderScrollData& aOwner, const ScrollMetadata& aData) {
    144  mScrollIds.AppendElement(aOwner.AddMetadata(aData));
    145 }
    146 
    147 const ScrollMetadata& WebRenderLayerScrollData::GetScrollMetadata(
    148    const WebRenderScrollData& aOwner, size_t aIndex) const {
    149  MOZ_ASSERT(aIndex < mScrollIds.Length());
    150  return aOwner.GetScrollMetadata(mScrollIds[aIndex]);
    151 }
    152 
    153 ScrollMetadata& WebRenderLayerScrollData::GetScrollMetadataMut(
    154    WebRenderScrollData& aOwner, size_t aIndex) {
    155  MOZ_ASSERT(aIndex < mScrollIds.Length());
    156  return aOwner.GetScrollMetadataMut(mScrollIds[aIndex]);
    157 }
    158 
    159 CSSTransformMatrix WebRenderLayerScrollData::GetTransformTyped() const {
    160  return ViewAs<CSSTransformMatrix>(GetTransform());
    161 }
    162 
    163 void WebRenderLayerScrollData::Dump(std::ostream& aOut,
    164                                    const WebRenderScrollData& aOwner) const {
    165  aOut << "WebRenderLayerScrollData(" << this
    166       << "), descendantCount=" << mDescendantCount;
    167 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
    168  if (mInitializedFrom) {
    169    aOut << ", item=" << (void*)mInitializedFrom;
    170  }
    171 #endif
    172  if (mAsyncZoomContainerId) {
    173    aOut << ", asyncZoomContainer";
    174  }
    175  for (size_t i = 0; i < mScrollIds.Length(); i++) {
    176    aOut << ", metadata" << i << "=" << aOwner.GetScrollMetadata(mScrollIds[i]);
    177  }
    178  if (!mAncestorTransform.IsIdentity()) {
    179    aOut << ", ancestorTransform=" << mAncestorTransform
    180         << " (asr=" << mAncestorTransformId << ")";
    181  }
    182  if (!mTransform.IsIdentity()) {
    183    aOut << ", transform=" << mTransform;
    184    if (mTransformIsPerspective) {
    185      aOut << ", transformIsPerspective";
    186    }
    187  }
    188  aOut << ", visible=" << mVisibleRect;
    189  if (mReferentId) {
    190    aOut << ", refLayersId=" << *mReferentId;
    191  }
    192  if (mEventRegionsOverride) {
    193    aOut << std::hex << ", eventRegionsOverride=0x"
    194         << (int)mEventRegionsOverride << std::dec;
    195  }
    196  if (mScrollbarData.mScrollbarLayerType != ScrollbarLayerType::None) {
    197    aOut << ", scrollbarType=" << (int)mScrollbarData.mScrollbarLayerType
    198         << std::hex << ", scrollbarAnimationId=0x"
    199         << mScrollbarAnimationId.valueOr(0) << std::dec;
    200  }
    201  if (mFixedPosScrollContainerId != ScrollableLayerGuid::NULL_SCROLL_ID) {
    202    aOut << ", fixedContainer=" << mFixedPosScrollContainerId << std::hex
    203         << ", fixedAnimation=0x" << mFixedPositionAnimationId.valueOr(0)
    204         << ", sideBits=0x" << (int)mFixedPositionSides << std::dec;
    205  }
    206  if (mStickyPosScrollContainerId != ScrollableLayerGuid::NULL_SCROLL_ID) {
    207    aOut << ", stickyContainer=" << mStickyPosScrollContainerId << std::hex
    208         << ", stickyAnimation=" << mStickyPositionAnimationId.valueOr(0)
    209         << std::dec << ", stickyInner=" << mStickyScrollRangeInner
    210         << ", stickyOuter=" << mStickyScrollRangeOuter;
    211  }
    212 }
    213 
    214 WebRenderScrollData::WebRenderScrollData()
    215    : mManager(nullptr),
    216      mBuilder(nullptr),
    217      mIsFirstPaint(false),
    218      mPaintSequenceNumber(0) {}
    219 
    220 WebRenderScrollData::WebRenderScrollData(WebRenderLayerManager* aManager,
    221                                         nsDisplayListBuilder* aBuilder)
    222    : mManager(aManager),
    223      mBuilder(aBuilder),
    224      mIsFirstPaint(false),
    225      mPaintSequenceNumber(0) {}
    226 
    227 bool WebRenderScrollData::Validate() const {
    228  // Attempt to traverse the tree structure encoded by the descendant counts,
    229  // validating as we go that everything is within bounds and properly nested.
    230  // In addition, check that the traversal visits every node exactly once.
    231  std::vector<size_t> visitCounts(mLayerScrollData.Length(), 0);
    232  if (mLayerScrollData.Length() > 0) {
    233    if (!mLayerScrollData[0].ValidateSubtree(*this, visitCounts, 0)) {
    234      return false;
    235    }
    236  }
    237  for (size_t visitCount : visitCounts) {
    238    if (visitCount != 1) {
    239      return false;
    240    }
    241  }
    242  return true;
    243 }
    244 
    245 WebRenderLayerManager* WebRenderScrollData::GetManager() const {
    246  return mManager;
    247 }
    248 
    249 nsDisplayListBuilder* WebRenderScrollData::GetBuilder() const {
    250  return mBuilder;
    251 }
    252 
    253 size_t WebRenderScrollData::AddMetadata(const ScrollMetadata& aMetadata) {
    254  ScrollableLayerGuid::ViewID scrollId = aMetadata.GetMetrics().GetScrollId();
    255  auto p = mScrollIdMap.lookupForAdd(scrollId);
    256  if (!p) {
    257    // It's a scrollId we hadn't seen before
    258    bool ok = mScrollIdMap.add(p, scrollId, mScrollMetadatas.Length());
    259    MOZ_RELEASE_ASSERT(ok);
    260    mScrollMetadatas.AppendElement(aMetadata);
    261  }  // else we didn't insert, because it already existed
    262  return p->value();
    263 }
    264 
    265 size_t WebRenderScrollData::AddLayerData(WebRenderLayerScrollData&& aData) {
    266  mLayerScrollData.AppendElement(std::move(aData));
    267  return mLayerScrollData.Length() - 1;
    268 }
    269 
    270 size_t WebRenderScrollData::GetLayerCount() const {
    271  return mLayerScrollData.Length();
    272 }
    273 
    274 bool WebRenderLayerScrollData::ValidateSubtree(
    275    const WebRenderScrollData& aParent, std::vector<size_t>& aVisitCounts,
    276    size_t aCurrentIndex) const {
    277  ++aVisitCounts[aCurrentIndex];
    278 
    279  // All scroll ids must be in bounds.
    280  for (size_t scrollMetadataIndex : mScrollIds) {
    281    if (scrollMetadataIndex >= aParent.mScrollMetadatas.Length()) {
    282      return false;
    283    }
    284  }
    285 
    286  // Descendant count must be nonnegative.
    287  if (mDescendantCount < 0) {
    288    return false;
    289  }
    290  size_t descendantCount = static_cast<size_t>(mDescendantCount);
    291 
    292  // Bounds check: for every layer, its index + its mDescendantCount
    293  // must be within bounds.
    294  if (aCurrentIndex + descendantCount >= aParent.mLayerScrollData.Length()) {
    295    return false;
    296  }
    297 
    298  // Recurse over our children, accumulating a count of our children
    299  // and their descendants as we go.
    300  size_t childCount = 0;
    301  size_t childDescendantCounts = 0;
    302  size_t currentChildIndex = aCurrentIndex + 1;
    303  while (currentChildIndex < (aCurrentIndex + descendantCount + 1)) {
    304    ++childCount;
    305 
    306    const WebRenderLayerScrollData* currentChild =
    307        &aParent.mLayerScrollData[currentChildIndex];
    308    childDescendantCounts += currentChild->mDescendantCount;
    309    currentChild->ValidateSubtree(aParent, aVisitCounts, currentChildIndex);
    310 
    311    // The current child's descendants come first in the array, and the next
    312    // element after that is our next child.
    313    currentChildIndex += (currentChild->mDescendantCount + 1);
    314  }
    315 
    316  // For a given layer, its descendant count must equal the number of
    317  // children + the descendant counts of its children added together.
    318  return descendantCount == (childCount + childDescendantCounts);
    319 }
    320 
    321 const WebRenderLayerScrollData* WebRenderScrollData::GetLayerData(
    322    size_t aIndex) const {
    323  if (aIndex >= mLayerScrollData.Length()) {
    324    return nullptr;
    325  }
    326  return &(mLayerScrollData.ElementAt(aIndex));
    327 }
    328 
    329 WebRenderLayerScrollData* WebRenderScrollData::GetLayerData(size_t aIndex) {
    330  if (aIndex >= mLayerScrollData.Length()) {
    331    return nullptr;
    332  }
    333  return &(mLayerScrollData.ElementAt(aIndex));
    334 }
    335 
    336 const ScrollMetadata& WebRenderScrollData::GetScrollMetadata(
    337    size_t aIndex) const {
    338  MOZ_ASSERT(aIndex < mScrollMetadatas.Length());
    339  return mScrollMetadatas[aIndex];
    340 }
    341 
    342 ScrollMetadata& WebRenderScrollData::GetScrollMetadataMut(size_t aIndex) {
    343  MOZ_ASSERT(aIndex < mScrollMetadatas.Length());
    344  return mScrollMetadatas[aIndex];
    345 }
    346 
    347 Maybe<size_t> WebRenderScrollData::HasMetadataFor(
    348    const ScrollableLayerGuid::ViewID& aScrollId) const {
    349  auto ptr = mScrollIdMap.lookup(aScrollId);
    350  return (ptr ? Some(ptr->value()) : Nothing());
    351 }
    352 
    353 void WebRenderScrollData::SetIsFirstPaint(bool aValue) {
    354  mIsFirstPaint = aValue;
    355 }
    356 
    357 bool WebRenderScrollData::IsFirstPaint() const { return mIsFirstPaint; }
    358 
    359 void WebRenderScrollData::SetPaintSequenceNumber(
    360    uint32_t aPaintSequenceNumber) {
    361  mPaintSequenceNumber = aPaintSequenceNumber;
    362 }
    363 
    364 uint32_t WebRenderScrollData::GetPaintSequenceNumber() const {
    365  return mPaintSequenceNumber;
    366 }
    367 
    368 void WebRenderScrollData::ApplyUpdates(ScrollUpdatesMap&& aUpdates,
    369                                       uint32_t aPaintSequenceNumber) {
    370  for (auto it = aUpdates.Iter(); !it.Done(); it.Next()) {
    371    if (Maybe<size_t> index = HasMetadataFor(it.Key())) {
    372      mScrollMetadatas[*index].UpdatePendingScrollInfo(std::move(it.Data()));
    373    }
    374  }
    375  mPaintSequenceNumber = aPaintSequenceNumber;
    376 }
    377 
    378 void WebRenderScrollData::PrependUpdates(
    379    const WebRenderScrollData& aPreviousData) {
    380  for (auto previousMetadata : aPreviousData.mScrollMetadatas) {
    381    const nsTArray<ScrollPositionUpdate>& previousUpdates =
    382        previousMetadata.GetScrollUpdates();
    383    if (previousUpdates.IsEmpty()) {
    384      continue;
    385    }
    386 
    387    if (Maybe<size_t> index =
    388            HasMetadataFor(previousMetadata.GetMetrics().GetScrollId())) {
    389      mScrollMetadatas[*index].PrependUpdates(previousUpdates);
    390    }
    391  }
    392 }
    393 
    394 void WebRenderScrollData::DumpSubtree(std::ostream& aOut, size_t aIndex,
    395                                      const std::string& aIndent) const {
    396  aOut << aIndent;
    397  mLayerScrollData.ElementAt(aIndex).Dump(aOut, *this);
    398  aOut << std::endl;
    399 
    400  int32_t descendants = mLayerScrollData.ElementAt(aIndex).GetDescendantCount();
    401  if (descendants == 0) {
    402    return;
    403  }
    404 
    405  // Build a stack of indices at which this aIndex's children live. We do
    406  // this because we want to dump them first-to-last but they are stored
    407  // last-to-first.
    408  std::stack<size_t> childIndices;
    409  size_t childIndex = aIndex + 1;
    410  while (descendants > 0) {
    411    childIndices.push(childIndex);
    412    // "1" for the child itelf, plus whatever descendants it has
    413    int32_t subtreeSize =
    414        1 + mLayerScrollData.ElementAt(childIndex).GetDescendantCount();
    415    childIndex += subtreeSize;
    416    descendants -= subtreeSize;
    417    MOZ_ASSERT(descendants >= 0);
    418  }
    419 
    420  std::string indent = aIndent + "    ";
    421  while (!childIndices.empty()) {
    422    size_t child = childIndices.top();
    423    childIndices.pop();
    424    DumpSubtree(aOut, child, indent);
    425  }
    426 }
    427 
    428 std::ostream& operator<<(std::ostream& aOut, const WebRenderScrollData& aData) {
    429  aOut << "--- WebRenderScrollData (firstPaint=" << aData.mIsFirstPaint
    430       << ") ---" << std::endl;
    431 
    432  if (aData.mLayerScrollData.Length() > 0) {
    433    aData.DumpSubtree(aOut, 0, std::string());
    434  }
    435  return aOut;
    436 }
    437 
    438 bool WebRenderScrollData::RepopulateMap() {
    439  MOZ_ASSERT(mScrollIdMap.empty());
    440  for (size_t i = 0; i < mScrollMetadatas.Length(); i++) {
    441    ScrollableLayerGuid::ViewID scrollId =
    442        mScrollMetadatas[i].GetMetrics().GetScrollId();
    443    bool ok = mScrollIdMap.putNew(scrollId, i);
    444    MOZ_RELEASE_ASSERT(ok);
    445  }
    446  return true;
    447 }
    448 
    449 }  // namespace layers
    450 }  // namespace mozilla
    451 
    452 namespace IPC {
    453 
    454 void ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Write(
    455    MessageWriter* aWriter, const paramType& aParam) {
    456  WriteParam(aWriter, aParam.mDescendantCount);
    457  WriteParam(aWriter, aParam.mScrollIds);
    458  WriteParam(aWriter, aParam.mAncestorTransform);
    459  WriteParam(aWriter, aParam.mAncestorTransformId);
    460  WriteParam(aWriter, aParam.mTransform);
    461  WriteParam(aWriter, aParam.mTransformIsPerspective);
    462  WriteParam(aWriter, aParam.mVisibleRect);
    463  WriteParam(aWriter, aParam.mRemoteDocumentSize);
    464  WriteParam(aWriter, aParam.mReferentId);
    465  WriteParam(aWriter, aParam.mEventRegionsOverride);
    466  WriteParam(aWriter, aParam.mScrollbarData);
    467  WriteParam(aWriter, aParam.mScrollbarAnimationId);
    468  WriteParam(aWriter, aParam.mFixedPositionAnimationId);
    469  WriteParam(aWriter, aParam.mFixedPositionSides);
    470  WriteParam(aWriter, aParam.mFixedPosScrollContainerId);
    471  WriteParam(aWriter, aParam.mStickyPosScrollContainerId);
    472  WriteParam(aWriter, aParam.mStickyScrollRangeOuter);
    473  WriteParam(aWriter, aParam.mStickyScrollRangeInner);
    474  WriteParam(aWriter, aParam.mStickyPositionAnimationId);
    475  WriteParam(aWriter, aParam.mZoomAnimationId);
    476  WriteParam(aWriter, aParam.mAsyncZoomContainerId);
    477  // Do not write |mInitializedFrom|, the pointer wouldn't be valid
    478  // on the compositor side.
    479 }
    480 
    481 bool ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Read(
    482    MessageReader* aReader, paramType* aResult) {
    483  return ReadParam(aReader, &aResult->mDescendantCount) &&
    484         ReadParam(aReader, &aResult->mScrollIds) &&
    485         ReadParam(aReader, &aResult->mAncestorTransform) &&
    486         ReadParam(aReader, &aResult->mAncestorTransformId) &&
    487         ReadParam(aReader, &aResult->mTransform) &&
    488         ReadParam(aReader, &aResult->mTransformIsPerspective) &&
    489         ReadParam(aReader, &aResult->mVisibleRect) &&
    490         ReadParam(aReader, &aResult->mRemoteDocumentSize) &&
    491         ReadParam(aReader, &aResult->mReferentId) &&
    492         ReadParam(aReader, &aResult->mEventRegionsOverride) &&
    493         ReadParam(aReader, &aResult->mScrollbarData) &&
    494         ReadParam(aReader, &aResult->mScrollbarAnimationId) &&
    495         ReadParam(aReader, &aResult->mFixedPositionAnimationId) &&
    496         ReadParam(aReader, &aResult->mFixedPositionSides) &&
    497         ReadParam(aReader, &aResult->mFixedPosScrollContainerId) &&
    498         ReadParam(aReader, &aResult->mStickyPosScrollContainerId) &&
    499         ReadParam(aReader, &aResult->mStickyScrollRangeOuter) &&
    500         ReadParam(aReader, &aResult->mStickyScrollRangeInner) &&
    501         ReadParam(aReader, &aResult->mStickyPositionAnimationId) &&
    502         ReadParam(aReader, &aResult->mZoomAnimationId) &&
    503         ReadParam(aReader, &aResult->mAsyncZoomContainerId);
    504 }
    505 
    506 void ParamTraits<mozilla::layers::WebRenderScrollData>::Write(
    507    MessageWriter* aWriter, const paramType& aParam) {
    508  WriteParam(aWriter, aParam.mScrollMetadatas);
    509  WriteParam(aWriter, aParam.mLayerScrollData);
    510  WriteParam(aWriter, aParam.mIsFirstPaint);
    511  WriteParam(aWriter, aParam.mPaintSequenceNumber);
    512 }
    513 
    514 bool ParamTraits<mozilla::layers::WebRenderScrollData>::Read(
    515    MessageReader* aReader, paramType* aResult) {
    516  return ReadParam(aReader, &aResult->mScrollMetadatas) &&
    517         ReadParam(aReader, &aResult->mLayerScrollData) &&
    518         ReadParam(aReader, &aResult->mIsFirstPaint) &&
    519         ReadParam(aReader, &aResult->mPaintSequenceNumber) &&
    520         aResult->RepopulateMap();
    521 }
    522 
    523 }  // namespace IPC