#include "config.h"
#include "ScrollingTree.h"
#if ENABLE(THREADED_SCROLLING)
#include "PlatformWheelEvent.h"
#include "ScrollingCoordinator.h"
#include "ScrollingStateTree.h"
#include "ScrollingThread.h"
#include "ScrollingTreeFixedNode.h"
#include "ScrollingTreeNode.h"
#include "ScrollingTreeScrollingNode.h"
#include "ScrollingTreeStickyNode.h"
#include <wtf/MainThread.h>
#include <wtf/TemporaryChange.h>
namespace WebCore {
PassRefPtr<ScrollingTree> ScrollingTree::create(ScrollingCoordinator* scrollingCoordinator)
{
return adoptRef(new ScrollingTree(scrollingCoordinator));
}
ScrollingTree::ScrollingTree(ScrollingCoordinator* scrollingCoordinator)
: m_scrollingCoordinator(scrollingCoordinator)
, m_hasWheelEventHandlers(false)
, m_canGoBack(false)
, m_canGoForward(false)
, m_mainFramePinnedToTheLeft(false)
, m_mainFramePinnedToTheRight(false)
, m_rubberBandsAtBottom(true)
, m_rubberBandsAtTop(true)
, m_mainFramePinnedToTheTop(false)
, m_mainFramePinnedToTheBottom(false)
, m_mainFrameIsRubberBanding(false)
, m_scrollPinningBehavior(DoNotPin)
, m_scrollingPerformanceLoggingEnabled(false)
, m_isHandlingProgrammaticScroll(false)
{
}
ScrollingTree::~ScrollingTree()
{
ASSERT(!m_scrollingCoordinator);
}
ScrollingTree::EventResult ScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent)
{
{
MutexLocker lock(m_mutex);
if (m_hasWheelEventHandlers)
return SendToMainThread;
if (!m_nonFastScrollableRegion.isEmpty()) {
IntPoint position = wheelEvent.position();
position.moveBy(m_mainFrameScrollPosition);
if (m_nonFastScrollableRegion.contains(position))
return SendToMainThread;
}
}
if (willWheelEventStartSwipeGesture(wheelEvent))
return DidNotHandleEvent;
ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, this, wheelEvent));
return DidHandleEvent;
}
void ScrollingTree::updateBackForwardState(bool canGoBack, bool canGoForward)
{
MutexLocker locker(m_swipeStateMutex);
m_canGoBack = canGoBack;
m_canGoForward = canGoForward;
}
void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
{
ASSERT(ScrollingThread::isCurrentThread());
if (m_rootNode)
m_rootNode->handleWheelEvent(wheelEvent);
}
static void derefScrollingCoordinator(ScrollingCoordinator* scrollingCoordinator)
{
ASSERT(isMainThread());
scrollingCoordinator->deref();
}
void ScrollingTree::invalidate()
{
ASSERT(ScrollingThread::isCurrentThread());
callOnMainThread(bind(derefScrollingCoordinator, m_scrollingCoordinator.release().leakRef()));
}
void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingStateTree> scrollingStateTree)
{
ASSERT(ScrollingThread::isCurrentThread());
bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode();
ScrollingStateScrollingNode* rootNode = scrollingStateTree->rootStateNode();
if (rootNode
&& (rootStateNodeChanged
|| rootNode->hasChangedProperty(ScrollingStateScrollingNode::WheelEventHandlerCount)
|| rootNode->hasChangedProperty(ScrollingStateScrollingNode::NonFastScrollableRegion)
|| rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))) {
MutexLocker lock(m_mutex);
if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))
m_mainFrameScrollPosition = IntPoint();
if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateScrollingNode::WheelEventHandlerCount))
m_hasWheelEventHandlers = scrollingStateTree->rootStateNode()->wheelEventHandlerCount();
if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateScrollingNode::NonFastScrollableRegion))
m_nonFastScrollableRegion = scrollingStateTree->rootStateNode()->nonFastScrollableRegion();
}
bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false;
TemporaryChange<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic);
removeDestroyedNodes(scrollingStateTree.get());
updateTreeFromStateNode(rootNode);
}
void ScrollingTree::updateTreeFromStateNode(ScrollingStateNode* stateNode)
{
if (!stateNode) {
m_nodeMap.clear();
m_rootNode = nullptr;
return;
}
ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->scrollingNodeID());
ScrollingTreeNode* node;
if (it != m_nodeMap.end()) {
node = it->value;
node->updateBeforeChildren(stateNode);
} else {
ScrollingNodeID nodeID = stateNode->scrollingNodeID();
if (!stateNode->parent()) {
m_nodeMap.clear();
m_rootNode = ScrollingTreeScrollingNode::create(this, nodeID);
m_nodeMap.set(nodeID, m_rootNode.get());
m_rootNode->updateBeforeChildren(stateNode);
node = m_rootNode.get();
} else {
OwnPtr<ScrollingTreeNode> newNode;
if (stateNode->isScrollingNode())
newNode = ScrollingTreeScrollingNode::create(this, nodeID);
else if (stateNode->isFixedNode())
newNode = ScrollingTreeFixedNode::create(this, nodeID);
else if (stateNode->isStickyNode())
newNode = ScrollingTreeStickyNode::create(this, nodeID);
else
ASSERT_NOT_REACHED();
node = newNode.get();
m_nodeMap.set(nodeID, node);
ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->parent()->scrollingNodeID());
ASSERT(it != m_nodeMap.end());
if (it != m_nodeMap.end()) {
ScrollingTreeNode* parent = it->value;
newNode->setParent(parent);
parent->appendChild(newNode.release());
}
node->updateBeforeChildren(stateNode);
}
}
Vector<OwnPtr<ScrollingStateNode> >* stateNodeChildren = stateNode->children();
if (stateNodeChildren) {
size_t size = stateNodeChildren->size();
for (size_t i = 0; i < size; ++i)
updateTreeFromStateNode(stateNodeChildren->at(i).get());
}
node->updateAfterChildren(stateNode);
}
void ScrollingTree::removeDestroyedNodes(ScrollingStateTree* stateTree)
{
const Vector<ScrollingNodeID>& removedNodes = stateTree->removedNodes();
size_t size = removedNodes.size();
for (size_t i = 0; i < size; ++i) {
ScrollingTreeNode* node = m_nodeMap.take(removedNodes[i]);
if (node && node->parent())
m_rootNode->removeChild(node);
}
}
void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom)
{
MutexLocker locker(m_swipeStateMutex);
m_mainFramePinnedToTheLeft = pinnedToTheLeft;
m_mainFramePinnedToTheRight = pinnedToTheRight;
m_mainFramePinnedToTheTop = pinnedToTheTop;
m_mainFramePinnedToTheBottom = pinnedToTheBottom;
}
void ScrollingTree::updateMainFrameScrollPosition(const IntPoint& scrollPosition, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
{
if (!m_scrollingCoordinator)
return;
{
MutexLocker lock(m_mutex);
m_mainFrameScrollPosition = scrollPosition;
}
callOnMainThread(bind(&ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition, m_scrollingCoordinator.get(), scrollPosition, m_isHandlingProgrammaticScroll, scrollingLayerPositionAction));
}
IntPoint ScrollingTree::mainFrameScrollPosition()
{
MutexLocker lock(m_mutex);
return m_mainFrameScrollPosition;
}
#if PLATFORM(MAC)
void ScrollingTree::handleWheelEventPhase(PlatformWheelEventPhase phase)
{
if (!m_scrollingCoordinator)
return;
callOnMainThread(bind(&ScrollingCoordinator::handleWheelEventPhase, m_scrollingCoordinator.get(), phase));
}
#endif
bool ScrollingTree::canGoBack()
{
MutexLocker lock(m_swipeStateMutex);
return m_canGoBack;
}
bool ScrollingTree::canGoForward()
{
MutexLocker lock(m_swipeStateMutex);
return m_canGoForward;
}
bool ScrollingTree::isRubberBandInProgress()
{
MutexLocker lock(m_mutex);
return m_mainFrameIsRubberBanding;
}
void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
{
MutexLocker locker(m_mutex);
m_mainFrameIsRubberBanding = isRubberBanding;
}
bool ScrollingTree::rubberBandsAtBottom()
{
MutexLocker lock(m_swipeStateMutex);
return m_rubberBandsAtBottom;
}
void ScrollingTree::setRubberBandsAtBottom(bool rubberBandsAtBottom)
{
MutexLocker locker(m_swipeStateMutex);
m_rubberBandsAtBottom = rubberBandsAtBottom;
}
bool ScrollingTree::rubberBandsAtTop()
{
MutexLocker lock(m_swipeStateMutex);
return m_rubberBandsAtTop;
}
void ScrollingTree::setRubberBandsAtTop(bool rubberBandsAtTop)
{
MutexLocker locker(m_swipeStateMutex);
m_rubberBandsAtTop = rubberBandsAtTop;
}
void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning)
{
MutexLocker locker(m_swipeStateMutex);
m_scrollPinningBehavior = pinning;
}
ScrollPinningBehavior ScrollingTree::scrollPinningBehavior()
{
MutexLocker lock(m_swipeStateMutex);
return m_scrollPinningBehavior;
}
bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
{
if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
return false;
MutexLocker lock(m_swipeStateMutex);
if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && m_canGoBack)
return true;
if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && m_canGoForward)
return true;
if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop)
return true;
if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom)
return true;
return false;
}
void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
{
m_scrollingPerformanceLoggingEnabled = flag;
}
bool ScrollingTree::scrollingPerformanceLoggingEnabled()
{
return m_scrollingPerformanceLoggingEnabled;
}
}
#endif // ENABLE(THREADED_SCROLLING)