RenderFrameSet.cpp [plain text]
#include "config.h"
#include "RenderFrameSet.h"
#include "Cursor.h"
#include "Document.h"
#include "EventHandler.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLFrameSetElement.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "MouseEvent.h"
#include "PaintInfo.h"
#include "RenderFrame.h"
#include "RenderIterator.h"
#include "RenderLayer.h"
#include "RenderView.h"
#include "Settings.h"
#include <wtf/StackStats.h>
namespace WebCore {
RenderFrameSet::RenderFrameSet(HTMLFrameSetElement& frameSet, PassRef<RenderStyle> style)
: RenderBox(frameSet, WTF::move(style), 0)
, m_isResizing(false)
, m_isChildResizing(false)
{
setInline(false);
}
RenderFrameSet::~RenderFrameSet()
{
}
HTMLFrameSetElement& RenderFrameSet::frameSetElement() const
{
return toHTMLFrameSetElement(nodeForNonAnonymous());
}
RenderFrameSet::GridAxis::GridAxis()
: m_splitBeingResized(noSplit)
{
}
static Color borderStartEdgeColor()
{
return Color(170, 170, 170);
}
static Color borderEndEdgeColor()
{
return Color::black;
}
static Color borderFillColor()
{
return Color(208, 208, 208);
}
void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
{
if (!paintInfo.rect.intersects(borderRect))
return;
GraphicsContext* context = paintInfo.context;
ColorSpace colorSpace = style().colorSpace();
context->fillRect(borderRect, frameSetElement().hasBorderColor() ? style().visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
if (borderRect.width() >= 3) {
context->fillRect(IntRect(borderRect.location(), IntSize(1, height())), borderStartEdgeColor(), colorSpace);
context->fillRect(IntRect(IntPoint(borderRect.maxX() - 1, borderRect.y()), IntSize(1, height())), borderEndEdgeColor(), colorSpace);
}
}
void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
{
if (!paintInfo.rect.intersects(borderRect))
return;
GraphicsContext* context = paintInfo.context;
ColorSpace colorSpace = style().colorSpace();
context->fillRect(borderRect, frameSetElement().hasBorderColor() ? style().visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
if (borderRect.height() >= 3) {
context->fillRect(IntRect(borderRect.location(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace);
context->fillRect(IntRect(IntPoint(borderRect.x(), borderRect.maxY() - 1), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace);
}
}
void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (paintInfo.phase != PaintPhaseForeground)
return;
RenderObject* child = firstChild();
if (!child)
return;
LayoutPoint adjustedPaintOffset = paintOffset + location();
size_t rows = m_rows.m_sizes.size();
size_t cols = m_cols.m_sizes.size();
LayoutUnit borderThickness = frameSetElement().border();
LayoutUnit yPos = 0;
for (size_t r = 0; r < rows; r++) {
LayoutUnit xPos = 0;
for (size_t c = 0; c < cols; c++) {
toRenderElement(child)->paint(paintInfo, adjustedPaintOffset);
xPos += m_cols.m_sizes[c];
if (borderThickness && m_cols.m_allowBorder[c + 1]) {
paintColumnBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x() + xPos, adjustedPaintOffset.y() + yPos, borderThickness, height())));
xPos += borderThickness;
}
child = child->nextSibling();
if (!child)
return;
}
yPos += m_rows.m_sizes[r];
if (borderThickness && m_rows.m_allowBorder[r + 1]) {
paintRowBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness)));
yPos += borderThickness;
}
}
}
void RenderFrameSet::GridAxis::resize(int size)
{
m_sizes.resize(size);
m_deltas.resize(size);
m_deltas.fill(0);
m_preventResize.resize(size + 1);
m_allowBorder.resize(size + 1);
}
void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
{
availableLen = std::max(availableLen, 0);
int* gridLayout = axis.m_sizes.data();
if (!grid) {
gridLayout[0] = availableLen;
return;
}
int gridLen = axis.m_sizes.size();
ASSERT(gridLen);
int totalRelative = 0;
int totalFixed = 0;
int totalPercent = 0;
int countRelative = 0;
int countFixed = 0;
int countPercent = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
gridLayout[i] = std::max(grid[i].intValue(), 0);
totalFixed += gridLayout[i];
countFixed++;
}
if (grid[i].isPercent()) {
gridLayout[i] = std::max(intValueForLength(grid[i], availableLen), 0);
totalPercent += gridLayout[i];
countPercent++;
}
if (grid[i].isRelative()) {
totalRelative += std::max(grid[i].intValue(), 1);
countRelative++;
}
}
int remainingLen = availableLen;
if (totalFixed > remainingLen) {
int remainingFixed = remainingLen;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
remainingLen -= gridLayout[i];
}
}
} else
remainingLen -= totalFixed;
if (totalPercent > remainingLen) {
int remainingPercent = remainingLen;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
remainingLen -= gridLayout[i];
}
}
} else
remainingLen -= totalPercent;
if (countRelative) {
int lastRelative = 0;
int remainingRelative = remainingLen;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isRelative()) {
gridLayout[i] = (std::max(grid[i].intValue(), 1) * remainingRelative) / totalRelative;
remainingLen -= gridLayout[i];
lastRelative = i;
}
}
if (remainingLen) {
gridLayout[lastRelative] += remainingLen;
remainingLen = 0;
}
}
if (remainingLen) {
if (countPercent && totalPercent) {
int remainingPercent = remainingLen;
int changePercent = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
gridLayout[i] += changePercent;
remainingLen -= changePercent;
}
}
} else if (totalFixed) {
int remainingFixed = remainingLen;
int changeFixed = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
gridLayout[i] += changeFixed;
remainingLen -= changeFixed;
}
}
}
}
if (remainingLen && countPercent) {
int remainingPercent = remainingLen;
int changePercent = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
changePercent = remainingPercent / countPercent;
gridLayout[i] += changePercent;
remainingLen -= changePercent;
}
}
} else if (remainingLen && countFixed) {
int remainingFixed = remainingLen;
int changeFixed = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
changeFixed = remainingFixed / countFixed;
gridLayout[i] += changeFixed;
remainingLen -= changeFixed;
}
}
}
if (remainingLen)
gridLayout[gridLen - 1] += remainingLen;
bool worked = true;
int* gridDelta = axis.m_deltas.data();
for (int i = 0; i < gridLen; ++i) {
if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
worked = false;
gridLayout[i] += gridDelta[i];
}
if (!worked) {
for (int i = 0; i < gridLen; ++i)
gridLayout[i] -= gridDelta[i];
axis.m_deltas.fill(0);
}
}
void RenderFrameSet::notifyFrameEdgeInfoChanged()
{
if (needsLayout())
return;
computeEdgeInfo();
}
void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
{
if (edgeInfo.allowBorder(LeftFrameEdge))
m_cols.m_allowBorder[c] = true;
if (edgeInfo.allowBorder(RightFrameEdge))
m_cols.m_allowBorder[c + 1] = true;
if (edgeInfo.preventResize(LeftFrameEdge))
m_cols.m_preventResize[c] = true;
if (edgeInfo.preventResize(RightFrameEdge))
m_cols.m_preventResize[c + 1] = true;
if (edgeInfo.allowBorder(TopFrameEdge))
m_rows.m_allowBorder[r] = true;
if (edgeInfo.allowBorder(BottomFrameEdge))
m_rows.m_allowBorder[r + 1] = true;
if (edgeInfo.preventResize(TopFrameEdge))
m_rows.m_preventResize[r] = true;
if (edgeInfo.preventResize(BottomFrameEdge))
m_rows.m_preventResize[r + 1] = true;
}
void RenderFrameSet::computeEdgeInfo()
{
m_rows.m_preventResize.fill(frameSetElement().noResize());
m_rows.m_allowBorder.fill(false);
m_cols.m_preventResize.fill(frameSetElement().noResize());
m_cols.m_allowBorder.fill(false);
RenderObject* child = firstChild();
if (!child)
return;
size_t rows = m_rows.m_sizes.size();
size_t cols = m_cols.m_sizes.size();
for (size_t r = 0; r < rows; ++r) {
for (size_t c = 0; c < cols; ++c) {
FrameEdgeInfo edgeInfo;
if (child->isFrameSet())
edgeInfo = toRenderFrameSet(child)->edgeInfo();
else
edgeInfo = toRenderFrame(child)->edgeInfo();
fillFromEdgeInfo(edgeInfo, r, c);
child = child->nextSibling();
if (!child)
return;
}
}
}
FrameEdgeInfo RenderFrameSet::edgeInfo() const
{
FrameEdgeInfo result(frameSetElement().noResize(), true);
int rows = frameSetElement().totalRows();
int cols = frameSetElement().totalCols();
if (rows && cols) {
result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
}
return result;
}
void RenderFrameSet::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
ASSERT(needsLayout());
bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout();
LayoutRect oldBounds;
RenderLayerModelObject* repaintContainer = 0;
if (doFullRepaint) {
repaintContainer = containerForRepaint();
oldBounds = clippedOverflowRectForRepaint(repaintContainer);
}
if (!parent()->isFrameSet() && !document().printing()) {
setWidth(view().viewWidth());
setHeight(view().viewHeight());
}
unsigned cols = frameSetElement().totalCols();
unsigned rows = frameSetElement().totalRows();
if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
m_rows.resize(rows);
m_cols.resize(cols);
}
LayoutUnit borderThickness = frameSetElement().border();
layOutAxis(m_rows, frameSetElement().rowLengths(), height() - (rows - 1) * borderThickness);
layOutAxis(m_cols, frameSetElement().colLengths(), width() - (cols - 1) * borderThickness);
if (flattenFrameSet())
positionFramesWithFlattening();
else
positionFrames();
RenderBox::layout();
computeEdgeInfo();
updateLayerTransform();
if (doFullRepaint) {
repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds));
LayoutRect newBounds = clippedOverflowRectForRepaint(repaintContainer);
if (newBounds != oldBounds)
repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds));
}
clearNeedsLayout();
}
void RenderFrameSet::positionFrames()
{
RenderBox* child = firstChildBox();
if (!child)
return;
int rows = frameSetElement().totalRows();
int cols = frameSetElement().totalCols();
int yPos = 0;
int borderThickness = frameSetElement().border();
for (int r = 0; r < rows; r++) {
int xPos = 0;
int height = m_rows.m_sizes[r];
for (int c = 0; c < cols; c++) {
child->setLocation(IntPoint(xPos, yPos));
int width = m_cols.m_sizes[c];
if (width != child->width() || height != child->height()) {
child->setWidth(width);
child->setHeight(height);
#if PLATFORM(IOS)
child->setNeedsLayout(MarkOnlyThis);
#else
child->setNeedsLayout();
#endif
child->layout();
}
xPos += width + borderThickness;
child = child->nextSiblingBox();
if (!child)
return;
}
yPos += height + borderThickness;
}
for (; child; child = child->nextSiblingBox()) {
child->setWidth(0);
child->setHeight(0);
child->clearNeedsLayout();
}
}
void RenderFrameSet::positionFramesWithFlattening()
{
RenderBox* child = firstChildBox();
if (!child)
return;
int rows = frameSetElement().totalRows();
int cols = frameSetElement().totalCols();
int borderThickness = frameSetElement().border();
bool repaintNeeded = false;
bool out = false;
for (int r = 0; r < rows && !out; r++) {
int extra = 0;
int height = m_rows.m_sizes[r];
for (int c = 0; c < cols; c++) {
IntRect oldFrameRect = pixelSnappedIntRect(child->frameRect());
int width = m_cols.m_sizes[c];
bool fixedWidth = frameSetElement().colLengths() && frameSetElement().colLengths()[c].isFixed();
bool fixedHeight = frameSetElement().rowLengths() && frameSetElement().rowLengths()[r].isFixed();
if (!fixedWidth)
child->setWidth(width ? width + extra / (cols - c) : 0);
else
child->setWidth(width);
child->setHeight(height);
child->setNeedsLayout();
if (child->isFrameSet())
toRenderFrameSet(child)->layout();
else
toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight);
if (child->height() > m_rows.m_sizes[r])
m_rows.m_sizes[r] = child->height();
if (child->width() > m_cols.m_sizes[c])
m_cols.m_sizes[c] = child->width();
if (child->frameRect() != oldFrameRect)
repaintNeeded = true;
extra += width - m_cols.m_sizes[c];
child = child->nextSiblingBox();
if (!child) {
out = true;
break;
}
}
}
int xPos = 0;
int yPos = 0;
out = false;
child = firstChildBox();
for (int r = 0; r < rows && !out; r++) {
xPos = 0;
for (int c = 0; c < cols; c++) {
IntRect oldRect = pixelSnappedIntRect(child->frameRect());
child->setLocation(IntPoint(xPos, yPos));
child->setHeight(m_rows.m_sizes[r]);
child->setWidth(m_cols.m_sizes[c]);
if (child->frameRect() != oldRect) {
repaintNeeded = true;
child->setNeedsLayout();
if (child->isFrameSet())
toRenderFrameSet(child)->layout();
else
toRenderFrame(child)->layoutWithFlattening(true, true);
}
xPos += m_cols.m_sizes[c] + borderThickness;
child = child->nextSiblingBox();
if (!child) {
out = true;
break;
}
}
yPos += m_rows.m_sizes[r] + borderThickness;
}
setWidth(xPos - borderThickness);
setHeight(yPos - borderThickness);
if (repaintNeeded)
repaint();
for (; child; child = child->nextSiblingBox()) {
child->setWidth(0);
child->setHeight(0);
child->clearNeedsLayout();
}
}
bool RenderFrameSet::flattenFrameSet() const
{
return frame().settings().frameFlatteningEnabled();
}
void RenderFrameSet::startResizing(GridAxis& axis, int position)
{
int split = hitTestSplit(axis, position);
if (split == noSplit || axis.m_preventResize[split]) {
axis.m_splitBeingResized = noSplit;
return;
}
axis.m_splitBeingResized = split;
axis.m_splitResizeOffset = position - splitPosition(axis, split);
}
void RenderFrameSet::continueResizing(GridAxis& axis, int position)
{
if (needsLayout())
return;
if (axis.m_splitBeingResized == noSplit)
return;
int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
if (!delta)
return;
axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
axis.m_deltas[axis.m_splitBeingResized] -= delta;
setNeedsLayout();
}
bool RenderFrameSet::userResize(MouseEvent* evt)
{
if (flattenFrameSet())
return false;
if (!m_isResizing) {
if (needsLayout())
return false;
if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) {
FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
startResizing(m_cols, localPos.x());
startResizing(m_rows, localPos.y());
if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
setIsResizing(true);
return true;
}
}
} else {
if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) {
FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
continueResizing(m_cols, localPos.x());
continueResizing(m_rows, localPos.y());
if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) {
setIsResizing(false);
return true;
}
}
}
return false;
}
void RenderFrameSet::setIsResizing(bool isResizing)
{
m_isResizing = isResizing;
for (auto& ancestor : ancestorsOfType<RenderFrameSet>(*this))
ancestor.m_isChildResizing = isResizing;
frame().eventHandler().setResizingFrameSet(isResizing ? &frameSetElement() : nullptr);
}
bool RenderFrameSet::isResizingRow() const
{
return m_isResizing && m_rows.m_splitBeingResized != noSplit;
}
bool RenderFrameSet::isResizingColumn() const
{
return m_isResizing && m_cols.m_splitBeingResized != noSplit;
}
bool RenderFrameSet::canResizeRow(const IntPoint& p) const
{
int r = hitTestSplit(m_rows, p.y());
return r != noSplit && !m_rows.m_preventResize[r];
}
bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
{
int c = hitTestSplit(m_cols, p.x());
return c != noSplit && !m_cols.m_preventResize[c];
}
int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
{
if (needsLayout())
return 0;
int borderThickness = frameSetElement().border();
int size = axis.m_sizes.size();
if (!size)
return 0;
int position = 0;
for (int i = 0; i < split && i < size; ++i)
position += axis.m_sizes[i] + borderThickness;
return position - borderThickness;
}
int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
{
if (needsLayout())
return noSplit;
int borderThickness = frameSetElement().border();
if (borderThickness <= 0)
return noSplit;
size_t size = axis.m_sizes.size();
if (!size)
return noSplit;
int splitPosition = axis.m_sizes[0];
for (size_t i = 1; i < size; ++i) {
if (position >= splitPosition && position < splitPosition + borderThickness)
return i;
splitPosition += borderThickness + axis.m_sizes[i];
}
return noSplit;
}
bool RenderFrameSet::isChildAllowed(const RenderObject& child, const RenderStyle&) const
{
return child.isFrame() || child.isFrameSet();
}
CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
{
IntPoint roundedPoint = roundedIntPoint(point);
if (canResizeRow(roundedPoint)) {
cursor = rowResizeCursor();
return SetCursor;
}
if (canResizeColumn(roundedPoint)) {
cursor = columnResizeCursor();
return SetCursor;
}
return RenderBox::getCursor(point, cursor);
}
}