#include "config.h"
#include "RenderRegion.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "IntRect.h"
#include "PaintInfo.h"
#include "RenderBoxRegionInfo.h"
#include "RenderNamedFlowThread.h"
#include "RenderView.h"
#include "StyleResolver.h"
namespace WebCore {
RenderRegion::RenderRegion(Node* node, RenderFlowThread* flowThread)
: RenderReplaced(node, IntSize())
, m_flowThread(flowThread)
, m_parentNamedFlowThread(0)
, m_isValid(false)
, m_hasCustomRegionStyle(false)
, m_regionState(RegionUndefined)
, m_dispatchRegionLayoutUpdateEvent(false)
{
ASSERT(node->document()->cssRegionsEnabled());
}
LayoutRect RenderRegion::regionOverflowRect() const
{
bool clipX = style()->overflowX() != OVISIBLE;
bool clipY = style()->overflowY() != OVISIBLE;
if ((clipX && clipY) || !isValid() || !m_flowThread)
return regionRect();
LayoutRect flowThreadOverflow = m_flowThread->visualOverflowRect();
LayoutUnit outlineSize = maximalOutlineSize(PaintPhaseOutline);
LayoutRect clipRect;
if (m_flowThread->isHorizontalWritingMode()) {
LayoutUnit minY = isFirstRegion() ? (flowThreadOverflow.y() - outlineSize) : regionRect().y();
LayoutUnit maxY = isLastRegion() ? max(regionRect().maxY(), flowThreadOverflow.maxY()) + outlineSize : regionRect().maxY();
LayoutUnit minX = clipX ? regionRect().x() : (flowThreadOverflow.x() - outlineSize);
LayoutUnit maxX = clipX ? regionRect().maxX() : (flowThreadOverflow.maxX() + outlineSize);
clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY);
} else {
LayoutUnit minX = isFirstRegion() ? (flowThreadOverflow.x() - outlineSize) : regionRect().x();
LayoutUnit maxX = isLastRegion() ? max(regionRect().maxX(), flowThreadOverflow.maxX()) + outlineSize : regionRect().maxX();
LayoutUnit minY = clipY ? regionRect().y() : (flowThreadOverflow.y() - outlineSize);
LayoutUnit maxY = clipY ? regionRect().maxY() : (flowThreadOverflow.maxY() + outlineSize);
clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY);
}
return clipRect;
}
bool RenderRegion::isFirstRegion() const
{
ASSERT(isValid() && m_flowThread);
return m_flowThread->firstRegion() == this;
}
bool RenderRegion::isLastRegion() const
{
ASSERT(isValid() && m_flowThread);
return m_flowThread->lastRegion() == this;
}
void RenderRegion::setRegionBoxesRegionStyle()
{
if (!hasCustomRegionStyle())
return;
for (RenderBoxRegionInfoMap::iterator iter = m_renderBoxRegionInfo.begin(), end = m_renderBoxRegionInfo.end(); iter != end; ++iter) {
const RenderBox* box = iter->first;
if (!box->canHaveRegionStyle())
continue;
RefPtr<RenderStyle> boxOriginalStyle = box->style();
(const_cast<RenderBox*>(box))->setStyleInternal(renderBoxRegionStyle(box));
m_renderBoxRegionStyle.set(box, boxOriginalStyle);
}
}
void RenderRegion::restoreRegionBoxesOriginalStyle()
{
if (!hasCustomRegionStyle())
return;
for (RenderBoxRegionInfoMap::iterator iter = m_renderBoxRegionInfo.begin(), end = m_renderBoxRegionInfo.end(); iter != end; ++iter) {
const RenderBox* box = iter->first;
RenderBoxRegionStyleMap::iterator it = m_renderBoxRegionStyle.find(box);
if (it == m_renderBoxRegionStyle.end())
continue;
RefPtr<RenderStyle> boxRegionStyle = box->style();
(const_cast<RenderBox*>(box))->setStyleInternal(it->second);
m_renderBoxRegionStyle.set(box, boxRegionStyle);
}
}
void RenderRegion::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (!m_flowThread || !isValid())
return;
setRegionBoxesRegionStyle();
m_flowThread->paintIntoRegion(paintInfo, this, LayoutPoint(paintOffset.x() + borderLeft() + paddingLeft(), paintOffset.y() + borderTop() + paddingTop()));
restoreRegionBoxesOriginalStyle();
}
bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
if (!isValid())
return false;
LayoutPoint adjustedLocation = accumulatedOffset + location();
LayoutRect boundsRect = borderBoxRectInRegion(result.region());
boundsRect.moveBy(adjustedLocation);
if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(pointInContainer))) {
if (m_flowThread && m_flowThread->hitTestRegion(this, request, result, pointInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop())))
return true;
updateHitTestResult(result, pointInContainer - toLayoutSize(adjustedLocation));
if (!result.addNodeToRectBasedTestResult(node(), pointInContainer, boundsRect))
return true;
}
return false;
}
void RenderRegion::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderReplaced::styleDidChange(diff, oldStyle);
bool customRegionStyle = false;
if (node()) {
Element* regionElement = static_cast<Element*>(node());
customRegionStyle = view()->document()->styleResolver()->checkRegionStyle(regionElement);
}
setHasCustomRegionStyle(customRegionStyle);
}
void RenderRegion::layout()
{
RenderReplaced::layout();
if (m_flowThread && isValid()) {
if (regionRect().width() != contentWidth() || regionRect().height() != contentHeight())
m_flowThread->invalidateRegions();
}
}
void RenderRegion::attachRegion()
{
if (!m_flowThread)
return;
RenderObject* parentObject = parent();
m_parentNamedFlowThread = 0;
for ( ; parentObject; parentObject = parentObject->parent()) {
if (parentObject->isRenderNamedFlowThread()) {
m_parentNamedFlowThread = toRenderNamedFlowThread(parentObject);
if (m_flowThread == m_parentNamedFlowThread) {
m_flowThread = 0;
return;
}
break;
}
}
m_flowThread->addRegionToThread(this);
}
void RenderRegion::detachRegion()
{
if (m_flowThread)
m_flowThread->removeRegionFromThread(this);
}
RenderBoxRegionInfo* RenderRegion::renderBoxRegionInfo(const RenderBox* box) const
{
if (!m_isValid || !m_flowThread)
return 0;
return m_renderBoxRegionInfo.get(box);
}
RenderBoxRegionInfo* RenderRegion::setRenderBoxRegionInfo(const RenderBox* box, LayoutUnit logicalLeftInset, LayoutUnit logicalRightInset,
bool containingBlockChainIsInset)
{
ASSERT(m_isValid && m_flowThread);
if (!m_isValid || !m_flowThread)
return 0;
OwnPtr<RenderBoxRegionInfo>& boxInfo = m_renderBoxRegionInfo.add(box, nullptr).iterator->second;
if (boxInfo)
*boxInfo = RenderBoxRegionInfo(logicalLeftInset, logicalRightInset, containingBlockChainIsInset);
else
boxInfo = adoptPtr(new RenderBoxRegionInfo(logicalLeftInset, logicalRightInset, containingBlockChainIsInset));
return boxInfo.get();
}
PassOwnPtr<RenderBoxRegionInfo> RenderRegion::takeRenderBoxRegionInfo(const RenderBox* box)
{
return m_renderBoxRegionInfo.take(box);
}
void RenderRegion::removeRenderBoxRegionInfo(const RenderBox* box)
{
m_renderBoxRegionInfo.remove(box);
}
void RenderRegion::deleteAllRenderBoxRegionInfo()
{
m_renderBoxRegionInfo.clear();
}
LayoutUnit RenderRegion::offsetFromLogicalTopOfFirstPage() const
{
if (!m_isValid || !m_flowThread)
return 0;
if (m_flowThread->isHorizontalWritingMode())
return regionRect().y();
return regionRect().x();
}
PassRefPtr<RenderStyle> RenderRegion::renderBoxRegionStyle(const RenderBox* renderBox)
{
ASSERT(m_renderBoxRegionInfo.find(renderBox) != m_renderBoxRegionInfo.end());
RenderBoxRegionStyleMap::iterator it = m_renderBoxRegionStyle.find(renderBox);
if (it != m_renderBoxRegionStyle.end())
return it->second;
return computeStyleInRegion(renderBox);
}
PassRefPtr<RenderStyle> RenderRegion::computeStyleInRegion(const RenderBox* box)
{
ASSERT(box);
ASSERT(box->view());
ASSERT(box->view()->document());
ASSERT(!box->isAnonymous());
ASSERT(box->node() && box->node()->isElementNode());
Element* element = toElement(box->node());
RefPtr<RenderStyle> renderBoxRegionStyle = box->view()->document()->styleResolver()->styleForElement(element, 0, DisallowStyleSharing, MatchAllRules, this);
m_renderBoxRegionStyle.add(box, renderBoxRegionStyle);
if (!box->hasBoxDecorations()) {
bool hasBoxDecorations = box->isTableCell() || renderBoxRegionStyle->hasBackground() || renderBoxRegionStyle->hasBorder() || renderBoxRegionStyle->hasAppearance() || renderBoxRegionStyle->boxShadow();
(const_cast<RenderBox*>(box))->setHasBoxDecorations(hasBoxDecorations);
}
return renderBoxRegionStyle.release();
}
void RenderRegion::clearBoxStyleInRegion(const RenderBox* box)
{
ASSERT(box);
m_renderBoxRegionStyle.remove(box);
}
}