void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const { // Search up the parent chain until we find the first one that's scrollable. AccessibilityObject* scrollParent = parentObject(); ScrollableArea* scrollableArea; for (scrollableArea = 0; scrollParent && !(scrollableArea = scrollParent->getScrollableAreaIfScrollable()); scrollParent = scrollParent->parentObject()) { } if (!scrollableArea) return; LayoutRect objectRect = elementRect(); IntPoint scrollPosition = scrollableArea->scrollPosition(); IntRect scrollVisibleRect = scrollableArea->visibleContentRect(); int desiredX = computeBestScrollOffset( scrollPosition.x(), objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(), objectRect.x(), objectRect.maxX(), 0, scrollVisibleRect.width()); int desiredY = computeBestScrollOffset( scrollPosition.y(), objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(), objectRect.y(), objectRect.maxY(), 0, scrollVisibleRect.height()); scrollParent->scrollTo(IntPoint(desiredX, desiredY)); // Recursively make sure the scroll parent itself is visible. if (scrollParent->parentObject()) scrollParent->scrollToMakeVisible(); }
InRegionScrollableArea::InRegionScrollableArea(WebPagePrivate* webPage, RenderLayer* layer) : m_webPage(webPage) , m_layer(layer) { ASSERT(webPage); ASSERT(layer); m_isNull = false; // FIXME: Add an ASSERT here as the 'layer' must be scrollable. RenderObject* layerRenderer = layer->renderer(); ASSERT(layerRenderer); if (layerRenderer->isRenderView()) { // #document case FrameView* view = toRenderView(layerRenderer)->frameView(); ASSERT(view); Frame* frame = view->frame(); ASSERT_UNUSED(frame, frame); m_scrollPosition = m_webPage->mapToTransformed(view->scrollPosition()); m_contentsSize = m_webPage->mapToTransformed(view->contentsSize()); m_viewportSize = m_webPage->mapToTransformed(view->visibleContentRect(false /*includeScrollbars*/)).size(); m_visibleWindowRect = m_webPage->mapToTransformed(m_webPage->getRecursiveVisibleWindowRect(view)); IntRect transformedWindowRect = IntRect(IntPoint::zero(), m_webPage->transformedViewportSize()); m_visibleWindowRect.intersect(transformedWindowRect); m_scrollsHorizontally = view->contentsWidth() > view->visibleWidth(); m_scrollsVertically = view->contentsHeight() > view->visibleHeight(); m_overscrollLimitFactor = 0.0; // FIXME eventually support overscroll } else { // RenderBox-based elements case (scrollable boxes (div's, p's, textarea's, etc)). RenderBox* box = m_layer->renderBox(); ASSERT(box); ASSERT(box->canBeScrolledAndHasScrollableArea()); ScrollableArea* scrollableArea = static_cast<ScrollableArea*>(m_layer); m_scrollPosition = m_webPage->mapToTransformed(scrollableArea->scrollPosition()); m_contentsSize = m_webPage->mapToTransformed(scrollableArea->contentsSize()); m_viewportSize = m_webPage->mapToTransformed(scrollableArea->visibleContentRect(false /*includeScrollbars*/)).size(); m_visibleWindowRect = m_layer->renderer()->absoluteClippedOverflowRect(); m_visibleWindowRect = m_layer->renderer()->frame()->view()->contentsToWindow(m_visibleWindowRect); IntRect visibleFrameWindowRect = m_webPage->getRecursiveVisibleWindowRect(m_layer->renderer()->frame()->view()); m_visibleWindowRect.intersect(visibleFrameWindowRect); m_visibleWindowRect = m_webPage->mapToTransformed(m_visibleWindowRect); IntRect transformedWindowRect = IntRect(IntPoint::zero(), m_webPage->transformedViewportSize()); m_visibleWindowRect.intersect(transformedWindowRect); m_scrollsHorizontally = box->scrollWidth() != box->clientWidth() && box->scrollsOverflowX(); m_scrollsVertically = box->scrollHeight() != box->clientHeight() && box->scrollsOverflowY(); m_overscrollLimitFactor = 0.0; // FIXME eventually support overscroll } }
// Tests that the visible rect (i.e. visual viewport rect) is correctly // calculated, taking into account both viewports and page scale. TEST_F(RootFrameViewportTest, VisibleContentRect) { IntSize viewportSize(500, 401); RootFrameViewStub* layoutViewport = RootFrameViewStub::create(viewportSize, IntSize(1000, 2000)); VisualViewportStub* visualViewport = VisualViewportStub::create(viewportSize, viewportSize); ScrollableArea* rootFrameViewport = RootFrameViewport::create(*visualViewport, *layoutViewport); rootFrameViewport->setScrollOffset(ScrollOffset(100, 75), ProgrammaticScroll); EXPECT_POINT_EQ(IntPoint(100, 75), rootFrameViewport->visibleContentRect().location()); EXPECT_SIZE_EQ(ScrollOffset(500, 401), rootFrameViewport->visibleContentRect().size()); visualViewport->setScale(2); EXPECT_POINT_EQ(IntPoint(100, 75), rootFrameViewport->visibleContentRect().location()); EXPECT_SIZE_EQ(ScrollOffset(250, 201), rootFrameViewport->visibleContentRect().size()); }
void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const { // Search up the parent chain until we find the first one that's scrollable. AXObject* scrollParent = parentObject(); ScrollableArea* scrollableArea = 0; while (scrollParent) { scrollableArea = scrollParent->getScrollableAreaIfScrollable(); if (scrollableArea && !scrollParent->isAXScrollView()) break; scrollParent = scrollParent->parentObject(); } if (!scrollParent || !scrollableArea) return; IntRect objectRect = pixelSnappedIntRect(elementRect()); IntPoint scrollPosition = scrollableArea->scrollPosition(); IntRect scrollVisibleRect = scrollableArea->visibleContentRect(); // Convert the object rect into local coordinates. if (!scrollParent->isWebArea()) { objectRect.moveBy(scrollPosition); objectRect.moveBy(-pixelSnappedIntRect(scrollParent->elementRect()).location()); } int desiredX = computeBestScrollOffset( scrollPosition.x(), objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(), objectRect.x(), objectRect.maxX(), 0, scrollVisibleRect.width()); int desiredY = computeBestScrollOffset( scrollPosition.y(), objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(), objectRect.y(), objectRect.maxY(), 0, scrollVisibleRect.height()); scrollParent->setScrollOffset(IntPoint(desiredX, desiredY)); // Convert the subfocus into the coordinates of the scroll parent. IntRect newSubfocus = subfocus; IntRect newElementRect = pixelSnappedIntRect(elementRect()); IntRect scrollParentRect = pixelSnappedIntRect(scrollParent->elementRect()); newSubfocus.move(newElementRect.x(), newElementRect.y()); newSubfocus.move(-scrollParentRect.x(), -scrollParentRect.y()); // Recursively make sure the scroll parent itself is visible. if (scrollParent->parentObject()) scrollParent->scrollToMakeVisibleWithSubFocus(newSubfocus); }
InRegionScrollableArea::InRegionScrollableArea(WebPagePrivate* webPage, RenderLayer* layer) : m_webPage(webPage) , m_layer(layer) , m_document(0) , m_hasWindowVisibleRectCalculated(false) { ASSERT(webPage); ASSERT(layer); m_isNull = false; // Add a pointer to the enclosing document as the pointer to layer or node along the way may become invalid. if (m_layer->enclosingElement()) m_document = m_layer->enclosingElement()->document(); // FIXME: Add an ASSERT here as the 'layer' must be scrollable. RenderObject* layerRenderer = layer->renderer(); ASSERT(layerRenderer); if (layerRenderer->isRenderView()) { // #document case RenderView* renderView = toRenderView(layerRenderer); ASSERT(renderView); FrameView* view = toRenderView(layerRenderer)->frameView(); ASSERT(view); Frame* frame = view->frame(); ASSERT_UNUSED(frame, frame); const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor; m_scrollPosition = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatPoint(view->scrollPosition())); m_contentsSize = viewportAccessor->roundToPixelFromDocumentContents(Platform::FloatRect(Platform::FloatPoint::zero(), WebCore::FloatSize(view->contentsSize()))).size(); m_viewportSize = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(view->visibleContentRect(ScrollableArea::ExcludeScrollbars))).size(); m_documentViewportRect = view->frameRect(); m_scrollsHorizontally = view->contentsWidth() > view->visibleWidth(); m_scrollsVertically = view->contentsHeight() > view->visibleHeight(); m_supportsCompositedScrolling = true; m_scrollTarget = InnerFrame; ASSERT(!m_cachedNonCompositedScrollableNode); m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(renderView->compositor()->scrollLayer()->platformLayer()); m_cachedCompositedScrollableLayer = renderView->compositor()->scrollLayer()->platformLayer(); } else { // RenderBox-based elements case (scrollable boxes (div's, p's, textarea's, etc)). RenderBox* box = m_layer->renderBox(); ASSERT(box); ASSERT(InRegionScrollerPrivate::canScrollRenderBox(box)); const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor; ScrollableArea* scrollableArea = static_cast<ScrollableArea*>(m_layer); m_scrollPosition = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatPoint(scrollableArea->scrollPosition())); m_contentsSize = viewportAccessor->roundToPixelFromDocumentContents(Platform::FloatRect(Platform::FloatPoint::zero(), WebCore::FloatSize(scrollableArea->contentsSize()))).size(); m_viewportSize = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(scrollableArea->visibleContentRect(ScrollableArea::ExcludeScrollbars))).size(); m_documentViewportRect = enclosingIntRect(box->absoluteClippedOverflowRect()); m_scrollsHorizontally = box->scrollWidth() != box->clientWidth(); m_scrollsVertically = box->scrollHeight() != box->clientHeight(); // Check the overflow if its not an input field because overflow can be set to hidden etc. by the content. if (!DOMSupport::isShadowHostTextInputElement(box->node())) { m_scrollsHorizontally = m_scrollsHorizontally && box->scrollsOverflowX(); m_scrollsVertically = m_scrollsVertically && box->scrollsOverflowY(); } m_scrollTarget = BlockElement; // Both caches below are self-exclusive. if (m_layer->usesCompositedScrolling()) { m_forceContentToBeHorizontallyScrollable = m_scrollsHorizontally; m_forceContentToBeVerticallyScrollable = m_scrollsVertically; // Force content to be scrollable even if it doesn't need to scroll in either direction. if (!m_scrollsHorizontally && !m_scrollsVertically) { if (box->scrollsOverflowY()) m_forceContentToBeVerticallyScrollable = true; else if (box->scrollsOverflowX()) // If it's already forced scrollable vertically, don't force it to scroll horizontally m_forceContentToBeHorizontallyScrollable = true; } m_supportsCompositedScrolling = true; ASSERT(m_layer->backing()->hasScrollingLayer()); m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(m_layer->backing()->scrollingContentsLayer()->platformLayer()); m_cachedCompositedScrollableLayer = m_layer->backing()->scrollingContentsLayer()->platformLayer(); ASSERT(!m_cachedNonCompositedScrollableNode); } else { m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(m_layer->enclosingElement()); m_cachedNonCompositedScrollableNode = m_layer->enclosingElement(); ASSERT(!m_cachedCompositedScrollableLayer); } } }
InRegionScrollableArea::InRegionScrollableArea(WebPagePrivate* webPage, RenderLayer* layer) : m_webPage(webPage) , m_layer(layer) , m_document(0) , m_hasWindowVisibleRectCalculated(false) { ASSERT(webPage); ASSERT(layer); m_isNull = false; // Add a pointer to the enclosing document as the pointer to layer or node along the way may become invalid. if (m_layer->enclosingElement()) m_document = m_layer->enclosingElement()->document(); // FIXME: Add an ASSERT here as the 'layer' must be scrollable. RenderObject* layerRenderer = layer->renderer(); ASSERT(layerRenderer); if (layerRenderer->isRenderView()) { // #document case RenderView* renderView = toRenderView(layerRenderer); ASSERT(renderView); FrameView* view = toRenderView(layerRenderer)->frameView(); ASSERT(view); Frame* frame = view->frame(); ASSERT_UNUSED(frame, frame); m_scrollPosition = m_webPage->mapToTransformed(view->scrollPosition()); m_contentsSize = m_webPage->mapToTransformed(view->contentsSize()); m_viewportSize = m_webPage->mapToTransformed(view->visibleContentRect(ScrollableArea::ExcludeScrollbars)).size(); m_documentViewportRect = view->frameRect(); m_scrollsHorizontally = view->contentsWidth() > view->visibleWidth(); m_scrollsVertically = view->contentsHeight() > view->visibleHeight(); m_supportsCompositedScrolling = true; m_scrollTarget = InnerFrame; ASSERT(!m_cachedNonCompositedScrollableNode); m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(renderView->compositor()->scrollLayer()->platformLayer()); m_cachedCompositedScrollableLayer = renderView->compositor()->scrollLayer()->platformLayer(); } else { // RenderBox-based elements case (scrollable boxes (div's, p's, textarea's, etc)). RenderBox* box = m_layer->renderBox(); ASSERT(box); ASSERT(InRegionScrollerPrivate::canScrollRenderBox(box)); ScrollableArea* scrollableArea = static_cast<ScrollableArea*>(m_layer); m_scrollPosition = m_webPage->mapToTransformed(scrollableArea->scrollPosition()); m_contentsSize = m_webPage->mapToTransformed(scrollableArea->contentsSize()); m_viewportSize = m_webPage->mapToTransformed(scrollableArea->visibleContentRect(ScrollableArea::ExcludeScrollbars)).size(); m_documentViewportRect = enclosingIntRect(box->absoluteClippedOverflowRect()); m_scrollsHorizontally = box->scrollWidth() != box->clientWidth(); m_scrollsVertically = box->scrollHeight() != box->clientHeight(); // Check the overflow if its not an input field because overflow can be set to hidden etc. by the content. if (!box->node() || !box->node()->rendererIsEditable()) { m_scrollsHorizontally = m_scrollsHorizontally && box->scrollsOverflowX(); m_scrollsVertically = m_scrollsVertically && box->scrollsOverflowY(); } m_scrollTarget = BlockElement; // Both caches below are self-exclusive. if (m_layer->usesCompositedScrolling()) { m_forceContentToBeVerticallyScrollable = true; m_supportsCompositedScrolling = true; ASSERT(m_layer->backing()->hasScrollingLayer()); m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(m_layer->backing()->scrollingContentsLayer()->platformLayer()); m_cachedCompositedScrollableLayer = m_layer->backing()->scrollingContentsLayer()->platformLayer(); ASSERT(!m_cachedNonCompositedScrollableNode); } else { m_camouflagedCompositedScrollableLayer = reinterpret_cast<unsigned>(m_layer->enclosingElement()); m_cachedNonCompositedScrollableNode = m_layer->enclosingElement(); ASSERT(!m_cachedCompositedScrollableLayer); } } }
// Test that the scrollIntoView correctly scrolls the main frame // and visual viewport such that the given rect is centered in the viewport. TEST_F(RootFrameViewportTest, ScrollIntoView) { IntSize viewportSize(100, 150); RootFrameViewStub* layoutViewport = RootFrameViewStub::create(viewportSize, IntSize(200, 300)); VisualViewportStub* visualViewport = VisualViewportStub::create(viewportSize, viewportSize); ScrollableArea* rootFrameViewport = RootFrameViewport::create(*visualViewport, *layoutViewport); // Test that the visual viewport is scrolled if the viewport has been // resized (as is the case when the ChromeOS keyboard comes up) but not // scaled. visualViewport->setViewportSize(IntSize(100, 100)); rootFrameViewport->scrollIntoView(LayoutRect(100, 250, 50, 50), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); EXPECT_SIZE_EQ(ScrollOffset(50, 150), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 50), visualViewport->getScrollOffset()); rootFrameViewport->scrollIntoView(LayoutRect(25, 75, 50, 50), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); EXPECT_SIZE_EQ(ScrollOffset(25, 75), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 0), visualViewport->getScrollOffset()); // Reset the visual viewport's size, scale the page, and repeat the test visualViewport->setViewportSize(IntSize(100, 150)); visualViewport->setScale(2); rootFrameViewport->setScrollOffset(ScrollOffset(), ProgrammaticScroll); rootFrameViewport->scrollIntoView(LayoutRect(50, 75, 50, 75), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(50, 75), visualViewport->getScrollOffset()); rootFrameViewport->scrollIntoView(LayoutRect(190, 290, 10, 10), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); EXPECT_SIZE_EQ(ScrollOffset(100, 150), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(50, 75), visualViewport->getScrollOffset()); // Scrolling into view the viewport rect itself should be a no-op. visualViewport->setViewportSize(IntSize(100, 100)); visualViewport->setScale(1.5f); visualViewport->setScrollOffset(ScrollOffset(0, 10), ProgrammaticScroll); layoutViewport->setScrollOffset(ScrollOffset(50, 50), ProgrammaticScroll); rootFrameViewport->setScrollOffset(rootFrameViewport->getScrollOffset(), ProgrammaticScroll); rootFrameViewport->scrollIntoView( LayoutRect(rootFrameViewport->visibleContentRect(ExcludeScrollbars)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); EXPECT_SIZE_EQ(ScrollOffset(50, 50), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 10), visualViewport->getScrollOffset()); rootFrameViewport->scrollIntoView( LayoutRect(rootFrameViewport->visibleContentRect(ExcludeScrollbars)), ScrollAlignment::alignCenterAlways, ScrollAlignment::alignCenterAlways); EXPECT_SIZE_EQ(ScrollOffset(50, 50), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 10), visualViewport->getScrollOffset()); rootFrameViewport->scrollIntoView( LayoutRect(rootFrameViewport->visibleContentRect(ExcludeScrollbars)), ScrollAlignment::alignTopAlways, ScrollAlignment::alignTopAlways); EXPECT_SIZE_EQ(ScrollOffset(50, 50), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 10), visualViewport->getScrollOffset()); }