TEST_F(ExternalPopupMenuTest, PopupAccountsForVisualViewportOffset)
{
    registerMockedURLLoad("select_mid_screen.html");
    loadFrame("select_mid_screen.html");

    webView()->resize(WebSize(100, 100));
    webView()->updateAllLifecyclePhases();

    HTMLSelectElement* select = toHTMLSelectElement(mainFrame()->frame()->document()->getElementById("select"));
    LayoutMenuList* menuList = toLayoutMenuList(select->layoutObject());
    ASSERT_TRUE(menuList);

    VisualViewport& visualViewport = webView()->page()->frameHost().visualViewport();

    IntRect rectInDocument = menuList->absoluteBoundingBoxRect();

    webView()->setPageScaleFactor(2);
    IntPoint scrollDelta(20, 30);
    visualViewport.move(scrollDelta);

    select->showPopup();

    EXPECT_EQ(rectInDocument.x() - scrollDelta.x(), client().shownBounds().x);
    EXPECT_EQ(rectInDocument.y() - scrollDelta.y(), client().shownBounds().y);
}
void ScrollableArea::scrollIntoRect(const LayoutRect& rectInContent, const FloatRect& targetRectInFrame)
{
    // Use |pixelSnappedIntRect| for rounding to pixel as opposed to |enclosingIntRect|. It gives a better
    // combined (location and size) rounding error resulting in a more accurate scroll offset.
    // FIXME: It would probably be best to do the whole calculation in LayoutUnits but contentsToRootFrame
    // and friends don't have LayoutRect/Point versions yet.
    IntRect boundsInContent = pixelSnappedIntRect(rectInContent);
    IntRect boundsInFrame(boundsInContent.location() - toIntSize(scrollPosition()), boundsInContent.size());

    int centeringOffsetX = (targetRectInFrame.width() - boundsInFrame.width()) / 2;
    int centeringOffsetY = (targetRectInFrame.height() - boundsInFrame.height()) / 2;

    IntSize scrollDelta(
        boundsInFrame.x() - centeringOffsetX - targetRectInFrame.x(),
        boundsInFrame.y() - centeringOffsetY - targetRectInFrame.y());

    DoublePoint targetOffset = DoublePoint(scrollPosition() + scrollDelta);

    setScrollPosition(targetOffset, ProgrammaticScroll);
}