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();
}
void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
{
    // Search up the parent chain and create a vector of all scrollable parent objects
    // and ending with this object itself.
    Vector<const AccessibilityObject*> objects;
    AccessibilityObject* parentObject;
    for (parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
        if (parentObject->getScrollableAreaIfScrollable())
            objects.prepend(parentObject);
    }
    objects.append(this);

    // Start with the outermost scrollable (the main window) and try to scroll the
    // next innermost object to the given point.
    int offsetX = 0, offsetY = 0;
    IntPoint point = globalPoint;
    size_t levels = objects.size() - 1;
    for (size_t i = 0; i < levels; i++) {
        const AccessibilityObject* outer = objects[i];
        const AccessibilityObject* inner = objects[i + 1];

        ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable();

        LayoutRect innerRect = inner->isAccessibilityScrollView() ? inner->parentObject()->elementRect() : inner->elementRect();
        LayoutRect objectRect = innerRect;
        IntPoint scrollPosition = scrollableArea->scrollPosition();

        // Convert the object rect into local coordinates.
        objectRect.move(offsetX, offsetY);
        if (!outer->isAccessibilityScrollView())
            objectRect.move(scrollPosition.x(), scrollPosition.y());

        int desiredX = computeBestScrollOffset(
            0,
            objectRect.x(), objectRect.maxX(),
            objectRect.x(), objectRect.maxX(),
            point.x(), point.x());
        int desiredY = computeBestScrollOffset(
            0,
            objectRect.y(), objectRect.maxY(),
            objectRect.y(), objectRect.maxY(),
            point.y(), point.y());
        outer->scrollTo(IntPoint(desiredX, desiredY));

        if (outer->isAccessibilityScrollView() && !inner->isAccessibilityScrollView()) {
            // If outer object we just scrolled is a scroll view (main window or iframe) but the
            // inner object is not, keep track of the coordinate transformation to apply to
            // future nested calculations.
            scrollPosition = scrollableArea->scrollPosition();
            offsetX -= (scrollPosition.x() + point.x());
            offsetY -= (scrollPosition.y() + point.y());
            point.move(scrollPosition.x() - innerRect.x(),
                       scrollPosition.y() - innerRect.y());
        } else if (inner->isAccessibilityScrollView()) {
            // Otherwise, if the inner object is a scroll view, reset the coordinate transformation.
            offsetX = 0;
            offsetY = 0;
        }
    }
}
Example #3
0
void FocusController::setContainingWindowIsVisible(bool containingWindowIsVisible)
{
    if (m_containingWindowIsVisible == containingWindowIsVisible)
        return;

    m_containingWindowIsVisible = containingWindowIsVisible;

    FrameView* view = m_page->mainFrame()->view();
    if (!view)
        return;

    contentAreaDidShowOrHide(view, containingWindowIsVisible);

    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        FrameView* frameView = frame->view();
        if (!frameView)
            continue;

        const HashSet<ScrollableArea*>* scrollableAreas = frameView->scrollableAreas();
        if (!scrollableAreas)
            continue;

        for (HashSet<ScrollableArea*>::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;
            ASSERT(scrollableArea->scrollbarsCanBeActive() || m_page->shouldSuppressScrollbarAnimations());

            contentAreaDidShowOrHide(scrollableArea, containingWindowIsVisible);
        }
    }
}
IntPoint AXObject::maximumScrollOffset() const
{
    ScrollableArea* area = getScrollableAreaIfScrollable();
    if (!area)
        return IntPoint();

    return IntPoint(area->maximumScrollPosition().x(), area->maximumScrollPosition().y());
}
Example #5
0
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
    }
}
void AXObject::setScrollOffset(const IntPoint& offset) const
{
    ScrollableArea* area = getScrollableAreaIfScrollable();
    if (!area)
        return;

    // TODO(bokan): This should potentially be a UserScroll.
    area->setScrollPosition(DoublePoint(offset.x(), offset.y()), ProgrammaticScroll);
}
Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const
{
#if ENABLE(IOS_TOUCH_EVENTS)
    // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers.
    ASSERT(frame->isMainFrame());
    UNUSED_PARAM(frameLocation);

    Document* document = frame->document();
    if (!document)
        return Region();

    Vector<IntRect> touchRects;
    document->getTouchRects(touchRects);
    
    Region touchRegion;
    for (const auto& rect : touchRects)
        touchRegion.unite(rect);

    return touchRegion;
#else
    Region nonFastScrollableRegion;
    FrameView* frameView = frame->view();
    if (!frameView)
        return nonFastScrollableRegion;

    IntPoint offset = frameLocation;
    offset.moveBy(frameView->frameRect().location());
    offset.move(0, frameView->topContentInset());

    if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
        for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;
            // Composited scrollable areas can be scrolled off the main thread.
            if (scrollableArea->usesCompositedScrolling())
                continue;
            IntRect box = scrollableArea->scrollableAreaBoundingBox();
            box.moveBy(offset);
            nonFastScrollableRegion.unite(box);
        }
    }

    for (const auto& child : frameView->children()) {
        if (!child->isPluginViewBase())
            continue;
        PluginViewBase* pluginViewBase = toPluginViewBase(child.get());
        if (pluginViewBase->wantsWheelEvents())
            nonFastScrollableRegion.unite(pluginViewBase->frameRect());
    }

    for (Frame* subframe = frame->tree().firstChild(); subframe; subframe = subframe->tree().nextSibling())
        nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subframe, offset));

    return nonFastScrollableRegion;
#endif
}
Example #8
0
ScrollAnimatorNone::ScrollAnimatorNone(ScrollableArea& scrollableArea)
    : ScrollAnimator(scrollableArea)
    , m_horizontalData(this, &m_currentPosX, scrollableArea.visibleWidth())
    , m_verticalData(this, &m_currentPosY, scrollableArea.visibleHeight())
    , m_startTime(0)
#if USE(REQUEST_ANIMATION_FRAME_TIMER)
    , m_animationTimer(*this, &ScrollAnimatorNone::requestAnimationTimerFired)
#else
    , m_animationActive(false)
#endif
{
}
void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea& scrollableArea, ScrollbarOrientation orientation)
{
    ASSERT(isMainThread());
    ASSERT(m_page);

    if (&scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view()))
        return;

    if (orientation == VerticalScrollbar)
        scrollableArea.verticalScrollbarLayerDidChange();
    else
        scrollableArea.horizontalScrollbarLayerDidChange();
}
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);
}
GraphicsLayer* TopDocumentRootScrollerController::rootScrollerLayer() const {
  if (!m_globalRootScroller)
    return nullptr;

  ScrollableArea* area =
      RootScrollerUtil::scrollableAreaFor(*m_globalRootScroller);

  if (!area)
    return nullptr;

  GraphicsLayer* graphicsLayer = area->layerForScrolling();

  // TODO(bokan): We should assert graphicsLayer here and
  // RootScrollerController should do whatever needs to happen to ensure
  // the root scroller gets composited.

  return graphicsLayer;
}
Example #12
0
// Tests that scrolls on the root frame scroll the visual viewport before
// trying to scroll the layout viewport.
TEST_F(RootFrameViewportTest, ViewportScrollOrder) {
  IntSize viewportSize(100, 100);
  RootFrameViewStub* layoutViewport =
      RootFrameViewStub::create(viewportSize, IntSize(200, 300));
  VisualViewportStub* visualViewport =
      VisualViewportStub::create(viewportSize, viewportSize);

  ScrollableArea* rootFrameViewport =
      RootFrameViewport::create(*visualViewport, *layoutViewport);

  visualViewport->setScale(2);

  rootFrameViewport->setScrollOffset(ScrollOffset(40, 40), UserScroll);
  EXPECT_SIZE_EQ(ScrollOffset(40, 40), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset());

  rootFrameViewport->setScrollOffset(ScrollOffset(60, 60), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(50, 50), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(10, 10), layoutViewport->getScrollOffset());
}
Example #13
0
Region ScrollingCoordinator::computeNonFastScrollableRegion(Frame* frame, const IntPoint& frameLocation)
{
    Region nonFastScrollableRegion;
    FrameView* frameView = frame->view();
    if (!frameView)
        return nonFastScrollableRegion;

    IntPoint offset = frameLocation;
    offset.moveBy(frameView->frameRect().location());

    if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
        for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;
#if USE(ACCELERATED_COMPOSITING)
            // Composited scrollable areas can be scrolled off the main thread.
            if (scrollableArea->usesCompositedScrolling())
                continue;
#endif
            IntRect box = scrollableArea->scrollableAreaBoundingBox();
            box.moveBy(offset);
            nonFastScrollableRegion.unite(box);
        }
    }

    if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
        for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
            if (!(*it)->isPluginViewBase())
                continue;

            PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>((*it).get());
            if (pluginViewBase->wantsWheelEvents())
                nonFastScrollableRegion.unite(pluginViewBase->frameRect());
        }
    }

    FrameTree* tree = frame->tree();
    for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling())
        nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subFrame, offset));

    return nonFastScrollableRegion;
}
Example #14
0
static Region computeNonFastScrollableRegion(FrameView* frameView)
{
    Region nonFastScrollableRegion;

    HashSet<FrameView*> childFrameViews;
    for (HashSet<RefPtr<Widget> >::const_iterator it = frameView->children()->begin(), end = frameView->children()->end(); it != end; ++it) {
        if ((*it)->isFrameView())
            childFrameViews.add(static_cast<FrameView*>(it->get()));
    }

    if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
        for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;

            // Check if this area can be scrolled at all.
            // If this scrollable area is a frame view that itself has scrollable areas, then we need to add it to the region.
            if ((!scrollableArea->horizontalScrollbar() || !scrollableArea->horizontalScrollbar()->enabled())
                && (!scrollableArea->verticalScrollbar() || !scrollableArea->verticalScrollbar()->enabled())
                && (!childFrameViews.contains(static_cast<FrameView*>(scrollableArea)) || !static_cast<FrameView*>(scrollableArea)->scrollableAreas()))
                    continue;

            nonFastScrollableRegion.unite(scrollableArea->scrollableAreaBoundingBox());
        }
    }

    return nonFastScrollableRegion;
}
Example #15
0
void Page::setShouldSuppressScrollbarAnimations(bool suppressAnimations)
{
    if (suppressAnimations == m_suppressScrollbarAnimations)
        return;

    if (!suppressAnimations) {
        // If animations are not going to be suppressed anymore, then there is nothing to do here but
        // change the cached value.
        m_suppressScrollbarAnimations = suppressAnimations;
        return;
    }

    // On the other hand, if we are going to start suppressing animations, then we need to make sure we
    // finish any current scroll animations first.
    FrameView* view = mainFrame()->view();
    if (!view)
        return;

    view->finishCurrentScrollAnimations();
    
    for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        FrameView* frameView = frame->view();
        if (!frameView)
            continue;

        const HashSet<ScrollableArea*>* scrollableAreas = frameView->scrollableAreas();
        if (!scrollableAreas)
            continue;

        for (HashSet<ScrollableArea*>::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;
            ASSERT(scrollableArea->scrollbarsCanBeActive());

            scrollableArea->finishCurrentScrollAnimations();
        }
    }

    m_suppressScrollbarAnimations = suppressAnimations;
}
Example #16
0
// Tests that the setScrollOffset method works correctly with both viewports.
TEST_F(RootFrameViewportTest, SetScrollOffset) {
  IntSize viewportSize(500, 500);
  RootFrameViewStub* layoutViewport =
      RootFrameViewStub::create(viewportSize, IntSize(1000, 2000));
  VisualViewportStub* visualViewport =
      VisualViewportStub::create(viewportSize, viewportSize);

  ScrollableArea* rootFrameViewport =
      RootFrameViewport::create(*visualViewport, *layoutViewport);

  visualViewport->setScale(2);

  // Ensure that the visual viewport scrolls first.
  rootFrameViewport->setScrollOffset(ScrollOffset(100, 100),
                                     ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(100, 100), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset());

  // Scroll to the visual viewport's extent, the layout viewport should scroll
  // the remainder.
  rootFrameViewport->setScrollOffset(ScrollOffset(300, 400),
                                     ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(250, 250), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 150), layoutViewport->getScrollOffset());

  // Only the layout viewport should scroll further. Make sure it doesn't scroll
  // out of bounds.
  rootFrameViewport->setScrollOffset(ScrollOffset(780, 1780),
                                     ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(250, 250), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(500, 1500), layoutViewport->getScrollOffset());

  // Scroll all the way back.
  rootFrameViewport->setScrollOffset(ScrollOffset(0, 0), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset());
}
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle)
{
    if (scrollingElementStyle.scrollSnapType() == ScrollSnapType::None) {
        scrollableArea.clearHorizontalSnapOffsets();
        scrollableArea.clearVerticalSnapOffsets();
        return;
    }

    LayoutUnit viewWidth = scrollingElementBox.width();
    LayoutUnit viewHeight = scrollingElementBox.height();
    LayoutUnit scrollWidth = scrollingElementBox.scrollWidth();
    LayoutUnit scrollHeight = scrollingElementBox.scrollHeight();
    bool canComputeHorizontalOffsets = scrollWidth > 0 && viewWidth > 0 && viewWidth < scrollWidth;
    bool canComputeVerticalOffsets = scrollHeight > 0 && viewHeight > 0 && viewHeight < scrollHeight;

    if (!canComputeHorizontalOffsets)
        scrollableArea.clearHorizontalSnapOffsets();
    if (!canComputeVerticalOffsets)
        scrollableArea.clearVerticalSnapOffsets();

    if (!canComputeHorizontalOffsets && !canComputeVerticalOffsets)
        return;

    Vector<LayoutUnit> horizontalSnapOffsetSubsequence;
    Vector<LayoutUnit> verticalSnapOffsetSubsequence;

    bool scrollSnapPointsXUsesElements = styleUsesElements(ScrollEventAxis::Horizontal, scrollingElementStyle);
    bool scrollSnapPointsYUsesElements = styleUsesElements(ScrollEventAxis::Vertical , scrollingElementStyle);

    if (scrollSnapPointsXUsesElements || scrollSnapPointsYUsesElements) {
        bool shouldAddHorizontalChildOffsets = scrollSnapPointsXUsesElements && canComputeHorizontalOffsets;
        bool shouldAddVerticalChildOffsets = scrollSnapPointsYUsesElements && canComputeVerticalOffsets;
        appendChildSnapOffsets(scrollingElement, shouldAddHorizontalChildOffsets, horizontalSnapOffsetSubsequence, shouldAddVerticalChildOffsets, verticalSnapOffsetSubsequence);
    }

    if (scrollingElementStyle.scrollSnapPointsX() && !scrollSnapPointsXUsesElements && canComputeHorizontalOffsets) {
        for (auto& snapLength : scrollingElementStyle.scrollSnapPointsX()->offsets)
            horizontalSnapOffsetSubsequence.append(valueForLength(snapLength, viewWidth));
    }

    if (scrollingElementStyle.scrollSnapPointsY() && !scrollSnapPointsYUsesElements && canComputeVerticalOffsets) {
        for (auto& snapLength : scrollingElementStyle.scrollSnapPointsY()->offsets)
            verticalSnapOffsetSubsequence.append(valueForLength(snapLength, viewHeight));
    }

    if (canComputeHorizontalOffsets) {
        auto horizontalSnapOffsets = std::make_unique<Vector<LayoutUnit>>();
        updateFromStyle(*horizontalSnapOffsets, scrollingElementStyle, ScrollEventAxis::Horizontal, viewWidth, scrollWidth, horizontalSnapOffsetSubsequence);
        scrollableArea.setHorizontalSnapOffsets(WTF::move(horizontalSnapOffsets));
    }
    if (canComputeVerticalOffsets) {
        auto verticalSnapOffsets = std::make_unique<Vector<LayoutUnit>>();
        updateFromStyle(*verticalSnapOffsets, scrollingElementStyle, ScrollEventAxis::Vertical, viewHeight, scrollHeight, verticalSnapOffsetSubsequence);
        scrollableArea.setVerticalSnapOffsets(WTF::move(verticalSnapOffsets));
    }
}
Example #18
0
static Region computeNonFastScrollableRegion(FrameView* frameView)
{
    Region nonFastScrollableRegion;

        if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
        for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
            ScrollableArea* scrollableArea = *it;

            // Check if this area can be scrolled at all.
            if ((!scrollableArea->horizontalScrollbar() || !scrollableArea->horizontalScrollbar()->enabled())
                && (!scrollableArea->verticalScrollbar() || !scrollableArea->verticalScrollbar()->enabled()))
                continue;

            nonFastScrollableRegion.unite(scrollableArea->scrollableAreaBoundingBox());
        }
    }

    return nonFastScrollableRegion;
}
Example #19
0
// 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());
}
Example #20
0
// 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());
}
BOOL OpDocumentEdit::OnInputAction(OpInputAction* action)
{
	DEBUG_CHECKER(TRUE);
	if (!GetDoc())
		return FALSE;

	CheckLogTreeChanged(TRUE);

	switch (action->GetAction())
	{
		case OpInputAction::ACTION_LOWLEVEL_PREFILTER_ACTION:
		{
			OpInputAction* child_action = action->GetChildAction();
			if (child_action->IsKeyboardInvoked())
			{
				DOM_EventType key_event = DOM_EVENT_NONE;
				switch (child_action->GetAction())
				{
				case OpInputAction::ACTION_LOWLEVEL_KEY_DOWN:
					key_event = ONKEYDOWN;
					break;
				case OpInputAction::ACTION_LOWLEVEL_KEY_UP:
					key_event = ONKEYUP;
					break;
				case OpInputAction::ACTION_LOWLEVEL_KEY_PRESSED:
					key_event = ONKEYPRESS;
					break;
				}
				if (key_event != DOM_EVENT_NONE)
				{
					OP_BOOLEAN result = FramesDocument::SendDocumentKeyEvent(GetDoc(), NULL, key_event, child_action->GetActionKeyCode(), child_action->GetKeyValue(), child_action->GetPlatformKeyEventData(), child_action->GetShiftKeys(), child_action->GetKeyRepeat(), child_action->GetKeyLocation(), static_cast<unsigned>(DOM_KEYEVENT_DOCUMENTEDIT));
					if (OpStatus::IsMemoryError(result))
						g_memory_manager->RaiseCondition(OpStatus::ERR_NO_MEMORY);
					return (result == OpBoolean::IS_TRUE);
				}
			}
			return FALSE;
		}
		case OpInputAction::ACTION_GET_ACTION_STATE:
		{
			OpInputAction* child_action = action->GetChildAction();
			OpInputAction::Action action_type = child_action->GetAction();
#ifdef USE_OP_CLIPBOARD
			BOOL force_enabling = FALSE;
			if (action_type == OpInputAction::ACTION_CUT ||
			    action_type == OpInputAction::ACTION_COPY ||
			    action_type == OpInputAction::ACTION_PASTE)
			{
				HTML_Element* target = m_layout_modifier.IsActive() ? m_layout_modifier.m_helm : (m_selection.HasContent() ? m_selection.GetStartElement() : m_caret.GetElement());
				force_enabling = g_clipboard_manager->ForceEnablingClipboardAction(action_type, m_doc, target);
			}
#endif // USE_OP_CLIPBOARD
			switch (action_type)
			{
				case OpInputAction::ACTION_UNDO:
					child_action->SetEnabled(!m_readonly && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_UNDO));
					return TRUE;

				case OpInputAction::ACTION_REDO:
					child_action->SetEnabled(!m_readonly && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_REDO));
					return TRUE;

				case OpInputAction::ACTION_TOGGLE_STYLE_BOLD:
					child_action->SetEnabled(!m_readonly && !m_plain_text_mode && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_BOLD));
					return TRUE;
				case OpInputAction::ACTION_TOGGLE_STYLE_ITALIC:
					child_action->SetEnabled(!m_readonly && !m_plain_text_mode && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_ITALIC));
					return TRUE;
				case OpInputAction::ACTION_TOGGLE_STYLE_UNDERLINE:
					child_action->SetEnabled(!m_readonly && !m_plain_text_mode && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_UNDERLINE));
					return TRUE;

#ifdef USE_OP_CLIPBOARD
				case OpInputAction::ACTION_CUT:
					child_action->SetEnabled(force_enabling || (!m_readonly && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_CUT)));
					return TRUE;
				case OpInputAction::ACTION_COPY:
					child_action->SetEnabled(force_enabling || queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_COPY));
					return TRUE;
				case OpInputAction::ACTION_PASTE:
					child_action->SetEnabled(force_enabling || (!m_readonly && queryCommandEnabled(OP_DOCUMENT_EDIT_COMMAND_PASTE)));
					return TRUE;
#endif // USE_OP_CLIPBOARD
			}
			return FALSE;
		}
		case OpInputAction::ACTION_LOWLEVEL_KEY_PRESSED:
		{
			OpKey::Code key = action->GetActionKeyCode();
#if defined(_DOCEDIT_DEBUG) && defined(USE_OP_CLIPBOARD)
			if (key == OP_KEY_F10)
			{
				m_doc->GetLogicalDocument()->GetRoot()->DumpDebugTree();
				return TRUE;
			}
			if (key == OP_KEY_F11)
			{
				OpString text;
				GetTextHTML(text);

				OP_NEW_DBG("OpDocumentEdit::OnInputAction()", "docedit");
				OP_DBG_LEN(text.CStr(), text.Length());

				return TRUE;
			}
			if (key == OP_KEY_F9)
			{
				const uni_char* tmp = UNI_L("<table border=1><tr><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr></table>");
				InsertTextHTML(tmp, uni_strlen(tmp));
				return TRUE;
			}
#endif // _DOCEDIT_DEBUG && USE_OP_CLIPBOARD */

			ShiftKeyState modifiers = action->GetShiftKeys();
			const uni_char *key_value = action->GetKeyValue();
			BOOL is_key_wanted;
			switch (key)
			{
#ifdef OP_KEY_ENTER_ENABLED
			case OP_KEY_ENTER:
				is_key_wanted = (modifiers & (SHIFTKEY_CTRL | SHIFTKEY_ALT | SHIFTKEY_META)) == 0;
				break;
#endif // OP_KEY_ENTER_ENABLED
#ifdef OP_KEY_TAB_ENABLED
			case OP_KEY_TAB:
				is_key_wanted = m_wants_tab && modifiers == SHIFTKEY_NONE;
				break;
#endif // OP_KEY_TAB_ENABLED
			default:
				/* A keypress involving CTRL is ignored, but not if it involves
				   ALT (or META.) Some key mappings use those modifier combinations
				   for dead/accented keys (e.g., Ctrl-Alt-Z on a Polish keyboard.) */
				if ((modifiers & SHIFTKEY_CTRL) != 0 && (modifiers & (SHIFTKEY_ALT | SHIFTKEY_META)) == 0)
					is_key_wanted = FALSE;
				else
					is_key_wanted = key_value != NULL && key_value[0] >= 32 && key_value[0] != 0x7f;
			}

			if (m_caret.GetElement() && is_key_wanted)
			{
				EditAction(action);
				return TRUE;
			}

			return FALSE;
		}

		case OpInputAction::ACTION_GO_TO_START:
		case OpInputAction::ACTION_GO_TO_END:
		case OpInputAction::ACTION_GO_TO_LINE_START:
		case OpInputAction::ACTION_GO_TO_LINE_END:
		case OpInputAction::ACTION_PAGE_UP:
		case OpInputAction::ACTION_PAGE_DOWN:
		case OpInputAction::ACTION_NEXT_CHARACTER:
		case OpInputAction::ACTION_PREVIOUS_CHARACTER:
		case OpInputAction::ACTION_NEXT_CHARACTER_SPATIAL:
		case OpInputAction::ACTION_PREVIOUS_CHARACTER_SPATIAL:
		case OpInputAction::ACTION_NEXT_WORD:
		case OpInputAction::ACTION_PREVIOUS_WORD:
		case OpInputAction::ACTION_NEXT_LINE:
		case OpInputAction::ACTION_PREVIOUS_LINE:
		case OpInputAction::ACTION_NEXT_LINE_SPATIAL:
		case OpInputAction::ACTION_PREVIOUS_LINE_SPATIAL:
		case OpInputAction::ACTION_RANGE_GO_TO_START:
		case OpInputAction::ACTION_RANGE_GO_TO_END:
		case OpInputAction::ACTION_RANGE_GO_TO_LINE_START:
		case OpInputAction::ACTION_RANGE_GO_TO_LINE_END:
		case OpInputAction::ACTION_RANGE_PAGE_UP:
		case OpInputAction::ACTION_RANGE_PAGE_DOWN:
		case OpInputAction::ACTION_RANGE_NEXT_CHARACTER:
		case OpInputAction::ACTION_RANGE_PREVIOUS_CHARACTER:
		case OpInputAction::ACTION_RANGE_NEXT_WORD:
		case OpInputAction::ACTION_RANGE_PREVIOUS_WORD:
		case OpInputAction::ACTION_RANGE_NEXT_LINE:
		case OpInputAction::ACTION_RANGE_PREVIOUS_LINE:
		case OpInputAction::ACTION_DELETE:
		case OpInputAction::ACTION_DELETE_WORD:
		case OpInputAction::ACTION_DELETE_TO_END_OF_LINE:
		case OpInputAction::ACTION_BACKSPACE:
		case OpInputAction::ACTION_BACKSPACE_WORD:
		case OpInputAction::ACTION_TOGGLE_STYLE_BOLD:
		case OpInputAction::ACTION_TOGGLE_STYLE_ITALIC:
		case OpInputAction::ACTION_TOGGLE_STYLE_UNDERLINE:
		case OpInputAction::ACTION_CONVERT_HEX_TO_UNICODE:
		{
			if (!m_caret.GetElement())
			{
				// Code is unfortunately dependant on m_caret.m_helm. Will be fixed in the next redesign/rewrite.
				return FALSE;
			}

			EditAction(action);
			return TRUE;
		}

		case OpInputAction::ACTION_TOGGLE_OVERSTRIKE:
		{
			if (!m_caret.GetElement())
			{
				// Code is unfortunately dependant on m_caret.m_helm. Will be fixed in the next redesign/rewrite.
				return FALSE;
			}

			m_caret.SetOverstrike(!GetDoc()->GetCaretPainter()->GetOverstrike());
			return TRUE;
		}

#ifdef SUPPORT_TEXT_DIRECTION
		case OpInputAction::ACTION_CHANGE_DIRECTION_TO_LTR:
		case OpInputAction::ACTION_CHANGE_DIRECTION_TO_RTL:
			if (!m_caret.GetElement())
			{
				// Code is unfortunately dependant on m_caret.m_helm. Will be fixed in the next redesign/rewrite.
				return FALSE;
			}

			SetRTL(action->GetAction() == OpInputAction::ACTION_CHANGE_DIRECTION_TO_RTL);
			return TRUE;
#endif // SUPPORT_TEXT_DIRECTION

		case OpInputAction::ACTION_UNDO:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_UNDO));
		case OpInputAction::ACTION_REDO:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_REDO));

#ifdef USE_OP_CLIPBOARD
		case OpInputAction::ACTION_CUT:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_CUT));
		case OpInputAction::ACTION_COPY:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_COPY));
		case OpInputAction::ACTION_PASTE:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_PASTE));
		case OpInputAction::ACTION_PASTE_MOUSE_SELECTION:
		{
			if (!m_caret.GetElement())
			{
				// Code is unfortunately dependant on m_caret.m_helm. Will be fixed in the next redesign/rewrite.
				return FALSE;
			}

# ifdef _X11_SELECTION_POLICY_
			g_clipboard_manager->SetMouseSelectionMode(TRUE);
# endif // _X11_SELECTION_POLICY_
			if( g_clipboard_manager->HasText() )
			{
				if( action->GetActionData() == 1 )
				{
					Clear();
				}
				HTML_Element* target = m_selection.HasContent() ? m_selection.GetStartElement() : m_caret.GetElement();
				g_clipboard_manager->Paste(this, m_doc, target);
			}
# ifdef _X11_SELECTION_POLICY_
			g_clipboard_manager->SetMouseSelectionMode(FALSE);
# endif // _X11_SELECTION_POLICY_
			return TRUE;
		}
#endif // USE_OP_CLIPBOARD

		case OpInputAction::ACTION_SELECT_ALL:
			return OpStatus::IsSuccess(execCommand(OP_DOCUMENT_EDIT_COMMAND_SELECTALL));

#ifdef DEBUG_DOCUMENT_EDIT
		case OpInputAction::ACTION_RELOAD:
		case OpInputAction::ACTION_FORCE_RELOAD:
#endif
		case OpInputAction::ACTION_CLEAR:
			Clear();
			return TRUE;

		case OpInputAction::ACTION_INSERT:
			if (!m_caret.GetElement())
			{
				// Code is unfortunately dependant on m_caret.m_helm. Will be fixed in the next redesign/rewrite.
				return FALSE;
			}

			InsertText(action->GetActionDataString(), uni_strlen(action->GetActionDataString()));
			return TRUE;

		case OpInputAction::ACTION_FOCUS_NEXT_WIDGET:
		case OpInputAction::ACTION_FOCUS_PREVIOUS_WIDGET:
			if (m_caret.GetElement())
			{
				HTML_Element *ec = GetEditableContainer(m_caret.GetElement());
				if (ec && ec->Type() == HE_BODY)
				{
					FramesDocElm *fd_elm = m_doc->GetDocManager()->GetFrame();
					if (fd_elm && fd_elm->IsInlineFrame())
					{
						// This is a IFRAME so we should focus the parentdocument and call this action on it, so
						// focus will be moved within the parentdocument.
						fd_elm->GetCurrentDoc()->GetParentDoc()->GetVisualDevice()->SetFocus(FOCUS_REASON_KEYBOARD);
						OP_BOOLEAN res = fd_elm->GetCurrentDoc()->GetParentDoc()->OnInputAction(action);
						return res == OpBoolean::IS_TRUE;
					}
				}
			}
			return FALSE;
		case OpInputAction::ACTION_SCROLL_UP:
		case OpInputAction::ACTION_SCROLL_DOWN:
		case OpInputAction::ACTION_SCROLL_LEFT:
		case OpInputAction::ACTION_SCROLL_RIGHT:
			if (m_caret.GetElement())
			{
				// Redirect theese actions to a scrollablecontainer so selectionscroll works.
				// The ScrollableContainer is not a parent or child of the DocumentEdits inputcontext.
				HTML_Element *ec = GetEditableContainer(m_caret.GetElement());
				if (ec && ec->GetLayoutBox())
				{
					ScrollableArea* sc = ec->GetLayoutBox()->GetScrollable();
					if (sc)
						return sc->TriggerScrollInputAction(action);
				}
			}
			break;
	}
	return FALSE;
}
Example #22
0
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);
        }
    }
}
Example #23
0
// Tests that scrolling the viewport when the layout viewport is
// !userInputScrollable (as happens when overflow:hidden is set) works
// correctly, that is, the visual viewport can scroll, but not the layout.
TEST_F(RootFrameViewportTest, UserInputScrollable) {
  IntSize viewportSize(100, 150);
  RootFrameViewStub* layoutViewport =
      RootFrameViewStub::create(viewportSize, IntSize(200, 300));
  VisualViewportStub* visualViewport =
      VisualViewportStub::create(viewportSize, viewportSize);

  ScrollableArea* rootFrameViewport =
      RootFrameViewport::create(*visualViewport, *layoutViewport);

  visualViewport->setScale(2);

  // Disable just the layout viewport's horizontal scrolling, the
  // RootFrameViewport should remain scrollable overall.
  layoutViewport->setUserInputScrollable(false, true);
  visualViewport->setUserInputScrollable(true, true);

  EXPECT_TRUE(rootFrameViewport->userInputScrollable(HorizontalScrollbar));
  EXPECT_TRUE(rootFrameViewport->userInputScrollable(VerticalScrollbar));

  // Layout viewport shouldn't scroll since it's not horizontally scrollable,
  // but visual viewport should.
  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(300, 0));
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 0), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 0), rootFrameViewport->getScrollOffset());

  // Vertical scrolling should be unaffected.
  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(0, 300));
  EXPECT_SIZE_EQ(ScrollOffset(0, 150), layoutViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 75), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 225), rootFrameViewport->getScrollOffset());

  // Try the same checks as above but for the vertical direction.
  // ===============================================

  rootFrameViewport->setScrollOffset(ScrollOffset(), ProgrammaticScroll);

  // Disable just the layout viewport's vertical scrolling, the
  // RootFrameViewport should remain scrollable overall.
  layoutViewport->setUserInputScrollable(true, false);
  visualViewport->setUserInputScrollable(true, true);

  EXPECT_TRUE(rootFrameViewport->userInputScrollable(HorizontalScrollbar));
  EXPECT_TRUE(rootFrameViewport->userInputScrollable(VerticalScrollbar));

  // Layout viewport shouldn't scroll since it's not vertically scrollable,
  // but visual viewport should.
  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(0, 300));
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), layoutViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 75), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 75), rootFrameViewport->getScrollOffset());

  // Horizontal scrolling should be unaffected.
  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(300, 0));
  EXPECT_SIZE_EQ(ScrollOffset(100, 0), layoutViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(50, 75), visualViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(150, 75), rootFrameViewport->getScrollOffset());
}
Example #24
0
// Make sure scrolls using the scroll animator (scroll(), setScrollOffset())
// work correctly when one of the subviewports is explicitly scrolled without
// using the // RootFrameViewport interface.
TEST_F(RootFrameViewportTest, TestScrollAnimatorUpdatedBeforeScroll) {
  IntSize viewportSize(100, 150);
  RootFrameViewStub* layoutViewport =
      RootFrameViewStub::create(viewportSize, IntSize(200, 300));
  VisualViewportStub* visualViewport =
      VisualViewportStub::create(viewportSize, viewportSize);

  ScrollableArea* rootFrameViewport =
      RootFrameViewport::create(*visualViewport, *layoutViewport);

  visualViewport->setScale(2);

  visualViewport->setScrollOffset(ScrollOffset(50, 75), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(50, 75), rootFrameViewport->getScrollOffset());

  // If the scroll animator doesn't update, it will still think it's at (0, 0)
  // and so it may early exit.
  rootFrameViewport->setScrollOffset(ScrollOffset(0, 0), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), rootFrameViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 0), visualViewport->getScrollOffset());

  // Try again for userScroll()
  visualViewport->setScrollOffset(ScrollOffset(50, 75), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(50, 75), rootFrameViewport->getScrollOffset());

  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(-50, 0));
  EXPECT_SIZE_EQ(ScrollOffset(0, 75), rootFrameViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 75), visualViewport->getScrollOffset());

  // Make sure the layout viewport is also accounted for.
  rootFrameViewport->setScrollOffset(ScrollOffset(0, 0), ProgrammaticScroll);
  layoutViewport->setScrollOffset(ScrollOffset(100, 150), ProgrammaticScroll);
  EXPECT_SIZE_EQ(ScrollOffset(100, 150), rootFrameViewport->getScrollOffset());

  rootFrameViewport->userScroll(ScrollByPixel, FloatSize(-100, 0));
  EXPECT_SIZE_EQ(ScrollOffset(0, 150), rootFrameViewport->getScrollOffset());
  EXPECT_SIZE_EQ(ScrollOffset(0, 150), layoutViewport->getScrollOffset());
}
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);
        }
    }
}
Example #26
0
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle)
{
    auto* scrollContainer = scrollingElement.renderer();
    auto scrollSnapType = scrollingElementStyle.scrollSnapType();
    if (!scrollContainer || scrollSnapType.strictness == ScrollSnapStrictness::None || scrollContainer->view().boxesWithScrollSnapPositions().isEmpty()) {
        scrollableArea.clearHorizontalSnapOffsets();
        scrollableArea.clearVerticalSnapOffsets();
        return;
    }

    Vector<LayoutUnit> verticalSnapOffsets;
    Vector<LayoutUnit> horizontalSnapOffsets;
    Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges;
    Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges;
    HashSet<float> seenVerticalSnapOffsets;
    HashSet<float> seenHorizontalSnapOffsets;
    bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline;
    bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block;
    auto maxScrollLeft = scrollingElementBox.scrollWidth() - scrollingElementBox.contentWidth();
    auto maxScrollTop = scrollingElementBox.scrollHeight() - scrollingElementBox.contentHeight();
    LayoutPoint containerScrollOffset(scrollingElementBox.scrollLeft(), scrollingElementBox.scrollTop());

    // The bounds of the scrolling container's snap port, where the top left of the scrolling container's border box is the origin.
    auto scrollSnapPort = computeScrollSnapPortOrAreaRect(scrollingElementBox.paddingBoxRect(), scrollingElementStyle.scrollPadding(), InsetOrOutset::Inset);
#if !LOG_DISABLED
    LOG(Scrolling, "Computing scroll snap offsets in snap port: %s", snapPortOrAreaToString(scrollSnapPort).utf8().data());
#endif
    for (auto* child : scrollContainer->view().boxesWithScrollSnapPositions()) {
        if (child->findEnclosingScrollableContainer() != scrollContainer)
            continue;

        // The bounds of the child element's snap area, where the top left of the scrolling container's border box is the origin.
        // The snap area is the bounding box of the child element's border box, after applying transformations.
        auto scrollSnapArea = LayoutRect(child->localToContainerQuad(FloatQuad(child->borderBoundingBox()), scrollingElement.renderBox()).boundingBox());
        scrollSnapArea.moveBy(containerScrollOffset);
        scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollSnapMargin(), InsetOrOutset::Outset);
#if !LOG_DISABLED
        LOG(Scrolling, "    Considering scroll snap area: %s", snapPortOrAreaToString(scrollSnapArea).utf8().data());
#endif
        auto alignment = child->style().scrollSnapAlign();
        if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) {
            auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.width(), alignment.x) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.width(), alignment.x), 0, maxScrollLeft);
            if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) {
                seenHorizontalSnapOffsets.add(absoluteScrollOffset);
                horizontalSnapOffsets.append(absoluteScrollOffset);
            }
        }
        if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) {
            auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.height(), alignment.y) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.height(), alignment.y), 0, maxScrollTop);
            if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) {
                seenVerticalSnapOffsets.add(absoluteScrollOffset);
                verticalSnapOffsets.append(absoluteScrollOffset);
            }
        }
    }

    if (!horizontalSnapOffsets.isEmpty()) {
        adjustAxisSnapOffsetsForScrollExtent(horizontalSnapOffsets, maxScrollLeft);
#if !LOG_DISABLED
        LOG(Scrolling, " => Computed horizontal scroll snap offsets: %s", snapOffsetsToString(horizontalSnapOffsets).utf8().data());
        LOG(Scrolling, " => Computed horizontal scroll snap offset ranges: %s", snapOffsetRangesToString(horizontalSnapOffsetRanges).utf8().data());
#endif
        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
            computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width());

        scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets);
        scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges);
    } else
        scrollableArea.clearHorizontalSnapOffsets();

    if (!verticalSnapOffsets.isEmpty()) {
        adjustAxisSnapOffsetsForScrollExtent(verticalSnapOffsets, maxScrollTop);
#if !LOG_DISABLED
        LOG(Scrolling, " => Computed vertical scroll snap offsets: %s", snapOffsetsToString(verticalSnapOffsets).utf8().data());
        LOG(Scrolling, " => Computed vertical scroll snap offset ranges: %s", snapOffsetRangesToString(verticalSnapOffsetRanges).utf8().data());
#endif
        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
            computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height());

        scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets);
        scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges);
    } else
        scrollableArea.clearVerticalSnapOffsets();
}