blob: 4d389cd8b2d3a0766de169f15bed2e208d69af5c [file] [log] [blame]
/*
* Copyright (C) 2009, 2010, 2011 Research In Motion Limited. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "BackingStoreClient.h"
#include "BackingStore.h"
#include "BackingStore_p.h"
#include "FloatPoint.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameView.h"
#include "HTMLFrameOwnerElement.h"
#include "Page.h"
#include "RenderBox.h"
#include "WebPage_p.h"
// FIXME: Leaving the below lines commented out as a reference for us to soon be sure if we need these
// methods and class variables be moved from WebPage to BackingStoreClient.
// Notification methods that deliver changes to the real geometry of the device as specified above.
// void notifyTransformChanged();
// void notifyTransformedContentsSizeChanged();
// void notifyTransformedScrollChanged();
// m_overflowExceedsContentsSize = true;
// haspendingscrollevent
using namespace WebCore;
namespace BlackBerry {
namespace WebKit {
static inline IntSize pointToSize(const IntPoint& point)
{
return IntSize(point.x(), point.y());
}
BackingStoreClient* BackingStoreClient::create(Frame* frame, Frame* parentFrame, WebPage* parentPage)
{
ASSERT(parentPage);
ASSERT(frame->view());
// FIXME: We do not support inner frames for now.
if (parentFrame)
return 0;
BackingStoreClient* parentBackingStoreClient
= parentFrame
? parentPage->d->backingStoreClientForFrame(parentFrame)
: 0;
// If this frame has a parent with no backingstore then just stop since
// our frame heirarchy is done.
if (parentFrame && !parentBackingStoreClient)
return 0;
BackingStoreClient* it = new BackingStoreClient(frame, parentFrame, parentPage);
ASSERT(it);
// Frame -> BackingStoreClient mapping is controlled by the Page.
parentPage->d->addBackingStoreClientForFrame(frame, it);
// Add the backing store client to the child list of its parent.
if (parentBackingStoreClient)
parentBackingStoreClient->addChild(it);
return it;
}
BackingStoreClient::BackingStoreClient(Frame* frame, Frame* parentFrame, WebPage* parentPage)
: m_frame(frame)
, m_webPage(parentPage)
, m_backingStore(0)
, m_parent(0)
, m_isClientGeneratedScroll(false)
, m_isScrollNotificationSuppressed(false)
{
UNUSED_PARAM(parentFrame);
m_backingStore = new BackingStore(m_webPage, this);
}
BackingStoreClient::~BackingStoreClient()
{
m_webPage->d->removeBackingStoreClientForFrame(m_frame);
delete m_backingStore;
m_backingStore = 0;
m_frame = 0;
}
void BackingStoreClient::addChild(BackingStoreClient* child)
{
ASSERT(child);
child->m_parent = this;
}
WTF::Vector <BackingStoreClient*> BackingStoreClient::children() const
{
WTF::Vector<BackingStoreClient*> children;
for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
BlackBerry::WebKit::BackingStoreClient* client =
m_webPage->d->backingStoreClientForFrame(child);
if (client)
children.append(client);
}
return children;
}
IntRect BackingStoreClient::absoluteRect() const
{
IntRect rect = IntRect(IntPoint::zero(), viewportSize());
if (!isMainFrame()) {
// It is possible that the owner HTML element has been removed at this point,
// especially when the frame is loading a JavaScript URL.
if (Element* elt = m_frame->ownerElement()) {
if (RenderBox* obj = elt->renderBox())
rect.move(obj->borderLeft() + obj->paddingLeft(), obj->borderTop() + obj->paddingTop());
}
}
Frame* frame = m_frame;
while (frame) {
if (Element* element = static_cast<Element*>(frame->ownerElement())) {
do {
rect.move(element->offsetLeft(), element->offsetTop());
} while ((element = element->offsetParent()));
}
if ((frame = frame->tree()->parent()))
rect.move((-frame->view()->scrollOffset()));
}
return rect;
}
IntRect BackingStoreClient::transformedAbsoluteRect() const
{
return m_webPage->d->mapToTransformed(absoluteRect());
}
IntPoint BackingStoreClient::absoluteLocation() const
{
return absoluteRect().location();
}
IntPoint BackingStoreClient::transformedAbsoluteLocation() const
{
return m_webPage->d->mapToTransformed(transformedAbsoluteRect()).location();
}
IntPoint BackingStoreClient::scrollPosition() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntPoint();
return m_frame->view()->scrollPosition() - pointToSize(m_frame->view()->minimumScrollPosition());
}
IntPoint BackingStoreClient::transformedScrollPosition() const
{
return m_webPage->d->mapToTransformed(scrollPosition());
}
void BackingStoreClient::setScrollPosition(const IntPoint& pos)
{
ASSERT(m_frame);
if (!m_frame->view())
return;
if (pos == scrollPosition())
return;
// We set a flag here to note that this scroll operation was originated
// within the BlackBerry-specific layer of WebKit and not by WebCore.
// This flag is checked in checkOriginOfCurrentScrollOperation() to decide
// whether to notify the client of the current scroll operation. This is
// why it is important that all scroll operations that originate within
// BlackBerry-specific code are encapsulated here and that callers of this
// method also directly or indirectly call notifyTransformedScrollChanged().
m_isScrollNotificationSuppressed = true;
m_frame->view()->setScrollPosition(pos + pointToSize(m_frame->view()->minimumScrollPosition()));
m_isScrollNotificationSuppressed = false;
}
IntPoint BackingStoreClient::maximumScrollPosition() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntPoint();
return m_frame->view()->maximumScrollPosition() - pointToSize(m_frame->view()->minimumScrollPosition());
}
IntPoint BackingStoreClient::transformedMaximumScrollPosition() const
{
return m_webPage->d->mapToTransformed(maximumScrollPosition());
}
IntSize BackingStoreClient::actualVisibleSize() const
{
return m_webPage->d->mapFromTransformed(transformedActualVisibleSize());
}
IntSize BackingStoreClient::transformedActualVisibleSize() const
{
if (isMainFrame())
return m_webPage->d->transformedActualVisibleSize();
return transformedViewportSize();
}
IntSize BackingStoreClient::viewportSize() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntSize();
if (isMainFrame())
return m_webPage->d->viewportSize();
return m_frame->view()->visibleContentRect().size();
}
IntSize BackingStoreClient::transformedViewportSize() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntSize();
if (isMainFrame())
return m_webPage->d->transformedViewportSize();
const IntSize untransformedViewportSize = m_frame->view()->visibleContentRect().size();
const FloatPoint transformedBottomRight = m_webPage->d->m_transformationMatrix->mapPoint(
FloatPoint(untransformedViewportSize.width(), untransformedViewportSize.height()));
return IntSize(floorf(transformedBottomRight.x()), floorf(transformedBottomRight.y()));
}
IntRect BackingStoreClient::visibleContentsRect() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntRect();
IntRect visibleContentRect = m_frame->view()->visibleContentRect();
if (isMainFrame())
return visibleContentRect;
IntPoint offset = absoluteLocation();
visibleContentRect.move(offset.x(), offset.y());
if (m_parent)
visibleContentRect.intersect(m_parent->visibleContentsRect());
return visibleContentRect;
}
IntRect BackingStoreClient::transformedVisibleContentsRect() const
{
// Usually this would be mapToTransformed(visibleContentsRect()), but
// that results in rounding errors because we already set the WebCore
// viewport size from our original transformedViewportSize().
// Instead, we only transform the scroll position and take the
// viewport size as it is, which ensures that e.g. blitting operations
// always cover the whole widget/screen.
IntRect visibleContentsRect = IntRect(transformedScrollPosition(), transformedViewportSize());
if (isMainFrame())
return visibleContentsRect;
IntPoint offset = transformedAbsoluteLocation();
visibleContentsRect.move(offset.x(), offset.y());
return visibleContentsRect;
}
IntSize BackingStoreClient::contentsSize() const
{
ASSERT(m_frame);
if (!m_frame->view())
return IntSize();
return m_frame->view()->contentsSize();
}
IntSize BackingStoreClient::transformedContentsSize() const
{
// mapToTransformed() functions use this method to crop their results,
// so we can't make use of them here. While we want rounding inside page
// boundaries to extend rectangles and round points, we need to crop the
// contents size to the floored values so that we don't try to display
// or report points that are not fully covered by the actual float-point
// contents rectangle.
const IntSize untransformedContentsSize = contentsSize();
const FloatPoint transformedBottomRight = m_webPage->d->m_transformationMatrix->mapPoint(
FloatPoint(untransformedContentsSize.width(), untransformedContentsSize.height()));
return IntSize(floorf(transformedBottomRight.x()), floorf(transformedBottomRight.y()));
}
void BackingStoreClient::clipToTransformedContentsRect(IntRect& rect) const
{
// FIXME: Needs to proper translate coordinates here?
rect.intersect(IntRect(IntPoint::zero(), transformedContentsSize()));
}
IntPoint BackingStoreClient::mapFromContentsToViewport(const IntPoint& point) const
{
const IntPoint scrollPosition = this->scrollPosition();
return IntPoint(point.x() - scrollPosition.x(), point.y() - scrollPosition.y());
}
IntPoint BackingStoreClient::mapFromViewportToContents(const IntPoint& point) const
{
const IntPoint scrollPosition = this->scrollPosition();
return IntPoint(point.x() + scrollPosition.x(), point.y() + scrollPosition.y());
}
IntRect BackingStoreClient::mapFromContentsToViewport(const IntRect& rect) const
{
return IntRect(mapFromContentsToViewport(rect.location()), rect.size());
}
IntRect BackingStoreClient::mapFromViewportToContents(const IntRect& rect) const
{
return IntRect(mapFromViewportToContents(rect.location()), rect.size());
}
IntPoint BackingStoreClient::mapFromTransformedContentsToTransformedViewport(const IntPoint& point) const
{
const IntPoint scrollPosition = transformedScrollPosition();
return IntPoint(point.x() - scrollPosition.x(), point.y() - scrollPosition.y());
}
IntPoint BackingStoreClient::mapFromTransformedViewportToTransformedContents(const IntPoint& point) const
{
const IntPoint scrollPosition = transformedScrollPosition();
return IntPoint(point.x() + scrollPosition.x(), point.y() + scrollPosition.y());
}
IntRect BackingStoreClient::mapFromTransformedContentsToTransformedViewport(const IntRect& rect) const
{
return IntRect(mapFromTransformedContentsToTransformedViewport(rect.location()), rect.size());
}
IntRect BackingStoreClient::mapFromTransformedViewportToTransformedContents(const IntRect& rect) const
{
return IntRect(mapFromTransformedViewportToTransformedContents(rect.location()), rect.size());
}
WebPagePrivate::LoadState BackingStoreClient::loadState() const
{
// FIXME: Does it need to call WebPage's?
return m_webPage->d->loadState();
}
bool BackingStoreClient::isLoading() const
{
// FIXME: Does it need to call WebPage's?
return m_webPage->d->isLoading();
}
bool BackingStoreClient::isFocused() const
{
return m_frame && m_frame->page() && m_frame->page()->focusController()
&& m_frame->page()->focusController()->focusedFrame() == m_frame;
}
bool BackingStoreClient::isClientGeneratedScroll() const
{
return m_isClientGeneratedScroll;
}
void BackingStoreClient::setIsClientGeneratedScroll(bool flag)
{
m_isClientGeneratedScroll = flag;
}
bool BackingStoreClient::isScrollNotificationSuppressed() const
{
return m_isScrollNotificationSuppressed;
}
void BackingStoreClient::setIsScrollNotificationSuppressed(bool flag)
{
m_isScrollNotificationSuppressed = flag;
}
void BackingStoreClient::checkOriginOfCurrentScrollOperation()
{
// This is called via ChromeClientBlackBerry::scroll in order to check the origin
// of the current scroll operation to decide whether to notify the client.
// If the current scroll operation was initiated internally by WebCore itself
// either via JavaScript, back/forward or otherwise then we need to go ahead
// and notify the client of this change.
if (isScrollNotificationSuppressed())
return;
if (isMainFrame())
m_webPage->d->notifyTransformedScrollChanged();
else
m_backingStore->d->scrollChanged(transformedScrollPosition());
}
}
}