void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset)
{
    // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
    if (m_scrollDimensionsDirty)
        computeScrollDimensions();

    if (scrollOffset() == toIntSize(newScrollOffset))
        return;

    setScrollOffset(toIntSize(newScrollOffset));

    LocalFrame* frame = box().frame();
    ASSERT(frame);

    RefPtr<FrameView> frameView = box().frameView();

    TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ScrollLayer", "data", InspectorScrollLayerEvent::data(&box()));

    const RenderLayerModelObject* paintInvalidationContainer = box().containerForPaintInvalidation();

    // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
    // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
    if (!frameView->isInPerformLayout()) {
        // If we're in the middle of layout, we'll just update layers once layout has finished.
        layer()->clipper().clearClipRectsIncludingDescendants();
        box().setPreviousPaintInvalidationRect(box().boundsRectForPaintInvalidation(paintInvalidationContainer));
        updateCompositingLayersAfterScroll();
    }

    // The caret rect needs to be invalidated after scrolling
    frame->selection().setCaretRectNeedsUpdate();

    FloatQuad quadForFakeMouseMoveEvent = FloatQuad(layer()->renderer()->previousPaintInvalidationRect());

    quadForFakeMouseMoveEvent = paintInvalidationContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
    frame->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);

    // For querying RenderLayer::compositingState()
    // This code appears correct, since scrolling outside of layout happens during activities that do not dirty compositing state.
    DisableCompositingQueryAsserts disabler;
    if (box().frameView()->isInPerformLayout())
        box().setShouldDoFullPaintInvalidation(true);
    else
        box().invalidatePaintUsingContainer(paintInvalidationContainer, layer()->renderer()->previousPaintInvalidationRect(), InvalidationScroll);

    // Schedule the scroll DOM event.
    if (box().node())
        box().node()->document().enqueueScrollEventForNode(box().node());
}
bool DevToolsEmulator::handleInputEvent(const WebInputEvent& inputEvent)
{
    Page* page = m_webViewImpl->page();
    if (!page)
        return false;

    // FIXME: This workaround is required for touch emulation on Mac, where
    // compositor-side pinch handling is not enabled. See http://crbug.com/138003.
    bool isPinch = inputEvent.type == WebInputEvent::GesturePinchBegin || inputEvent.type == WebInputEvent::GesturePinchUpdate || inputEvent.type == WebInputEvent::GesturePinchEnd;
    if (isPinch && m_touchEventEmulationEnabled) {
        FrameView* frameView = page->deprecatedLocalMainFrame()->view();
        PlatformGestureEventBuilder gestureEvent(frameView, static_cast<const WebGestureEvent&>(inputEvent));
        float pageScaleFactor = page->pageScaleFactor();
        if (gestureEvent.type() == PlatformEvent::GesturePinchBegin) {
            m_lastPinchAnchorCss = adoptPtr(new IntPoint(frameView->scrollPosition() + gestureEvent.position()));
            m_lastPinchAnchorDip = adoptPtr(new IntPoint(gestureEvent.position()));
            m_lastPinchAnchorDip->scale(pageScaleFactor, pageScaleFactor);
        }
        if (gestureEvent.type() == PlatformEvent::GesturePinchUpdate && m_lastPinchAnchorCss) {
            float newPageScaleFactor = pageScaleFactor * gestureEvent.scale();
            IntPoint anchorCss(*m_lastPinchAnchorDip.get());
            anchorCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
            m_webViewImpl->setPageScaleFactor(newPageScaleFactor);
            m_webViewImpl->mainFrame()->setScrollOffset(toIntSize(*m_lastPinchAnchorCss.get() - toIntSize(anchorCss)));
        }
        if (gestureEvent.type() == PlatformEvent::GesturePinchEnd) {
            m_lastPinchAnchorCss.clear();
            m_lastPinchAnchorDip.clear();
        }
        return true;
    }

    return false;
}
Example #3
0
void RotationViewportAnchor::computeOrigins(
    const FloatSize& innerSize,
    IntPoint& mainFrameOffset,
    FloatPoint& visualViewportOffset) const {
  IntSize outerSize = layoutViewport().visibleContentRect().size();

  // Compute the viewport origins in CSS pixels relative to the document.
  FloatSize absVisualViewportOffset = m_normalizedVisualViewportOffset;
  absVisualViewportOffset.scale(outerSize.width(), outerSize.height());

  FloatPoint innerOrigin = getInnerOrigin(innerSize);
  FloatPoint outerOrigin = innerOrigin - absVisualViewportOffset;

  IntRect outerRect = IntRect(flooredIntPoint(outerOrigin), outerSize);
  FloatRect innerRect = FloatRect(innerOrigin, innerSize);

  moveToEncloseRect(outerRect, innerRect);

  outerRect.setLocation(IntPoint(
      layoutViewport().clampScrollOffset(toIntSize(outerRect.location()))));

  moveIntoRect(innerRect, outerRect);

  mainFrameOffset = outerRect.location();
  visualViewportOffset =
      FloatPoint(innerRect.location() - outerRect.location());
}
Example #4
0
IntPoint ScrollableArea::constrainScrollPositionForOverhang(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, int headerHeight, int footerHeight)
{
    // The viewport rect that we're scrolling shouldn't be larger than our document.
    IntSize idealScrollRectSize(std::min(visibleContentRect.width(), totalContentsSize.width()), std::min(visibleContentRect.height(), totalContentsSize.height()));
    
    IntRect scrollRect(scrollPosition + scrollOrigin - IntSize(0, headerHeight), idealScrollRectSize);
    IntRect documentRect(IntPoint(), IntSize(totalContentsSize.width(), totalContentsSize.height() - headerHeight - footerHeight));

    // Use intersection to constrain our ideal scroll rect by the document rect.
    scrollRect.intersect(documentRect);

    if (scrollRect.size() != idealScrollRectSize) {
        // If the rect was clipped, restore its size, effectively pushing it "down" from the top left.
        scrollRect.setSize(idealScrollRectSize);

        // If we still clip, push our rect "up" from the bottom right.
        scrollRect.intersect(documentRect);
        if (scrollRect.width() < idealScrollRectSize.width())
            scrollRect.move(-(idealScrollRectSize.width() - scrollRect.width()), 0);
        if (scrollRect.height() < idealScrollRectSize.height())
            scrollRect.move(0, -(idealScrollRectSize.height() - scrollRect.height()));
    }

    return scrollRect.location() - toIntSize(scrollOrigin);
}
static void applyClipRects(const ClipRectsContext& context, const LayoutObject& layoutObject, LayoutPoint offset, ClipRects& clipRects)
{
    ASSERT(layoutObject.hasOverflowClip() || layoutObject.hasClip() || layoutObject.style()->containsPaint());
    LayoutView* view = layoutObject.view();
    ASSERT(view);
    if (clipRects.fixed() && context.rootLayer->layoutObject() == view)
        offset -= toIntSize(view->frameView()->scrollPosition());
    if (layoutObject.hasOverflowClip() || layoutObject.style()->containsPaint()) {
        ClipRect newOverflowClip = toLayoutBox(layoutObject).overflowClipRect(offset, context.scrollbarRelevancy);
        newOverflowClip.setHasRadius(layoutObject.style()->hasBorderRadius());
        clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
        if (layoutObject.isPositioned())
            clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
        if (layoutObject.isLayoutView())
            clipRects.setFixedClipRect(intersection(newOverflowClip, clipRects.fixedClipRect()));
        if (layoutObject.style()->containsPaint()) {
            clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
            clipRects.setFixedClipRect(intersection(newOverflowClip, clipRects.fixedClipRect()));
        }
    }
    if (layoutObject.hasClip()) {
        LayoutRect newClip = toLayoutBox(layoutObject).clipRect(offset);
        clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect()).setIsClippedByClipCss());
        clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowClipRect()).setIsClippedByClipCss());
        clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect()).setIsClippedByClipCss());

    }
}
Example #6
0
void ViewClientEfl::didRenderFrame(WKViewRef, WKSize contentsSize, WKRect coveredRect, const void* clientInfo)
{
    EwkView* ewkView = toEwkView(clientInfo);
    if (WKPageUseFixedLayout(ewkView->wkPage()))
        ewkView->pageViewportController().didRenderFrame(toIntSize(contentsSize), toIntRect(coveredRect));
    else
        ewkView->scheduleUpdateDisplay();
}
Example #7
0
void ViewClientEfl::didChangeContentsSize(WKViewRef, WKSize size, const void* clientInfo)
{
    EwkView* ewkView = toEwkView(clientInfo);
    if (WKPageUseFixedLayout(ewkView->wkPage()))
        ewkView->pageViewportController().didChangeContentsSize(toIntSize(size));
    else
        ewkView->scheduleUpdateDisplay();

    ewkView->smartCallback<ContentsSizeChanged>().call(size);
}
Example #8
0
bool WebDevToolsAgentImpl::handleInputEvent(WebCore::Page* page, const WebInputEvent& inputEvent)
{
    if (!m_attached && !m_generatingEvent)
        return false;

    // FIXME: This workaround is required for touch emulation on Mac, where
    // compositor-side pinch handling is not enabled. See http://crbug.com/138003.
    bool isPinch = inputEvent.type == WebInputEvent::GesturePinchBegin || inputEvent.type == WebInputEvent::GesturePinchUpdate || inputEvent.type == WebInputEvent::GesturePinchEnd;
    if (isPinch && m_touchEventEmulationEnabled && m_emulateViewportEnabled) {
        FrameView* frameView = page->mainFrame()->view();
        PlatformGestureEventBuilder gestureEvent(frameView, *static_cast<const WebGestureEvent*>(&inputEvent));
        float pageScaleFactor = page->pageScaleFactor();
        if (gestureEvent.type() == PlatformEvent::GesturePinchBegin) {
            m_lastPinchAnchorCss = adoptPtr(new WebCore::IntPoint(frameView->scrollPosition() + gestureEvent.position()));
            m_lastPinchAnchorDip = adoptPtr(new WebCore::IntPoint(gestureEvent.position()));
            m_lastPinchAnchorDip->scale(pageScaleFactor, pageScaleFactor);
        }
        if (gestureEvent.type() == PlatformEvent::GesturePinchUpdate && m_lastPinchAnchorCss) {
            float newPageScaleFactor = pageScaleFactor * gestureEvent.scale();
            WebCore::IntPoint anchorCss(*m_lastPinchAnchorDip.get());
            anchorCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
            m_webViewImpl->setPageScaleFactor(newPageScaleFactor);
            m_webViewImpl->setMainFrameScrollOffset(*m_lastPinchAnchorCss.get() - toIntSize(anchorCss));
        }
        if (gestureEvent.type() == PlatformEvent::GesturePinchEnd) {
            m_lastPinchAnchorCss.clear();
            m_lastPinchAnchorDip.clear();
        }
        return true;
    }

    InspectorController* ic = inspectorController();
    if (!ic)
        return false;

    if (WebInputEvent::isGestureEventType(inputEvent.type) && inputEvent.type == WebInputEvent::GestureTap) {
        // Only let GestureTab in (we only need it and we know PlatformGestureEventBuilder supports it).
        PlatformGestureEvent gestureEvent = PlatformGestureEventBuilder(page->mainFrame()->view(), *static_cast<const WebGestureEvent*>(&inputEvent));
        return ic->handleGestureEvent(page->mainFrame(), gestureEvent);
    }
    if (WebInputEvent::isMouseEventType(inputEvent.type) && inputEvent.type != WebInputEvent::MouseEnter) {
        // PlatformMouseEventBuilder does not work with MouseEnter type, so we filter it out manually.
        PlatformMouseEvent mouseEvent = PlatformMouseEventBuilder(page->mainFrame()->view(), *static_cast<const WebMouseEvent*>(&inputEvent));
        return ic->handleMouseEvent(page->mainFrame(), mouseEvent);
    }
    if (WebInputEvent::isTouchEventType(inputEvent.type)) {
        PlatformTouchEvent touchEvent = PlatformTouchEventBuilder(page->mainFrame()->view(), *static_cast<const WebTouchEvent*>(&inputEvent));
        return ic->handleTouchEvent(page->mainFrame(), touchEvent);
    }
    if (WebInputEvent::isKeyboardEventType(inputEvent.type)) {
        PlatformKeyboardEvent keyboardEvent = PlatformKeyboardEventBuilder(*static_cast<const WebKeyboardEvent*>(&inputEvent));
        return ic->handleKeyboardEvent(page->mainFrame(), keyboardEvent);
    }
    return false;
}
Example #9
0
IntSize WebFrame::scrollOffset() const
{
    if (!m_coreFrame)
        return IntSize();
    
    FrameView* view = m_coreFrame->view();
    if (!view)
        return IntSize();

    return toIntSize(view->scrollPosition());
}
void RenderLayerScrollableArea::paintOverflowControls(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
{
    // Don't do anything if we have no overflow.
    if (!box().hasOverflowClip())
        return;

    IntPoint adjustedPaintOffset = paintOffset;
    if (paintingOverlayControls)
        adjustedPaintOffset = m_cachedOverlayScrollbarOffset;

    // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout,
    // but sometimes widgets can move without layout occurring (most notably when you scroll a
    // document that contains fixed positioned elements).
    positionOverflowControls(toIntSize(adjustedPaintOffset));

    // Overlay scrollbars paint in a second pass through the layer tree so that they will paint
    // on top of everything else. If this is the normal painting pass, paintingOverlayControls
    // will be false, and we should just tell the root layer that there are overlay scrollbars
    // that need to be painted. That will cause the second pass through the layer tree to run,
    // and we'll paint the scrollbars then. In the meantime, cache tx and ty so that the
    // second pass doesn't need to re-enter the RenderTree to get it right.
    if (hasOverlayScrollbars() && !paintingOverlayControls) {
        m_cachedOverlayScrollbarOffset = paintOffset;
        IntRect localDamgeRect = damageRect;
        localDamgeRect.moveBy(-paintOffset);
        if (!overflowControlsIntersectRect(localDamgeRect))
            return;

        RenderView* renderView = box().view();

        RenderLayer* paintingRoot = layer()->enclosingLayerWithCompositedLayerMapping(IncludeSelf);
        if (!paintingRoot)
            paintingRoot = renderView->layer();

        paintingRoot->setContainsDirtyOverlayScrollbars(true);
        return;
    }

    // This check is required to avoid painting custom CSS scrollbars twice.
    if (paintingOverlayControls && !hasOverlayScrollbars())
        return;

    // Now that we're sure the scrollbars are in the right place, paint them.
    if (m_hBar)
        m_hBar->paint(context, damageRect);
    if (m_vBar)
        m_vBar->paint(context, damageRect);
}
LayoutRect RenderLayerScrollableArea::exposeRect(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
{
    LayoutRect localExposeRect(box().absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox());
    LayoutRect layerBounds(0, 0, box().clientWidth(), box().clientHeight());
    LayoutRect r = ScrollAlignment::getRectToExpose(layerBounds, localExposeRect, alignX, alignY);

    IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location()));
    if (clampedScrollOffset == adjustedScrollOffset())
        return rect;

    IntSize oldScrollOffset = adjustedScrollOffset();
    scrollToOffset(clampedScrollOffset);
    IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset;
    localExposeRect.move(-scrollOffsetDifference);
    return LayoutRect(box().localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox());
}
Example #12
0
ClipRect PaintLayerClipper::backgroundClipRect(const ClipRectsContext& context) const
{
    ASSERT(m_layoutObject.layer()->parent());
    ASSERT(m_layoutObject.view());

    RefPtr<ClipRects> parentClipRects = ClipRects::create();
    if (m_layoutObject.layer() == context.rootLayer)
        parentClipRects->reset(LayoutRect(LayoutRect::infiniteIntRect()));
    else
        m_layoutObject.layer()->parent()->clipper().getOrCalculateClipRects(context, *parentClipRects);

    ClipRect result = backgroundClipRectForPosition(*parentClipRects, m_layoutObject.style()->position());

    // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite.
    if (parentClipRects->fixed() && context.rootLayer->layoutObject() == m_layoutObject.view() && result != LayoutRect(LayoutRect::infiniteIntRect()))
        result.move(toIntSize(m_layoutObject.view()->frameView()->scrollPosition()));

    return result;
}
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);
}
Example #14
0
IntPoint VisualViewport::clampDocumentOffsetAtScale(const IntPoint& offset,
                                                    float scale) {
  if (!mainFrame() || !mainFrame()->view())
    return IntPoint();

  FrameView* view = mainFrame()->view();

  FloatSize scaledSize(m_size);
  scaledSize.scale(1 / scale);

  IntSize visualViewportMax =
      flooredIntSize(FloatSize(contentsSize()) - scaledSize);
  IntSize max = view->maximumScrollOffsetInt() + visualViewportMax;
  IntSize min =
      view->minimumScrollOffsetInt();  // VisualViewportMin should be (0, 0)

  IntSize clamped = toIntSize(offset);
  clamped = clamped.shrunkTo(max);
  clamped = clamped.expandedTo(min);
  return IntPoint(clamped);
}
Example #15
0
bool MouseWheelRegionOverlay::updateRegion()
{
    std::unique_ptr<Region> region = std::make_unique<Region>();
    
    for (const Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext()) {
        if (!frame->view() || !frame->document())
            continue;

        Document::RegionFixedPair frameRegion = frame->document()->absoluteRegionForEventTargets(frame->document()->wheelEventTargets());
    
        IntPoint frameOffset = frame->view()->contentsToRootView(IntPoint());
        frameRegion.first.translate(toIntSize(frameOffset));
        
        region->unite(frameRegion.first);
    }
    
    region->translate(m_overlay->viewToOverlayOffset());

    bool regionChanged = !m_region || !(*m_region == *region);
    m_region = WTFMove(region);
    return regionChanged;
}
Example #16
0
void NonCompositedContentHost::setViewport(const WebCore::IntSize& viewportSize, const WebCore::IntSize& contentsSize, const WebCore::IntPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin)
{
    if (!haveScrollLayer())
        return;

    bool visibleRectChanged = m_viewportSize != viewportSize;

    m_viewportSize = viewportSize;
    WebLayer* layer = scrollLayer();
    layer->setScrollPosition(scrollPosition + scrollOrigin);
    layer->setPosition(WebFloatPoint(-scrollPosition));
    // Due to the possibility of pinch zoom, the noncomposited layer is always
    // assumed to be scrollable.
    layer->setScrollable(true);
    m_graphicsLayer->setSize(contentsSize);

    // In RTL-style pages, the origin of the initial containing block for the
    // root layer may be positive; translate the layer to avoid negative
    // coordinates.
    m_layerAdjust = -toIntSize(scrollOrigin);
    if (m_graphicsLayer->transform().m41() != m_layerAdjust.width() || m_graphicsLayer->transform().m42() != m_layerAdjust.height()) {
        WebCore::TransformationMatrix transform = m_graphicsLayer->transform();
        transform.setM41(m_layerAdjust.width());
        transform.setM42(m_layerAdjust.height());
        m_graphicsLayer->setTransform(transform);

        // If a tiled layer is shifted left or right, the content that goes into
        // each tile will change. Invalidate the entire layer when this happens.
        m_graphicsLayer->setNeedsDisplay();
    } else if (visibleRectChanged)
        m_graphicsLayer->setNeedsDisplay();

    WebCore::GraphicsLayer* clipLayer = m_graphicsLayer->parent()->parent();
    WebCore::GraphicsLayer* rootLayer = clipLayer;
    while (rootLayer->parent())
        rootLayer = rootLayer->parent();
    setScrollbarBoundsContainPageScale(rootLayer, clipLayer);
}
void GraphicsLayer::didScroll()
{
    if (m_scrollableArea)
        m_scrollableArea->scrollToOffsetWithoutAnimation(m_scrollableArea->minimumScrollPosition() + toIntSize(m_layer->layer()->scrollPosition()));
}
WebTouchEvent WebEventFactory::createWebTouchEvent(const EwkTouchEvent* event, const AffineTransform& toWebContent)
{
    API::Array* touchPointsArray = toImpl(event->touchPoints());
    size_t size = touchPointsArray->size();

    Vector<WebPlatformTouchPoint> touchPoints;
    touchPoints.reserveInitialCapacity(size);

    for (size_t i = 0; i < size; ++i) {
        if (EwkTouchPoint* point = touchPointsArray->at<EwkTouchPoint>(i))
            touchPoints.uncheckedAppend(WebPlatformTouchPoint(point->id(), toWebPlatformTouchPointState(point->state()), toIntPoint(point->screenPosition()), toWebContent.mapPoint(toIntPoint(point->position())), toIntSize(point->radius()), point->rotationAngle(), point->forceFactor()));
    }

    return WebTouchEvent(toWebEventType(event->eventType()), touchPoints, toWebEventModifiers(event->modifiers()), event->timestamp());
}
Example #19
0
Region ScrollingCoordinator::absoluteNonFastScrollableRegionForFrame(const Frame& frame) const
{
    RenderView* renderView = frame.contentRenderer();
    if (!renderView || renderView->documentBeingDestroyed())
        return Region();

#if ENABLE(IOS_TOUCH_EVENTS)
    // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers.
    ASSERT(frame.isMainFrame());

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

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

    // FIXME: use absoluteRegionForEventTargets().
    return touchRegion;
#else
    Region nonFastScrollableRegion;
    FrameView* frameView = frame.view();
    if (!frameView)
        return nonFastScrollableRegion;

    // FIXME: should ASSERT(!frameView->needsLayout()) here, but need to fix DebugPageOverlays
    // to not ask for regions at bad times.

    if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
        for (auto& scrollableArea : *scrollableAreas) {
            // Composited scrollable areas can be scrolled off the main thread.
            if (scrollableArea->usesAsyncScrolling())
                continue;

            bool isInsideFixed;
            IntRect box = scrollableArea->scrollableAreaBoundingBox(&isInsideFixed);
            if (isInsideFixed)
                box = IntRect(frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(box)));

            nonFastScrollableRegion.unite(box);
        }
    }

    for (auto& widget : frameView->widgetsInRenderTree()) {
        RenderWidget* renderWidget = RenderWidget::find(widget);
        if (!renderWidget || !is<PluginViewBase>(*widget))
            continue;
    
        if (downcast<PluginViewBase>(*widget).wantsWheelEvents())
            nonFastScrollableRegion.unite(renderWidget->absoluteBoundingBoxRect());
    }
    
    // FIXME: if we've already accounted for this subframe as a scrollable area, we can avoid recursing into it here.
    for (Frame* subframe = frame.tree().firstChild(); subframe; subframe = subframe->tree().nextSibling()) {
        FrameView* subframeView = subframe->view();
        if (!subframeView)
            continue;

        Region subframeRegion = absoluteNonFastScrollableRegionForFrame(*subframe);
        // Map from the frame document to our document.
        IntPoint offset = subframeView->contentsToContainingViewContents(IntPoint());

        // FIXME: this translation ignores non-trival transforms on the frame.
        subframeRegion.translate(toIntSize(offset));
        nonFastScrollableRegion.unite(subframeRegion);
    }

    Document::RegionFixedPair wheelHandlerRegion = frame.document()->absoluteRegionForEventTargets(frame.document()->wheelEventTargets());
    bool wheelHandlerInFixedContent = wheelHandlerRegion.second;
    if (wheelHandlerInFixedContent) {
        // FIXME: need to handle position:sticky here too.
        LayoutRect inflatedWheelHandlerBounds = frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(wheelHandlerRegion.first.bounds()));
        wheelHandlerRegion.first.unite(enclosingIntRect(inflatedWheelHandlerBounds));
    }
    
    nonFastScrollableRegion.unite(wheelHandlerRegion.first);

    // FIXME: If this is not the main frame, we could clip the region to the frame's bounds.
    return nonFastScrollableRegion;
#endif
}
ScrollPosition ScrollableArea::scrollPositionFromOffset(ScrollOffset offset) const
{
    return scrollPositionFromOffset(offset, toIntSize(m_scrollOrigin));
}
ScrollOffset ScrollableArea::scrollOffsetFromPosition(ScrollPosition position) const
{
    return scrollOffsetFromPosition(position, toIntSize(m_scrollOrigin));
}