@Override
  public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height) {
    myFpsMeter.onPaintStarted();
    JBHiDPIScaledImage image = popup ? myPopupImage : myImage;

    Dimension size = getRealImageSize(image);
    if (size.width != width || size.height != height) {
      image = (JBHiDPIScaledImage)RetinaImage.createFrom(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE),
                                                         getPixelDensity(), null);
    }

    assert image != null;
    if (popup) {
      synchronized (myPopupMutex) {
        drawByteBuffer(image, buffer, dirtyRects);
        myPopupImage = image;
      }
    }
    else {
      drawByteBuffer(image, buffer, dirtyRects);
      myImage = image;
    }

    myContentOutdated = true;
    SwingUtilities.invokeLater(() -> {
      if (!browser.getUIComponent().isShowing()) return;
      Component component = browser.getUIComponent();
      JRootPane root = SwingUtilities.getRootPane(component);
      RepaintManager rm = RepaintManager.currentManager(root);
      Rectangle dirtySrc = new Rectangle(0, 0, component.getWidth(), component.getHeight());
      Rectangle dirtyDst = SwingUtilities.convertRectangle(component, dirtySrc, root);
      int dx = 1;
      // NOTE: should mark area outside browser (otherwise background component won't be repainted)
      rm.addDirtyRegion(root, dirtyDst.x - dx, dirtyDst.y - dx, dirtyDst.width + dx * 2, dirtyDst.height + dx * 2);
    });

    { // notify fps-meter
      long pixCount = 0;
      for (Rectangle r : dirtyRects)
        pixCount += (long)r.width * r.height;
      myFpsMeter.onPaintFinished(pixCount);
    }
  }
@Override
  public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height)