ScrollingCoordinator.cpp [plain text]
#include "config.h"
#include "ScrollingCoordinator.h"
#include "Frame.h"
#include "FrameView.h"
#include "IntRect.h"
#include "Page.h"
#include "PlatformWheelEvent.h"
#include "PluginViewBase.h"
#include "Region.h"
#include "RenderView.h"
#include "ScrollAnimator.h"
#include "ScrollingTreeState.h"
#include <wtf/MainThread.h>
#if USE(ACCELERATED_COMPOSITING)
#include "RenderLayerCompositor.h"
#endif
#if ENABLE(THREADED_SCROLLING)
#include "ScrollingThread.h"
#include "ScrollingTree.h"
#include <wtf/Functional.h>
#include <wtf/PassRefPtr.h>
#endif
namespace WebCore {
ScrollingCoordinator::ScrollingCoordinator(Page* page)
: m_page(page)
, m_forceMainThreadScrollLayerPositionUpdates(false)
#if ENABLE(THREADED_SCROLLING)
, m_scrollingTreeState(ScrollingTreeState::create())
, m_scrollingTree(ScrollingTree::create(this))
, m_scrollingTreeStateCommitterTimer(this, &ScrollingCoordinator::scrollingTreeStateCommitterTimerFired)
#endif
, m_private(0)
{
}
void ScrollingCoordinator::pageDestroyed()
{
ASSERT(m_page);
m_page = 0;
#if ENABLE(THREADED_SCROLLING)
m_scrollingTreeStateCommitterTimer.stop();
ScrollingThread::dispatch(bind(&ScrollingTree::invalidate, m_scrollingTree.release()));
#endif
}
#if ENABLE(THREADED_SCROLLING)
ScrollingTree* ScrollingCoordinator::scrollingTree() const
{
ASSERT(m_scrollingTree);
return m_scrollingTree.get();
}
#endif
bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
{
ASSERT(isMainThread());
ASSERT(m_page);
if (frameView->frame() != m_page->mainFrame())
return false;
#if USE(ACCELERATED_COMPOSITING)
RenderView* renderView = m_page->mainFrame()->contentRenderer();
if (!renderView)
return false;
return renderView->usesCompositing();
#else
return false;
#endif
}
static Region 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;
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;
}
void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView)
{
ASSERT(isMainThread());
ASSERT(m_page);
Region nonFastScrollableRegion = computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint());
setNonFastScrollableRegion(nonFastScrollableRegion);
if (!coordinatesScrollingForFrameView(frameView))
return;
ScrollParameters scrollParameters;
scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
scrollParameters.hasEnabledHorizontalScrollbar = frameView->horizontalScrollbar() && frameView->horizontalScrollbar()->enabled();
scrollParameters.hasEnabledVerticalScrollbar = frameView->verticalScrollbar() && frameView->verticalScrollbar()->enabled();
scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
scrollParameters.scrollOrigin = frameView->scrollOrigin();
scrollParameters.viewportRect = IntRect(IntPoint(), frameView->visibleContentRect().size());
scrollParameters.contentsSize = frameView->contentsSize();
setScrollParameters(scrollParameters);
}
void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView*)
{
ASSERT(isMainThread());
ASSERT(m_page);
recomputeWheelEventHandlerCount();
}
void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
{
ASSERT(isMainThread());
ASSERT(m_page);
if (!coordinatesScrollingForFrameView(frameView))
return;
updateShouldUpdateScrollLayerPositionOnMainThread();
}
void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
{
ASSERT(isMainThread());
ASSERT(m_page);
if (!coordinatesScrollingForFrameView(frameView))
return;
updateShouldUpdateScrollLayerPositionOnMainThread();
}
static GraphicsLayer* scrollLayerForFrameView(FrameView* frameView)
{
#if USE(ACCELERATED_COMPOSITING)
Frame* frame = frameView->frame();
if (!frame)
return 0;
RenderView* renderView = frame->contentRenderer();
if (!renderView)
return 0;
return renderView->compositor()->scrollLayer();
#else
return 0;
#endif
}
void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
{
ASSERT(isMainThread());
ASSERT(m_page);
if (!coordinatesScrollingForFrameView(frameView))
return;
frameViewLayoutUpdated(frameView);
recomputeWheelEventHandlerCount();
updateShouldUpdateScrollLayerPositionOnMainThread();
setScrollLayer(scrollLayerForFrameView(frameView));
}
bool ScrollingCoordinator::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
{
ASSERT(isMainThread());
ASSERT(m_page);
if (!coordinatesScrollingForFrameView(frameView))
return false;
#if ENABLE(THREADED_SCROLLING)
if (frameView->frame()->document()->inPageCache()) {
updateMainFrameScrollPosition(scrollPosition);
return true;
}
m_scrollingTreeState->setRequestedScrollPosition(scrollPosition);
scheduleTreeStateCommit();
return true;
#else
UNUSED_PARAM(scrollPosition);
return false;
#endif
}
bool ScrollingCoordinator::handleWheelEvent(FrameView*, const PlatformWheelEvent& wheelEvent)
{
ASSERT(isMainThread());
ASSERT(m_page);
#if ENABLE(THREADED_SCROLLING)
if (m_scrollingTree->willWheelEventStartSwipeGesture(wheelEvent))
return false;
ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, m_scrollingTree.get(), wheelEvent));
#else
UNUSED_PARAM(wheelEvent);
#endif
return true;
}
void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition)
{
ASSERT(isMainThread());
if (!m_page)
return;
FrameView* frameView = m_page->mainFrame()->view();
if (!frameView)
return;
frameView->setConstrainsScrollingToContentEdge(false);
frameView->notifyScrollPositionChanged(scrollPosition);
frameView->setConstrainsScrollingToContentEdge(true);
}
void ScrollingCoordinator::updateMainFrameScrollPositionAndScrollLayerPosition()
{
#if USE(ACCELERATED_COMPOSITING) && ENABLE(THREADED_SCROLLING)
ASSERT(isMainThread());
if (!m_page)
return;
FrameView* frameView = m_page->mainFrame()->view();
if (!frameView)
return;
IntPoint scrollPosition = m_scrollingTree->mainFrameScrollPosition();
frameView->setConstrainsScrollingToContentEdge(false);
frameView->notifyScrollPositionChanged(scrollPosition);
frameView->setConstrainsScrollingToContentEdge(true);
if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
scrollLayer->setPosition(-frameView->scrollPosition());
#endif
}
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
{
ASSERT(isMainThread());
if (!m_page)
return;
FrameView* frameView = m_page->mainFrame()->view();
if (!frameView)
return;
frameView->scrollAnimator()->handleWheelEventPhase(phase);
}
#endif
void ScrollingCoordinator::recomputeWheelEventHandlerCount()
{
unsigned wheelEventHandlerCount = 0;
for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
if (frame->document())
wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
}
setWheelEventHandlerCount(wheelEventHandlerCount);
}
bool ScrollingCoordinator::hasNonLayerFixedObjects(FrameView* frameView)
{
const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
if (!viewportConstrainedObjects)
return false;
#if USE(ACCELERATED_COMPOSITING)
for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
RenderObject* viewportConstrainedObject = *it;
if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
return true;
RenderBoxModelObject* viewportConstrainedBoxModelObject = toRenderBoxModelObject(viewportConstrainedObject);
if (!viewportConstrainedBoxModelObject->layer()->backing())
return true;
}
return false;
#else
return viewportConstrainedObjects->size();
#endif
}
void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
{
FrameView* frameView = m_page->mainFrame()->view();
setShouldUpdateScrollLayerPositionOnMainThread(m_forceMainThreadScrollLayerPositionUpdates
|| frameView->hasSlowRepaintObjects()
|| (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
|| (supportsFixedPositionLayers() && hasNonLayerFixedObjects(frameView))
|| m_page->mainFrame()->document()->isImageDocument());
}
void ScrollingCoordinator::setForceMainThreadScrollLayerPositionUpdates(bool forceMainThreadScrollLayerPositionUpdates)
{
if (m_forceMainThreadScrollLayerPositionUpdates == forceMainThreadScrollLayerPositionUpdates)
return;
m_forceMainThreadScrollLayerPositionUpdates = forceMainThreadScrollLayerPositionUpdates;
updateShouldUpdateScrollLayerPositionOnMainThread();
}
#if ENABLE(THREADED_SCROLLING)
void ScrollingCoordinator::setScrollLayer(GraphicsLayer* scrollLayer)
{
m_scrollingTreeState->setScrollLayer(scrollLayer);
scheduleTreeStateCommit();
}
void ScrollingCoordinator::setNonFastScrollableRegion(const Region& region)
{
m_scrollingTreeState->setNonFastScrollableRegion(region);
scheduleTreeStateCommit();
}
void ScrollingCoordinator::setScrollParameters(const ScrollParameters& scrollParameters)
{
m_scrollingTreeState->setHorizontalScrollElasticity(scrollParameters.horizontalScrollElasticity);
m_scrollingTreeState->setVerticalScrollElasticity(scrollParameters.verticalScrollElasticity);
m_scrollingTreeState->setHasEnabledHorizontalScrollbar(scrollParameters.hasEnabledHorizontalScrollbar);
m_scrollingTreeState->setHasEnabledVerticalScrollbar(scrollParameters.hasEnabledVerticalScrollbar);
m_scrollingTreeState->setHorizontalScrollbarMode(scrollParameters.horizontalScrollbarMode);
m_scrollingTreeState->setVerticalScrollbarMode(scrollParameters.verticalScrollbarMode);
m_scrollingTreeState->setScrollOrigin(scrollParameters.scrollOrigin);
m_scrollingTreeState->setViewportRect(scrollParameters.viewportRect);
m_scrollingTreeState->setContentsSize(scrollParameters.contentsSize);
scheduleTreeStateCommit();
}
void ScrollingCoordinator::setWheelEventHandlerCount(unsigned wheelEventHandlerCount)
{
m_scrollingTreeState->setWheelEventHandlerCount(wheelEventHandlerCount);
scheduleTreeStateCommit();
}
void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(bool shouldUpdateScrollLayerPositionOnMainThread)
{
m_scrollingTreeState->setShouldUpdateScrollLayerPositionOnMainThread(shouldUpdateScrollLayerPositionOnMainThread);
scheduleTreeStateCommit();
}
void ScrollingCoordinator::scheduleTreeStateCommit()
{
if (m_scrollingTreeStateCommitterTimer.isActive())
return;
if (!m_scrollingTreeState->hasChangedProperties())
return;
m_scrollingTreeStateCommitterTimer.startOneShot(0);
}
void ScrollingCoordinator::scrollingTreeStateCommitterTimerFired(Timer<ScrollingCoordinator>*)
{
commitTreeState();
}
void ScrollingCoordinator::commitTreeStateIfNeeded()
{
if (!m_scrollingTreeState->hasChangedProperties())
return;
commitTreeState();
m_scrollingTreeStateCommitterTimer.stop();
}
void ScrollingCoordinator::commitTreeState()
{
ASSERT(m_scrollingTreeState->hasChangedProperties());
OwnPtr<ScrollingTreeState> treeState = m_scrollingTreeState->commit();
ScrollingThread::dispatch(bind(&ScrollingTree::commitNewTreeState, m_scrollingTree.get(), treeState.release()));
}
bool ScrollingCoordinator::supportsFixedPositionLayers() const
{
return false;
}
void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer*, bool)
{
}
void ScrollingCoordinator::setLayerIsFixedToContainerLayer(GraphicsLayer*, bool)
{
}
#endif // !ENABLE(THREADED_SCROLLING)
}