void
OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const
{
  uint32_t i = IndexOf(aStart);
  for (; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed()) {
      apzc->SnapBackIfOverscrolled();
    }
  }
}
void
OverscrollHandoffChain::SnapBackOverscrolledApzc() const
{
  for (uint32_t i = 0; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed() && apzc->SnapBackIfOverscrolled()) {
      // At most one APZC along the hand-off chain can be overscrolled.
      break;
    }
  }
}
bool
LayerTransactionParent::RecvSetAsyncScrollOffset(PLayerParent* aLayer,
                                                 const int32_t& aX, const int32_t& aY)
{
  if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
    return false;
  }

  Layer* layer = cast(aLayer)->AsLayer();
  if (!layer) {
    return false;
  }
  ContainerLayer* containerLayer = layer->AsContainerLayer();
  if (!containerLayer) {
    return false;
  }
  AsyncPanZoomController* controller = containerLayer->GetAsyncPanZoomController();
  if (!controller) {
    return false;
  }
  controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY));
  return true;
}
void
OverscrollHandoffChain::SnapBackOverscrolledApzc() const
{
  uint32_t i = 0;
  for (i = 0; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed() && apzc->SnapBackIfOverscrolled()) {
      // At most one APZC along the hand-off chain can be overscrolled.
      break;
    }
  }

  // In debug builds, verify our assumption that only one APZC is overscrolled.
#ifdef DEBUG
  ++i;
  for (; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed()) {
      MOZ_ASSERT(!apzc->IsOverscrolled());
    }
  }
#endif
}
void
OverscrollHandoffChain::RequestSnapOnLock(Layer::ScrollDirection aAxis) const
{
  for (uint32_t i = 0; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed()) {
      switch (aAxis) {
      case Layer::HORIZONTAL:
        if (!apzc->CanScroll(Layer::VERTICAL)) {
          apzc->RequestSnap();
        }
        break;
      case Layer::VERTICAL:
        if (!apzc->CanScroll(Layer::HORIZONTAL)) {
          apzc->RequestSnap();
        }
        break;
      default:
        MOZ_ASSERT(false);
        break;
      }
    }
  }
}
void
OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const
{
  uint32_t i = IndexOf(aStart);
  for (; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed() && apzc->SnapBackIfOverscrolled()) {
      // At most one APZC from |aStart| onwards can be overscrolled.
      break;
    }
  }

  // In debug builds, verify our assumption that only one APZC from |aStart|
  // onwards is overscrolled.
#ifdef DEBUG
  ++i;
  for (; i < Length(); ++i) {
    AsyncPanZoomController* apzc = mChain[i];
    if (!apzc->IsDestroyed()) {
      MOZ_ASSERT(!apzc->IsOverscrolled());
    }
  }
#endif
}
static void
ApplyAsyncTransformToScrollbarForContent(ContainerLayer* aScrollbar,
                                         Layer* aContent, bool aScrollbarIsChild)
{
  // We only apply the transform if the scroll-target layer has non-container
  // children (i.e. when it has some possibly-visible content). This is to
  // avoid moving scroll-bars in the situation that only a scroll information
  // layer has been built for a scroll frame, as this would result in a
  // disparity between scrollbars and visible content.
  if (aContent->AsContainerLayer() &&
      !LayerHasNonContainerDescendants(aContent->AsContainerLayer())) {
    return;
  }

  const FrameMetrics& metrics = aContent->GetFrameMetrics();
  AsyncPanZoomController* apzc = aContent->GetAsyncPanZoomController();

  Matrix4x4 asyncTransform = apzc->GetCurrentAsyncTransform();
  Matrix4x4 nontransientTransform = apzc->GetNontransientAsyncTransform();
  Matrix4x4 nontransientUntransform = nontransientTransform;
  nontransientUntransform.Invert();
  Matrix4x4 transientTransform = asyncTransform * nontransientUntransform;

  // |transientTransform| represents the amount by which we have scrolled and
  // zoomed since the last paint. Because the scrollbar was sized and positioned based
  // on the painted content, we need to adjust it based on transientTransform so that
  // it reflects what the user is actually seeing now.
  // - The scroll thumb needs to be scaled in the direction of scrolling by the inverse
  //   of the transientTransform scale (representing the zoom). This is because zooming
  //   in decreases the fraction of the whole scrollable rect that is in view.
  // - It needs to be translated in opposite direction of the transientTransform
  //   translation (representing the scroll). This is because scrolling down, which
  //   translates the layer content up, should result in moving the scroll thumb down.
  //   The amount of the translation to the scroll thumb should be such that the ratio
  //   of the translation to the size of the scroll port is the same as the ratio
  //   of the scroll amount to the size of the scrollable rect.
  Matrix4x4 scrollbarTransform;
  if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
    float scale = metrics.CalculateCompositedSizeInCssPixels().height / metrics.mScrollableRect.height;
    scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f, 1.f / transientTransform._22, 1.f);
    scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(0, -transientTransform._42 * scale, 0);
  }
  if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
    float scale = metrics.CalculateCompositedSizeInCssPixels().width / metrics.mScrollableRect.width;
    scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f / transientTransform._11, 1.f, 1.f);
    scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(-transientTransform._41 * scale, 0, 0);
  }

  Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();

  if (aScrollbarIsChild) {
    // If the scrollbar layer is a child of the content it is a scrollbar for, then we
    // need to do an extra untransform to cancel out the transient async transform on
    // the content. This is needed because otherwise that transient async transform is
    // part of the effective transform of this scrollbar, and the scrollbar will jitter
    // as the content scrolls.
    transientTransform.Invert();
    transform = transform * transientTransform;
  }

  // GetTransform already takes the pre- and post-scale into account.  Since we
  // will apply the pre- and post-scale again when computing the effective
  // transform, we must apply the inverses here.
  transform.Scale(1.0f/aScrollbar->GetPreXScale(),
                  1.0f/aScrollbar->GetPreYScale(),
                  1);
  transform = transform * Matrix4x4().Scale(1.0f/aScrollbar->GetPostXScale(),
                                            1.0f/aScrollbar->GetPostYScale(),
                                            1);
  aScrollbar->AsLayerComposite()->SetShadowTransform(transform);
}