/**
   * Returns true if this client is fine with performing a redraw operation or false if it would
   * prefer that the action didn't take place.
   */
  private boolean getRedrawHint() {
    if (mForceRedraw) {
      mForceRedraw = false;
      return true;
    }

    if (!mPanZoomController.getRedrawHint()) {
      return false;
    }

    return DisplayPortCalculator.aboutToCheckerboard(
        mViewportMetrics, mPanZoomController.getVelocityVector(), mDisplayPort);
  }
  private void adjustViewport(DisplayPortMetrics displayPort) {
    ImmutableViewportMetrics metrics = getViewportMetrics();
    ImmutableViewportMetrics clampedMetrics = metrics.clamp();

    if (displayPort == null) {
      displayPort =
          DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector());
    }

    mDisplayPort = displayPort;
    mGeckoViewport = clampedMetrics;

    if (mRecordDrawTimes) {
      mDrawTimingQueue.add(displayPort);
    }

    GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(clampedMetrics, displayPort));
  }
  // This is called on the Gecko thread to determine if we're still interested
  // in the update of this display-port to continue. We can return true here
  // to abort the current update and continue with any subsequent ones. This
  // is useful for slow-to-render pages when the display-port starts lagging
  // behind enough that continuing to draw it is wasted effort.
  public ProgressiveUpdateData progressiveUpdateCallback(
      boolean aHasPendingNewThebesContent,
      float x,
      float y,
      float width,
      float height,
      float resolution,
      boolean lowPrecision) {
    // Skip all low precision draws until we're at risk of checkerboarding
    if (lowPrecision && !mProgressiveUpdateWasInDanger) {
      mProgressiveUpdateData.abort = true;
      return mProgressiveUpdateData;
    }

    // Reset the checkerboard risk flag
    if (!lowPrecision && mLastProgressiveUpdateWasLowPrecision) {
      mProgressiveUpdateWasInDanger = false;
    }
    mLastProgressiveUpdateWasLowPrecision = lowPrecision;

    // Grab a local copy of the last display-port sent to Gecko and the
    // current viewport metrics to avoid races when accessing them.
    DisplayPortMetrics displayPort = mDisplayPort;
    ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
    mProgressiveUpdateData.setViewport(viewportMetrics);
    mProgressiveUpdateData.abort = false;

    // Always abort updates if the resolution has changed. There's no use
    // in drawing at the incorrect resolution.
    if (!FloatUtils.fuzzyEquals(resolution, viewportMetrics.zoomFactor)) {
      Log.d(LOGTAG, "Aborting draw due to resolution change");
      mProgressiveUpdateData.abort = true;
      return mProgressiveUpdateData;
    }

    // Store the high precision displayport for comparison when doing low
    // precision updates.
    if (!lowPrecision) {
      if (!FloatUtils.fuzzyEquals(resolution, mProgressiveUpdateDisplayPort.resolution)
          || !FloatUtils.fuzzyEquals(x, mProgressiveUpdateDisplayPort.getLeft())
          || !FloatUtils.fuzzyEquals(y, mProgressiveUpdateDisplayPort.getTop())
          || !FloatUtils.fuzzyEquals(x + width, mProgressiveUpdateDisplayPort.getRight())
          || !FloatUtils.fuzzyEquals(y + height, mProgressiveUpdateDisplayPort.getBottom())) {
        mProgressiveUpdateDisplayPort =
            new DisplayPortMetrics(x, y, x + width, y + height, resolution);
      }
    }

    // XXX All sorts of rounding happens inside Gecko that becomes hard to
    //     account exactly for. Given we align the display-port to tile
    //     boundaries (and so they rarely vary by sub-pixel amounts), just
    //     check that values are within a couple of pixels of the
    //     display-port bounds.

    // Never abort drawing if we can't be sure we've sent a more recent
    // display-port. If we abort updating when we shouldn't, we can end up
    // with blank regions on the screen and we open up the risk of entering
    // an endless updating cycle.
    if (Math.abs(displayPort.getLeft() - mProgressiveUpdateDisplayPort.getLeft()) <= 2
        && Math.abs(displayPort.getTop() - mProgressiveUpdateDisplayPort.getTop()) <= 2
        && Math.abs(displayPort.getBottom() - mProgressiveUpdateDisplayPort.getBottom()) <= 2
        && Math.abs(displayPort.getRight() - mProgressiveUpdateDisplayPort.getRight()) <= 2) {
      return mProgressiveUpdateData;
    }

    if (!lowPrecision && !mProgressiveUpdateWasInDanger) {
      // If we're not doing low precision draws and we're about to
      // checkerboard, give up and move onto low precision drawing.
      if (DisplayPortCalculator.aboutToCheckerboard(
          viewportMetrics, mPanZoomController.getVelocityVector(), mProgressiveUpdateDisplayPort)) {
        mProgressiveUpdateWasInDanger = true;
      }
    }

    // Abort updates when the display-port no longer contains the visible
    // area of the page (that is, the viewport cropped by the page
    // boundaries).
    // XXX This makes the assumption that we never let the visible area of
    //     the page fall outside of the display-port.
    if (Math.max(viewportMetrics.viewportRectLeft, viewportMetrics.pageRectLeft) + 1 < x
        || Math.max(viewportMetrics.viewportRectTop, viewportMetrics.pageRectTop) + 1 < y
        || Math.min(viewportMetrics.viewportRectRight, viewportMetrics.pageRectRight) - 1
            > x + width
        || Math.min(viewportMetrics.viewportRectBottom, viewportMetrics.pageRectBottom) - 1
            > y + height) {
      Log.d(LOGTAG, "Aborting update due to viewport not in display-port");
      mProgressiveUpdateData.abort = true;
      return mProgressiveUpdateData;
    }

    // Abort drawing stale low-precision content if there's a more recent
    // display-port in the pipeline.
    if (lowPrecision && !aHasPendingNewThebesContent) {
      mProgressiveUpdateData.abort = true;
    }
    return mProgressiveUpdateData;
  }