summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/RenderFrameSet.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/rendering/RenderFrameSet.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/rendering/RenderFrameSet.cpp')
-rw-r--r--Source/WebCore/rendering/RenderFrameSet.cpp890
1 files changed, 890 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderFrameSet.cpp b/Source/WebCore/rendering/RenderFrameSet.cpp
new file mode 100644
index 0000000..600d65e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderFrameSet.cpp
@@ -0,0 +1,890 @@
+/**
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Simon Hausmann <hausmann@kde.org>
+ * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderFrameSet.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 "RenderFrame.h"
+#include "RenderView.h"
+#include "Settings.h"
+
+namespace WebCore {
+
+RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
+ : RenderBox(frameSet)
+ , m_isResizing(false)
+ , m_isChildResizing(false)
+#ifdef ANDROID_FLATTEN_FRAMESET
+ , m_gridCalculated(false)
+#endif
+{
+ setInline(false);
+}
+
+RenderFrameSet::~RenderFrameSet()
+{
+}
+
+RenderFrameSet::GridAxis::GridAxis()
+ : m_splitBeingResized(noSplit)
+{
+}
+
+inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
+{
+ return static_cast<HTMLFrameSetElement*>(node());
+}
+
+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;
+
+ // FIXME: We should do something clever when borders from distinct framesets meet at a join.
+
+ // Fill first.
+ GraphicsContext* context = paintInfo.context;
+ ColorSpace colorSpace = style()->colorSpace();
+ context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
+
+ // Now stroke the edges but only if we have enough room to paint both edges with a little
+ // bit of the fill color showing through.
+ if (borderRect.width() >= 3) {
+ context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor(), colorSpace);
+ context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor(), colorSpace);
+ }
+}
+
+void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
+{
+ if (!paintInfo.rect.intersects(borderRect))
+ return;
+
+ // FIXME: We should do something clever when borders from distinct framesets meet at a join.
+
+ // Fill first.
+ GraphicsContext* context = paintInfo.context;
+ ColorSpace colorSpace = style()->colorSpace();
+ context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
+
+ // Now stroke the edges but only if we have enough room to paint both edges with a little
+ // bit of the fill color showing through.
+ if (borderRect.height() >= 3) {
+ context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace);
+ context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace);
+ }
+}
+
+void RenderFrameSet::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.phase != PaintPhaseForeground)
+ return;
+
+ RenderObject* child = firstChild();
+ if (!child)
+ return;
+
+ // Add in our offsets.
+ tx += x();
+ ty += y();
+
+ int rows = frameSet()->totalRows();
+ int cols = frameSet()->totalCols();
+ int borderThickness = frameSet()->border();
+
+ int yPos = 0;
+ for (int r = 0; r < rows; r++) {
+ int xPos = 0;
+ for (int c = 0; c < cols; c++) {
+ child->paint(paintInfo, tx, ty);
+ xPos += m_cols.m_sizes[c];
+ if (borderThickness && m_cols.m_allowBorder[c + 1]) {
+ paintColumnBorder(paintInfo, IntRect(tx + xPos, ty + 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, IntRect(tx, ty + yPos, width(), borderThickness));
+ yPos += borderThickness;
+ }
+ }
+}
+
+bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
+ int x, int y, int tx, int ty, HitTestAction action)
+{
+ if (action != HitTestForeground)
+ return false;
+
+ bool inside = RenderBox::nodeAtPoint(request, result, x, y, tx, ty, action)
+ || m_isResizing;
+
+ if (inside && frameSet()->noResize()
+ && !request.readOnly() && !result.innerNode()) {
+ result.setInnerNode(node());
+ result.setInnerNonSharedNode(node());
+ }
+
+ return inside || m_isChildResizing;
+}
+
+void RenderFrameSet::GridAxis::resize(int size)
+{
+ m_sizes.resize(size);
+ m_deltas.resize(size);
+ m_deltas.fill(0);
+
+ // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset
+ // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
+ // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info.
+ m_preventResize.resize(size + 1);
+ m_allowBorder.resize(size + 1);
+}
+
+void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
+{
+ availableLen = 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;
+
+ // First we need to investigate how many columns of each type we have and
+ // how much space these columns are going to require.
+ for (int i = 0; i < gridLen; ++i) {
+ // Count the total length of all of the fixed columns/rows -> totalFixed
+ // Count the number of columns/rows which are fixed -> countFixed
+ if (grid[i].isFixed()) {
+ gridLayout[i] = max(grid[i].value(), 0);
+ totalFixed += gridLayout[i];
+ countFixed++;
+ }
+
+ // Count the total percentage of all of the percentage columns/rows -> totalPercent
+ // Count the number of columns/rows which are percentages -> countPercent
+ if (grid[i].isPercent()) {
+ gridLayout[i] = max(grid[i].calcValue(availableLen), 0);
+ totalPercent += gridLayout[i];
+ countPercent++;
+ }
+
+ // Count the total relative of all the relative columns/rows -> totalRelative
+ // Count the number of columns/rows which are relative -> countRelative
+ if (grid[i].isRelative()) {
+ totalRelative += max(grid[i].value(), 1);
+ countRelative++;
+ }
+ }
+
+ int remainingLen = availableLen;
+
+ // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
+ // columns/rows we need to proportionally adjust their size.
+ 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;
+
+ // Percentage columns/rows are our second priority. Divide the remaining space proportionally
+ // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
+ // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
+ // and the available space is 300px, each column will become 100px in width.
+ 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;
+
+ // Relative columns/rows are our last priority. Divide the remaining space proportionally
+ // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
+ if (countRelative) {
+ int lastRelative = 0;
+ int remainingRelative = remainingLen;
+
+ for (int i = 0; i < gridLen; ++i) {
+ if (grid[i].isRelative()) {
+ gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative;
+ remainingLen -= gridLayout[i];
+ lastRelative = i;
+ }
+ }
+
+ // If we could not evenly distribute the available space of all of the relative
+ // columns/rows, the remainder will be added to the last column/row.
+ // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
+ // be 1px and will be added to the last column: 33px, 33px, 34px.
+ if (remainingLen) {
+ gridLayout[lastRelative] += remainingLen;
+ remainingLen = 0;
+ }
+ }
+
+ // If we still have some left over space we need to divide it over the already existing
+ // columns/rows
+ if (remainingLen) {
+ // Our first priority is to spread if over the percentage columns. The remaining
+ // space is spread evenly, for example: if we have a space of 100px, the columns
+ // definition of 25%,25% used to result in two columns of 25px. After this the
+ // columns will each be 50px in width.
+ 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) {
+ // Our last priority is to spread the remaining space over the fixed columns.
+ // For example if we have 100px of space and two column of each 40px, both
+ // columns will become exactly 50px.
+ 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 we still have some left over space we probably ended up with a remainder of
+ // a division. We cannot spread it evenly anymore. If we have any percentage
+ // columns/rows simply spread the remainder equally over all available percentage columns,
+ // regardless of their size.
+ 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;
+ }
+ }
+ }
+
+ // If we don't have any percentage columns/rows we only have fixed columns. Spread
+ // the remainder equally over all fixed columns/rows.
+ 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;
+ }
+ }
+ }
+
+ // Still some left over. Add it to the last column, because it is impossible
+ // spread it evenly or equally.
+ if (remainingLen)
+ gridLayout[gridLen - 1] += remainingLen;
+
+ // now we have the final layout, distribute the delta over it
+ 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 the deltas broke something, undo them
+ if (!worked) {
+ for (int i = 0; i < gridLen; ++i)
+ gridLayout[i] -= gridDelta[i];
+ axis.m_deltas.fill(0);
+ }
+}
+
+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(frameSet()->noResize());
+ m_rows.m_allowBorder.fill(false);
+ m_cols.m_preventResize.fill(frameSet()->noResize());
+ m_cols.m_allowBorder.fill(false);
+
+ RenderObject* child = firstChild();
+ if (!child)
+ return;
+
+ int rows = frameSet()->totalRows();
+ int cols = frameSet()->totalCols();
+ for (int r = 0; r < rows; ++r) {
+ for (int 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(frameSet()->noResize(), true);
+
+ int rows = frameSet()->totalRows();
+ int cols = frameSet()->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()
+{
+ ASSERT(needsLayout());
+
+ bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout();
+ IntRect oldBounds;
+ if (doFullRepaint)
+ oldBounds = absoluteClippedOverflowRect();
+
+ if (!parent()->isFrameSet() && !document()->printing()) {
+#ifdef ANDROID_FLATTEN_FRAMESET
+ // Force a grid recalc.
+ m_gridCalculated = false;
+#endif
+ setWidth(view()->viewWidth());
+ setHeight(view()->viewHeight());
+ }
+
+ size_t cols = frameSet()->totalCols();
+ size_t rows = frameSet()->totalRows();
+
+ if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
+ m_rows.resize(rows);
+ m_cols.resize(cols);
+#ifdef ANDROID_FLATTEN_FRAMESET
+ m_gridCalculated = false;
+#endif
+ }
+
+#ifdef ANDROID_FLATTEN_FRAMESET
+ if (!m_gridCalculated) {
+ m_gridCalculated = true;
+ // Make all the child framesets recalculate their grid.
+ RenderObject* child = firstChild();
+ for (; child; child = child->nextSibling()) {
+ if (child->isFrameSet())
+ static_cast<RenderFrameSet*>(child)->setGridNeedsLayout();
+ }
+#endif
+ int borderThickness = frameSet()->border();
+ layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness);
+ layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness);
+#ifdef ANDROID_FLATTEN_FRAMESET
+ }
+#endif
+
+ if (flattenFrameSet())
+ positionFramesWithFlattening();
+ else
+ positionFrames();
+
+ RenderBox::layout();
+
+ computeEdgeInfo();
+
+ if (doFullRepaint) {
+ view()->repaintViewRectangle(oldBounds);
+ IntRect newBounds = absoluteClippedOverflowRect();
+ if (newBounds != oldBounds)
+ view()->repaintViewRectangle(newBounds);
+ }
+
+ setNeedsLayout(false);
+}
+
+void RenderFrameSet::positionFrames()
+{
+ RenderBox* child = firstChildBox();
+ if (!child)
+ return;
+
+ int rows = frameSet()->totalRows();
+ int cols = frameSet()->totalCols();
+
+ int yPos = 0;
+ int borderThickness = frameSet()->border();
+#ifdef ANDROID_FLATTEN_FRAMESET
+ // Keep track of the maximum width of a row which will become the maximum width of the frameset.
+ int maxWidth = 0;
+ const Length* rowLengths = frameSet()->rowLengths();
+ const Length* colLengths = frameSet()->colLengths();
+
+ for (int r = 0; r < rows && child; r++) {
+ int xPos = 0;
+ int height = m_rows.m_sizes[r];
+ int rowHeight = -1;
+ if (rowLengths) {
+ Length l = rowLengths[r];
+ if (l.isFixed())
+ rowHeight = l.value();
+ }
+ for (int c = 0; c < cols && child; c++) {
+ child->setX(xPos);
+ child->setY(yPos);
+ child->setWidth(m_cols.m_sizes[c]);
+ child->setHeight(height);
+ int colWidth = -1;
+ if (colLengths) {
+ Length l = colLengths[c];
+ if (l.isFixed())
+ colWidth = l.value();
+ }
+ if (colWidth && rowHeight) {
+ child->setNeedsLayout(true);
+ child->layout();
+ } else {
+ child->layoutIfNeeded();
+ }
+
+ ASSERT(child->width() >= m_cols.m_sizes[c]);
+ m_cols.m_sizes[c] = child->width();
+
+ height = max(child->height(), height);
+ xPos += child->width() + borderThickness;
+ child = (RenderBox*)child->nextSibling();
+ }
+ ASSERT(height >= m_rows.m_sizes[r]);
+ m_rows.m_sizes[r] = height;
+ maxWidth = max(xPos, maxWidth);
+ yPos += height + borderThickness;
+ }
+
+ // Compute a new width and height according to the positioning of each expanded child frame.
+ // Note: we subtract borderThickness because we only count borders between frames.
+ int newWidth = maxWidth - borderThickness;
+ int newHeight = yPos - borderThickness;
+
+ // Distribute the extra width and height evenly across the grid.
+ int dWidth = (width() - newWidth) / cols;
+ int dHeight = (height() - newHeight) / rows;
+ if (dWidth > 0) {
+ int availableWidth = width() - (cols - 1) * borderThickness;
+ for (int c = 0; c < cols; c++)
+ availableWidth -= m_cols.m_sizes[c] += dWidth;
+ // If the extra width did not distribute evenly, add the remainder to
+ // the last column.
+ if (availableWidth)
+ m_cols.m_sizes[cols - 1] += availableWidth;
+ }
+ if (dHeight > 0) {
+ int availableHeight = height() - (rows - 1) * borderThickness;
+ for (int r = 0; r < rows; r++)
+ availableHeight -= m_rows.m_sizes[r] += dHeight;
+ // If the extra height did not distribute evenly, add the remainder to
+ // the last row.
+ if (availableHeight)
+ m_rows.m_sizes[rows - 1] += availableHeight;
+ }
+ // Ensure the rows and columns are filled by falling through to the normal
+ // layout
+ setHeight(max(height(), newHeight));
+ setWidth(max(width(), newWidth));
+ child = (RenderBox*)firstChild();
+ yPos = 0;
+#endif // ANDROID_FLATTEN_FRAMESET
+
+ 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(xPos, yPos);
+ int width = m_cols.m_sizes[c];
+
+ // has to be resized and itself resize its contents
+ if (width != child->width() || height != child->height()) {
+ child->setWidth(width);
+ child->setHeight(height);
+ child->setNeedsLayout(true);
+ child->layout();
+ }
+
+ xPos += width + borderThickness;
+
+ child = child->nextSiblingBox();
+ if (!child)
+ return;
+ }
+ yPos += height + borderThickness;
+ }
+
+ // all the remaining frames are hidden to avoid ugly spurious unflowed frames
+ for (; child; child = child->nextSiblingBox()) {
+ child->setWidth(0);
+ child->setHeight(0);
+ child->setNeedsLayout(false);
+ }
+}
+
+void RenderFrameSet::positionFramesWithFlattening()
+{
+ RenderBox* child = firstChildBox();
+ if (!child)
+ return;
+
+ int rows = frameSet()->totalRows();
+ int cols = frameSet()->totalCols();
+
+ int borderThickness = frameSet()->border();
+ bool repaintNeeded = false;
+
+ // calculate frameset height based on actual content height to eliminate scrolling
+ 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 = child->frameRect();
+
+ int width = m_cols.m_sizes[c];
+
+ bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed();
+ bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed();
+
+ // has to be resized and itself resize its contents
+ if (!fixedWidth)
+ child->setWidth(width ? width + extra / (cols - c) : 0);
+ else
+ child->setWidth(width);
+ child->setHeight(height);
+
+ child->setNeedsLayout(true);
+
+ 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;
+
+ // difference between calculated frame width and the width it actually decides to have
+ 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++) {
+ // ensure the rows and columns are filled
+ IntRect oldRect = child->frameRect();
+
+ child->setLocation(xPos, yPos);
+ child->setHeight(m_rows.m_sizes[r]);
+ child->setWidth(m_cols.m_sizes[c]);
+
+ if (child->frameRect() != oldRect) {
+ repaintNeeded = true;
+
+ // update to final size
+ child->setNeedsLayout(true);
+ 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();
+
+ // all the remaining frames are hidden to avoid ugly spurious unflowed frames
+ for (; child; child = child->nextSiblingBox()) {
+ child->setWidth(0);
+ child->setHeight(0);
+ child->setNeedsLayout(false);
+ }
+}
+
+bool RenderFrameSet::flattenFrameSet() const
+{
+ return frame() && frame()->settings()->frameFlatteningEnabled();
+}
+
+void RenderFrameSet::startResizing(GridAxis& axis, int position)
+{
+ int split = hitTestSplit(axis, position);
+ if (split == noSplit || !axis.m_allowBorder[split] || 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 == 0)
+ return;
+ axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
+ axis.m_deltas[axis.m_splitBeingResized] -= delta;
+ setNeedsLayout(true);
+}
+
+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 pos = localToAbsolute();
+ startResizing(m_cols, evt->absoluteLocation().x() - pos.x());
+ startResizing(m_rows, evt->absoluteLocation().y() - pos.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 pos = localToAbsolute();
+ continueResizing(m_cols, evt->absoluteLocation().x() - pos.x());
+ continueResizing(m_rows, evt->absoluteLocation().y() - pos.y());
+ if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) {
+ setIsResizing(false);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void RenderFrameSet::setIsResizing(bool isResizing)
+{
+ m_isResizing = isResizing;
+ for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+ if (ancestor->isFrameSet())
+ toRenderFrameSet(ancestor)->m_isChildResizing = isResizing;
+ }
+ if (Frame* frame = this->frame())
+ frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0);
+}
+
+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_allowBorder[r] && !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_allowBorder[c] && !m_cols.m_preventResize[c];
+}
+
+int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
+{
+ if (needsLayout())
+ return 0;
+
+ int borderThickness = frameSet()->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 = frameSet()->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(RenderObject* child, RenderStyle*) const
+{
+ return child->isFrame() || child->isFrameSet();
+}
+
+} // namespace WebCore