InRegionScroller.cpp [plain text]
#include "config.h"
#include "InRegionScroller.h"
#include "BackingStoreClient.h"
#include "DOMSupport.h"
#include "Frame.h"
#include "HTMLFrameOwnerElement.h"
#include "HitTestResult.h"
#include "InRegionScrollableArea.h"
#include "InRegionScroller_p.h"
#include "LayerCompositingThread.h"
#include "LayerWebKitThread.h"
#include "Page.h"
#include "RenderBox.h"
#include "RenderLayer.h"
#include "RenderLayerBacking.h"
#include "RenderLayerCompositor.h"
#include "RenderObject.h"
#include "RenderView.h"
#include "SelectionHandler.h"
#include "WebKitThreadViewportAccessor.h"
#include "WebPage_p.h"
#include <BlackBerryPlatformViewportAccessor.h>
using namespace WebCore;
namespace BlackBerry {
namespace WebKit {
static bool canScrollInnerFrame(Frame*);
static RenderLayer* parentLayer(RenderLayer*);
static bool isNonRenderViewFixedPositionedContainer(RenderLayer*);
InRegionScroller::InRegionScroller(WebPagePrivate* webPagePrivate)
: d(new InRegionScrollerPrivate(webPagePrivate))
{
ASSERT(webPagePrivate);
}
InRegionScroller::~InRegionScroller()
{
delete d;
}
bool InRegionScroller::setDocumentScrollPositionCompositingThread(unsigned camouflagedLayer, const Platform::IntPoint& documentScrollPosition)
{
ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread());
return d->setScrollPositionCompositingThread(camouflagedLayer, documentScrollPosition);
}
bool InRegionScroller::setDocumentScrollPositionWebKitThread(unsigned camouflagedLayer, const Platform::IntPoint& documentScrollPosition,
bool supportsAcceleratedScrolling, Platform::ScrollViewBase::ScrollTarget scrollTarget)
{
ASSERT(Platform::webKitThreadMessageClient()->isCurrentThread());
return d->setScrollPositionWebKitThread(camouflagedLayer, documentScrollPosition, supportsAcceleratedScrolling, scrollTarget);
}
InRegionScrollerPrivate::InRegionScrollerPrivate(WebPagePrivate* webPagePrivate)
: m_webPage(webPagePrivate)
, m_needsActiveScrollableAreaCalculation(false)
, m_selectionScrollView(0)
{
}
void InRegionScrollerPrivate::reset()
{
std::vector<Platform::ScrollViewBase*> emptyInRegionScrollableAreas;
m_webPage->m_client->notifyInRegionScrollableAreasChanged(emptyInRegionScrollableAreas);
m_needsActiveScrollableAreaCalculation = false;
for (size_t i = 0; i < m_activeInRegionScrollableAreas.size(); ++i)
delete m_activeInRegionScrollableAreas[i];
m_activeInRegionScrollableAreas.clear();
}
void InRegionScrollerPrivate::resetSelectionScrollView()
{
m_webPage->m_client->notifySelectionScrollView(0);
m_webPage->m_selectionHandler->setSelectionSubframeViewportRect(WebCore::IntRect());
if (m_selectionScrollView) {
delete m_selectionScrollView;
m_selectionScrollView = 0;
}
}
bool InRegionScrollerPrivate::isActive() const
{
return m_activeInRegionScrollableAreas.size() > 0;
}
void InRegionScrollerPrivate::clearDocumentData(const Document* documentGoingAway)
{
InRegionScrollableArea* scrollableArea = static_cast<InRegionScrollableArea*>(m_selectionScrollView);
if (scrollableArea && scrollableArea->document() == documentGoingAway)
resetSelectionScrollView();
if (m_needsActiveScrollableAreaCalculation) {
reset();
return;
}
scrollableArea = static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[0]);
ASSERT(scrollableArea);
if (scrollableArea->document() == documentGoingAway)
reset();
}
bool InRegionScrollerPrivate::setScrollPositionCompositingThread(unsigned camouflagedLayer, const WebCore::IntPoint& scrollPosition)
{
LayerWebKitThread* layerWebKitThread = reinterpret_cast<LayerWebKitThread*>(camouflagedLayer);
if (!isValidScrollableLayerWebKitThread(layerWebKitThread))
return false;
LayerCompositingThread* scrollLayer = layerWebKitThread->layerCompositingThread();
FloatPoint anchor;
if (scrollLayer->override()->isAnchorPointSet())
anchor = scrollLayer->override()->anchorPoint();
else
anchor = scrollLayer->anchorPoint();
FloatSize bounds;
if (scrollLayer->override()->isBoundsSet())
bounds = scrollLayer->override()->bounds();
else
bounds = scrollLayer->bounds();
FloatPoint layerPosition(-scrollPosition.x() + anchor.x() * bounds.width(), -scrollPosition.y() + anchor.y() * bounds.height());
scrollLayer->override()->setPosition(FloatPoint(layerPosition.x(), layerPosition.y()));
return true;
}
bool InRegionScrollerPrivate::setScrollPositionWebKitThread(unsigned camouflagedLayer, const WebCore::IntPoint& scrollPosition,
bool supportsAcceleratedScrolling, Platform::ScrollViewBase::ScrollTarget scrollTarget)
{
RenderLayer* layer = 0;
if (supportsAcceleratedScrolling) {
LayerWebKitThread* layerWebKitThread = reinterpret_cast<LayerWebKitThread*>(camouflagedLayer);
if (!isValidScrollableLayerWebKitThread(layerWebKitThread))
return false;
if (layerWebKitThread->owner()) {
GraphicsLayer* graphicsLayer = layerWebKitThread->owner();
if (scrollTarget == Platform::ScrollViewBase::BlockElement) {
RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(graphicsLayer->client());
layer = backing->owningLayer();
} else {
RenderLayerCompositor* compositor = static_cast<RenderLayerCompositor*>(graphicsLayer->client());
layer = compositor->rootRenderLayer();
}
}
} else {
Node* node = reinterpret_cast<Node*>(camouflagedLayer);
if (!isValidScrollableNode(node) || !node->renderer())
return false;
layer = node->renderer()->enclosingLayer();
}
if (!layer)
return false;
calculateActiveAndShrinkCachedScrollableAreas(layer);
return setLayerScrollPosition(layer, scrollPosition);
}
void InRegionScrollerPrivate::calculateActiveAndShrinkCachedScrollableAreas(RenderLayer* layer)
{
if (!m_needsActiveScrollableAreaCalculation)
return;
ASSERT(layer);
std::vector<Platform::ScrollViewBase*>::iterator end = m_activeInRegionScrollableAreas.end();
std::vector<Platform::ScrollViewBase*>::iterator it = m_activeInRegionScrollableAreas.begin();
while (it != end) {
InRegionScrollableArea* curr = static_cast<InRegionScrollableArea*>(*it);
if (layer == curr->layer()) {
++it;
continue;
}
delete *it;
it = m_activeInRegionScrollableAreas.erase(it);
end = m_activeInRegionScrollableAreas.end();
}
ASSERT(m_activeInRegionScrollableAreas.size() == 1);
m_needsActiveScrollableAreaCalculation = false;
}
WebCore::IntRect InRegionScrollerPrivate::clipToRect(const WebCore::IntRect& clippingRect, InRegionScrollableArea* scrollable)
{
RenderLayer* layer = scrollable->layer();
if (!layer)
return clippingRect;
const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
if (layer->renderer()->isRenderView()) { FrameView* view = toRenderView(layer->renderer())->frameView();
ASSERT(view);
ASSERT(canScrollInnerFrame(view->frame()));
WebCore::IntRect frameWindowRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(m_webPage->getRecursiveVisibleWindowRect(view)));
frameWindowRect.intersect(clippingRect);
return frameWindowRect;
}
RenderBox* box = layer->renderBox();
ASSERT(box);
ASSERT(canScrollRenderBox(box));
WebCore::IntRect visibleWindowRect = enclosingIntRect(box->absoluteClippedOverflowRect());
visibleWindowRect = box->frame()->view()->contentsToWindow(visibleWindowRect);
visibleWindowRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(visibleWindowRect));
visibleWindowRect.intersect(clippingRect);
return visibleWindowRect;
}
void InRegionScrollerPrivate::calculateInRegionScrollableAreasForPoint(const WebCore::IntPoint& documentPoint)
{
ASSERT(m_activeInRegionScrollableAreas.empty());
m_needsActiveScrollableAreaCalculation = false;
const HitTestResult& result = m_webPage->hitTestResult(documentPoint);
Node* node = result.innerNonSharedNode();
if (!node || !node->renderer())
return;
RenderLayer* layer = node->renderer()->enclosingLayer();
if (!layer)
return;
do {
RenderObject* renderer = layer->renderer();
if (renderer && renderer->isRenderView()) {
if (RenderView* renderView = toRenderView(renderer)) {
FrameView* view = renderView->frameView();
if (!view) {
reset();
return;
}
if (!renderView->compositor()->scrollLayer())
continue;
if (canScrollInnerFrame(view->frame())) {
pushBackInRegionScrollable(new InRegionScrollableArea(m_webPage, layer));
continue;
}
}
} else if (canScrollRenderBox(layer->renderBox())) {
pushBackInRegionScrollable(new InRegionScrollableArea(m_webPage, layer));
continue;
}
if (isNonRenderViewFixedPositionedContainer(layer) && m_activeInRegionScrollableAreas.size()) {
Platform::ScrollViewBase* end = m_activeInRegionScrollableAreas.back();
end->setCanPropagateScrollingToEnclosingScrollable(false);
}
} while ((layer = parentLayer(layer)));
if (m_activeInRegionScrollableAreas.empty())
return;
m_needsActiveScrollableAreaCalculation = true;
WebCore::IntRect recursiveClippingRect(WebCore::IntPoint::zero(), m_webPage->transformedViewportSize());
for (int i = m_activeInRegionScrollableAreas.size() - 1; i >= 0; --i) {
InRegionScrollableArea* scrollable = static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i]);
scrollable->setVisibleWindowRect(clipToRect(recursiveClippingRect, scrollable));
recursiveClippingRect = scrollable->visibleWindowRect();
}
}
void InRegionScrollerPrivate::updateSelectionScrollView(const Node* node)
{
m_selectionScrollView = firstScrollableInRegionForNode(node);
m_webPage->m_client->notifySelectionScrollView(m_selectionScrollView);
m_webPage->m_selectionHandler->setSelectionSubframeViewportRect(m_selectionScrollView ? WebCore::IntRect(m_selectionScrollView->documentViewportRect()) : WebCore::IntRect());
}
Platform::ScrollViewBase* InRegionScrollerPrivate::firstScrollableInRegionForNode(const Node* node)
{
if (!node || !node->renderer())
return 0;
RenderLayer* layer = node->renderer()->enclosingLayer();
if (!layer)
return 0;
do {
RenderObject* renderer = layer->renderer();
if (renderer->isRenderView()) {
if (RenderView* renderView = toRenderView(renderer)) {
FrameView* view = renderView->frameView();
if (!view) {
resetSelectionScrollView();
return 0;
}
if (!renderView->compositor()->scrollLayer())
continue;
if (canScrollInnerFrame(view->frame()))
return clipAndCreateInRegionScrollableArea(layer);
}
} else if (canScrollRenderBox(layer->renderBox()))
return clipAndCreateInRegionScrollableArea(layer);
if (isNonRenderViewFixedPositionedContainer(layer) && m_activeInRegionScrollableAreas.size()) {
Platform::ScrollViewBase* end = m_activeInRegionScrollableAreas.back();
end->setCanPropagateScrollingToEnclosingScrollable(false);
}
} while ((layer = parentLayer(layer)));
return 0;
}
Platform::ScrollViewBase* InRegionScrollerPrivate::clipAndCreateInRegionScrollableArea(RenderLayer* layer)
{
WebCore::IntRect recursiveClippingRect(WebCore::IntPoint::zero(), m_webPage->transformedViewportSize());
InRegionScrollableArea* scrollable = new InRegionScrollableArea(m_webPage, layer);
scrollable->setVisibleWindowRect(clipToRect(recursiveClippingRect, scrollable));
return scrollable;
}
const std::vector<Platform::ScrollViewBase*>& InRegionScrollerPrivate::activeInRegionScrollableAreas() const
{
return m_activeInRegionScrollableAreas;
}
bool InRegionScrollerPrivate::setLayerScrollPosition(RenderLayer* layer, const IntPoint& scrollPosition)
{
RenderObject* layerRenderer = layer->renderer();
ASSERT(layerRenderer);
if (layerRenderer->isRenderView()) { FrameView* view = toRenderView(layerRenderer)->frameView();
ASSERT(view);
Frame* frame = view->frame();
ASSERT_UNUSED(frame, frame);
ASSERT(canScrollInnerFrame(frame));
view->setCanBlitOnScroll(false);
view->setScrollPosition(scrollPosition);
} else {
layer->scrollToOffset(toIntSize(scrollPosition));
}
layer->renderer()->frame()->selection()->updateAppearance();
return true;
}
void InRegionScrollerPrivate::adjustScrollDelta(const WebCore::IntPoint& maxOffset, const WebCore::IntPoint& currentOffset, WebCore::IntSize& delta) const
{
if (currentOffset.x() + delta.width() > maxOffset.x())
delta.setWidth(std::min(maxOffset.x() - currentOffset.x(), delta.width()));
if (currentOffset.x() + delta.width() < 0)
delta.setWidth(std::max(-currentOffset.x(), delta.width()));
if (currentOffset.y() + delta.height() > maxOffset.y())
delta.setHeight(std::min(maxOffset.y() - currentOffset.y(), delta.height()));
if (currentOffset.y() + delta.height() < 0)
delta.setHeight(std::max(-currentOffset.y(), delta.height()));
}
static bool canScrollInnerFrame(Frame* frame)
{
if (!frame || !frame->view())
return false;
if (!frame->ownerElement())
return false;
ASSERT(frame != frame->page()->mainFrame());
IntSize visibleSize = frame->view()->visibleContentRect().size();
IntSize contentsSize = frame->view()->contentsSize();
bool canBeScrolled = contentsSize.height() > visibleSize.height() || contentsSize.width() > visibleSize.width();
return canBeScrolled && (frame->ownerElement()->scrollingMode() != ScrollbarAlwaysOff);
}
bool InRegionScrollerPrivate::canScrollRenderBox(RenderBox* box)
{
if (!box)
return false;
if (box->layer() && box->layer()->usesCompositedScrolling()) {
if (box->style()->overflowScrolling() == OSBlackberryTouch)
return true;
}
if (!box->hasOverflowClip())
return false;
if (box->scrollHeight() == box->clientHeight() && box->scrollWidth() == box->clientWidth())
return false;
if ((box->scrollsOverflowX() && (box->scrollWidth() != box->clientWidth()))
|| (box->scrollsOverflowY() && (box->scrollHeight() != box->clientHeight())))
return true;
Node* node = box->node();
return node && (DOMSupport::isShadowHostTextInputElement(node) || node->isDocumentNode());
}
static RenderLayer* parentLayer(RenderLayer* layer)
{
ASSERT(layer);
if (layer->parent())
return layer->parent();
RenderObject* renderer = layer->renderer();
Document* document = renderer->document();
if (document) {
HTMLFrameOwnerElement* ownerElement = document->ownerElement();
if (ownerElement) {
RenderObject* subRenderer = ownerElement->renderer();
if (subRenderer)
return subRenderer->enclosingLayer();
}
}
return 0;
}
static bool isNonRenderViewFixedPositionedContainer(RenderLayer* layer)
{
RenderObject* o = layer->renderer();
if (o->isRenderView())
return false;
return o->isOutOfFlowPositioned() && o->style()->position() == FixedPosition;
}
void InRegionScrollerPrivate::pushBackInRegionScrollable(InRegionScrollableArea* scrollableArea)
{
ASSERT(!scrollableArea->isNull());
scrollableArea->setCanPropagateScrollingToEnclosingScrollable(!isNonRenderViewFixedPositionedContainer(scrollableArea->layer()));
m_activeInRegionScrollableAreas.push_back(scrollableArea);
}
bool InRegionScrollerPrivate::isValidScrollableLayerWebKitThread(LayerWebKitThread* layerWebKitThread) const
{
if (!layerWebKitThread)
return false;
for (unsigned i = 0; i < m_activeInRegionScrollableAreas.size(); ++i) {
if (static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i])->cachedScrollableLayer() == layerWebKitThread)
return true;
}
return false;
}
bool InRegionScrollerPrivate::isValidScrollableNode(Node* node) const
{
if (!node)
return false;
for (unsigned i = 0; i < m_activeInRegionScrollableAreas.size(); ++i) {
if (static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i])->cachedScrollableNode() == node)
return true;
}
return false;
}
}
}