/** Implementation of PanZoomTarget */
 public void setAnimationTarget(ImmutableViewportMetrics metrics) {
   if (mGeckoIsReady) {
     // We know what the final viewport of the animation is going to be, so
     // immediately request a draw of that area by setting the display port
     // accordingly. This way we should have the content pre-rendered by the
     // time the animation is done.
     DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null);
     adjustViewport(displayPort);
   }
 }
  /**
   * This function is invoked by Gecko via JNI; be careful when modifying signature. The compositor
   * invokes this function just before compositing a frame where the document is different from the
   * document composited on the last frame. In these cases, the viewport information we have in Java
   * is no longer valid and needs to be replaced with the new viewport information provided.
   * setPageRect will never be invoked on the same frame that this function is invoked on; and this
   * function will always be called prior to syncViewportInfo.
   */
  public void setFirstPaintViewport(
      float offsetX,
      float offsetY,
      float zoom,
      float pageLeft,
      float pageTop,
      float pageRight,
      float pageBottom,
      float cssPageLeft,
      float cssPageTop,
      float cssPageRight,
      float cssPageBottom) {
    synchronized (this) {
      final ImmutableViewportMetrics newMetrics =
          getViewportMetrics()
              .setViewportOrigin(offsetX, offsetY)
              .setZoomFactor(zoom)
              .setPageRect(
                  new RectF(pageLeft, pageTop, pageRight, pageBottom),
                  new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom));
      // Since we have switched to displaying a different document, we need to update any
      // viewport-related state we have lying around. This includes mGeckoViewport and
      // mViewportMetrics. Usually this information is updated via handleViewportMessage
      // while we remain on the same document.
      post(
          new Runnable() {
            public void run() {
              mGeckoViewport = newMetrics;
            }
          });
      setViewportMetrics(newMetrics);

      Tab tab = Tabs.getInstance().getSelectedTab();
      mView.setBackgroundColor(tab.getBackgroundColor());
      setZoomConstraints(tab.getZoomConstraints());

      // At this point, we have just switched to displaying a different document than we
      // we previously displaying. This means we need to abort any panning/zooming animations
      // that are in progress and send an updated display port request to browser.js as soon
      // as possible. We accomplish this by passing true to abortPanZoomAnimation, which
      // sends the request after aborting the animation. The display port request is actually
      // a full viewport update, which is fine because if browser.js has somehow moved to
      // be out of sync with this first-paint viewport, then we force them back in sync.
      abortPanZoomAnimation();

      // Indicate that the document is about to be composited so the
      // LayerView background can be removed.
      if (mView.getPaintState() == LayerView.PAINT_START) {
        mView.setPaintState(LayerView.PAINT_BEFORE_FIRST);
      }
    }
    DisplayPortCalculator.resetPageState();
    mDrawTimingQueue.reset();
  }
  /** Attaches to root layer so that Gecko appears. */
  public void notifyGeckoReady() {
    mGeckoIsReady = true;

    mRootLayer = new VirtualLayer(new IntSize(mView.getWidth(), mView.getHeight()));
    mLayerRenderer = mView.getRenderer();

    mView.setListener(this);
    sendResizeEventIfNecessary(true);

    DisplayPortCalculator.initPrefs();
    PluginLayer.initPrefs();
  }
  /**
   * 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));
  }
 public DisplayPortMetrics getDisplayPort(
     boolean pageSizeUpdate,
     boolean isBrowserContentDisplayed,
     int tabId,
     ImmutableViewportMetrics metrics) {
   Tabs tabs = Tabs.getInstance();
   if (tabs.isSelectedTab(tabs.getTab(tabId)) && isBrowserContentDisplayed) {
     // for foreground tabs, send the viewport update unless the document
     // displayed is different from the content document. In that case, just
     // calculate the display port.
     return handleViewportMessage(
         metrics, pageSizeUpdate ? ViewportMessageType.PAGE_SIZE : ViewportMessageType.UPDATE);
   } else {
     // for background tabs, request a new display port calculation, so that
     // when we do switch to that tab, we have the correct display port and
     // don't need to draw twice (once to allow the first-paint viewport to
     // get to java, and again once java figures out the display port).
     return DisplayPortCalculator.calculate(metrics, null);
   }
 }
  /**
   * This function is invoked by Gecko via JNI; be careful when modifying signature. The compositor
   * invokes this function on every frame to figure out what part of the page to display, and to
   * inform Java of the current display port. Since it is called on every frame, it needs to be
   * ultra-fast. It avoids taking any locks or allocating any objects. We keep around a
   * mCurrentViewTransform so we don't need to allocate a new ViewTransform everytime we're called.
   * NOTE: we might be able to return a ImmutableViewportMetrics which would avoid the copy into
   * mCurrentViewTransform.
   */
  public ViewTransform syncViewportInfo(
      int x, int y, int width, int height, float resolution, boolean layersUpdated) {
    // getViewportMetrics is thread safe so we don't need to synchronize.
    // We save the viewport metrics here, so we later use it later in
    // createFrame (which will be called by nsWindow::DrawWindowUnderlay on
    // the native side, by the compositor). The viewport
    // metrics can change between here and there, as it's accessed outside
    // of the compositor thread.
    mFrameMetrics = getViewportMetrics();

    mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft;
    mCurrentViewTransform.y = mFrameMetrics.viewportRectTop;
    mCurrentViewTransform.scale = mFrameMetrics.zoomFactor;

    mRootLayer.setPositionAndResolution(x, y, x + width, y + height, resolution);

    if (layersUpdated && mRecordDrawTimes) {
      // If we got a layers update, that means a draw finished. Check to see if the area drawn
      // matches
      // one of our requested displayports; if it does calculate the draw time and notify the
      // DisplayPortCalculator
      DisplayPortMetrics drawn = new DisplayPortMetrics(x, y, x + width, y + height, resolution);
      long time = mDrawTimingQueue.findTimeFor(drawn);
      if (time >= 0) {
        long now = SystemClock.uptimeMillis();
        time = now - time;
        mRecordDrawTimes = DisplayPortCalculator.drawTimeUpdate(time, width * height);
      }
    }

    if (layersUpdated && mDrawListener != null) {
      /* Used by robocop for testing purposes */
      mDrawListener.drawFinished();
    }

    return mCurrentViewTransform;
  }
  /** Viewport message handler. */
  private DisplayPortMetrics handleViewportMessage(
      ImmutableViewportMetrics messageMetrics, ViewportMessageType type) {
    synchronized (this) {
      ImmutableViewportMetrics metrics;
      ImmutableViewportMetrics oldMetrics = getViewportMetrics();

      switch (type) {
        default:
        case UPDATE:
          // Keep the old viewport size
          metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight());
          abortPanZoomAnimation();
          break;
        case PAGE_SIZE:
          // adjust the page dimensions to account for differences in zoom
          // between the rendered content (which is what Gecko tells us)
          // and our zoom level (which may have diverged).
          float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor;
          metrics =
              oldMetrics.setPageRect(
                  RectUtils.scale(messageMetrics.getPageRect(), scaleFactor),
                  messageMetrics.getCssPageRect());
          break;
      }

      final ImmutableViewportMetrics newMetrics = metrics;
      post(
          new Runnable() {
            public void run() {
              mGeckoViewport = newMetrics;
            }
          });
      setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE);
      mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null);
    }
    return mDisplayPort;
  }
  // 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;
  }