Home | History | Annotate | Download | only in rendering
      1 /**
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2000 Simon Hausmann <hausmann (at) kde.org>
      4  *           (C) 2000 Stefan Schimanski (1Stein (at) gmx.de)
      5  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "RenderFrameSet.h"
     26 
     27 #include "Document.h"
     28 #include "EventHandler.h"
     29 #include "EventNames.h"
     30 #include "Frame.h"
     31 #include "FrameView.h"
     32 #include "GraphicsContext.h"
     33 #include "HTMLFrameSetElement.h"
     34 #include "HitTestRequest.h"
     35 #include "HitTestResult.h"
     36 #include "MouseEvent.h"
     37 #include "RenderFrame.h"
     38 #include "RenderView.h"
     39 #include "Settings.h"
     40 
     41 namespace WebCore {
     42 
     43 RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
     44     : RenderBox(frameSet)
     45     , m_isResizing(false)
     46     , m_isChildResizing(false)
     47 #ifdef FLATTEN_FRAMESET
     48     , m_gridCalculated(false)
     49 #endif
     50 {
     51     setInline(false);
     52 }
     53 
     54 RenderFrameSet::~RenderFrameSet()
     55 {
     56 }
     57 
     58 RenderFrameSet::GridAxis::GridAxis()
     59     : m_splitBeingResized(noSplit)
     60 {
     61 }
     62 
     63 inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
     64 {
     65     return static_cast<HTMLFrameSetElement*>(node());
     66 }
     67 
     68 static Color borderStartEdgeColor()
     69 {
     70     return Color(170, 170, 170);
     71 }
     72 
     73 static Color borderEndEdgeColor()
     74 {
     75     return Color::black;
     76 }
     77 
     78 static Color borderFillColor()
     79 {
     80     return Color(208, 208, 208);
     81 }
     82 
     83 void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
     84 {
     85     if (!paintInfo.rect.intersects(borderRect))
     86         return;
     87 
     88     // FIXME: We should do something clever when borders from distinct framesets meet at a join.
     89 
     90     // Fill first.
     91     GraphicsContext* context = paintInfo.context;
     92     ColorSpace colorSpace = style()->colorSpace();
     93     context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor(), colorSpace);
     94 
     95     // Now stroke the edges but only if we have enough room to paint both edges with a little
     96     // bit of the fill color showing through.
     97     if (borderRect.width() >= 3) {
     98         context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor(), colorSpace);
     99         context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor(), colorSpace);
    100     }
    101 }
    102 
    103 void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
    104 {
    105     if (!paintInfo.rect.intersects(borderRect))
    106         return;
    107 
    108     // FIXME: We should do something clever when borders from distinct framesets meet at a join.
    109 
    110     // Fill first.
    111     GraphicsContext* context = paintInfo.context;
    112     ColorSpace colorSpace = style()->colorSpace();
    113     context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor(), colorSpace);
    114 
    115     // Now stroke the edges but only if we have enough room to paint both edges with a little
    116     // bit of the fill color showing through.
    117     if (borderRect.height() >= 3) {
    118         context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace);
    119         context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace);
    120     }
    121 }
    122 
    123 void RenderFrameSet::paint(PaintInfo& paintInfo, int tx, int ty)
    124 {
    125     if (paintInfo.phase != PaintPhaseForeground)
    126         return;
    127 
    128     RenderObject* child = firstChild();
    129     if (!child)
    130         return;
    131 
    132     // Add in our offsets.
    133     tx += x();
    134     ty += y();
    135 
    136     int rows = frameSet()->totalRows();
    137     int cols = frameSet()->totalCols();
    138     int borderThickness = frameSet()->border();
    139 
    140     int yPos = 0;
    141     for (int r = 0; r < rows; r++) {
    142         int xPos = 0;
    143         for (int c = 0; c < cols; c++) {
    144             child->paint(paintInfo, tx, ty);
    145             xPos += m_cols.m_sizes[c];
    146             if (borderThickness && m_cols.m_allowBorder[c + 1]) {
    147                 paintColumnBorder(paintInfo, IntRect(tx + xPos, ty + yPos, borderThickness, height()));
    148                 xPos += borderThickness;
    149             }
    150             child = child->nextSibling();
    151             if (!child)
    152                 return;
    153         }
    154         yPos += m_rows.m_sizes[r];
    155         if (borderThickness && m_rows.m_allowBorder[r + 1]) {
    156             paintRowBorder(paintInfo, IntRect(tx, ty + yPos, width(), borderThickness));
    157             yPos += borderThickness;
    158         }
    159     }
    160 }
    161 
    162 bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
    163     int x, int y, int tx, int ty, HitTestAction action)
    164 {
    165     if (action != HitTestForeground)
    166         return false;
    167 
    168     bool inside = RenderBox::nodeAtPoint(request, result, x, y, tx, ty, action)
    169         || m_isResizing;
    170 
    171     if (inside && frameSet()->noResize()
    172             && !request.readOnly() && !result.innerNode()) {
    173         result.setInnerNode(node());
    174         result.setInnerNonSharedNode(node());
    175     }
    176 
    177     return inside || m_isChildResizing;
    178 }
    179 
    180 void RenderFrameSet::GridAxis::resize(int size)
    181 {
    182     m_sizes.resize(size);
    183     m_deltas.resize(size);
    184     m_deltas.fill(0);
    185 
    186     // To track edges for resizability and borders, we need to be (size + 1).  This is because a parent frameset
    187     // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
    188     // what to do.  We are capable of tainting that parent frameset's borders, so we have to cache this info.
    189     m_preventResize.resize(size + 1);
    190     m_allowBorder.resize(size + 1);
    191 }
    192 
    193 void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
    194 {
    195     availableLen = max(availableLen, 0);
    196 
    197     int* gridLayout = axis.m_sizes.data();
    198 
    199     if (!grid) {
    200         gridLayout[0] = availableLen;
    201         return;
    202     }
    203 
    204     int gridLen = axis.m_sizes.size();
    205     ASSERT(gridLen);
    206 
    207     int totalRelative = 0;
    208     int totalFixed = 0;
    209     int totalPercent = 0;
    210     int countRelative = 0;
    211     int countFixed = 0;
    212     int countPercent = 0;
    213 
    214     // First we need to investigate how many columns of each type we have and
    215     // how much space these columns are going to require.
    216     for (int i = 0; i < gridLen; ++i) {
    217         // Count the total length of all of the fixed columns/rows -> totalFixed
    218         // Count the number of columns/rows which are fixed -> countFixed
    219         if (grid[i].isFixed()) {
    220             gridLayout[i] = max(grid[i].value(), 0);
    221             totalFixed += gridLayout[i];
    222             countFixed++;
    223         }
    224 
    225         // Count the total percentage of all of the percentage columns/rows -> totalPercent
    226         // Count the number of columns/rows which are percentages -> countPercent
    227         if (grid[i].isPercent()) {
    228             gridLayout[i] = max(grid[i].calcValue(availableLen), 0);
    229             totalPercent += gridLayout[i];
    230             countPercent++;
    231         }
    232 
    233         // Count the total relative of all the relative columns/rows -> totalRelative
    234         // Count the number of columns/rows which are relative -> countRelative
    235         if (grid[i].isRelative()) {
    236             totalRelative += max(grid[i].value(), 1);
    237             countRelative++;
    238         }
    239     }
    240 
    241     int remainingLen = availableLen;
    242 
    243     // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
    244     // columns/rows we need to proportionally adjust their size.
    245     if (totalFixed > remainingLen) {
    246         int remainingFixed = remainingLen;
    247 
    248         for (int i = 0; i < gridLen; ++i) {
    249             if (grid[i].isFixed()) {
    250                 gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
    251                 remainingLen -= gridLayout[i];
    252             }
    253         }
    254     } else
    255         remainingLen -= totalFixed;
    256 
    257     // Percentage columns/rows are our second priority. Divide the remaining space proportionally
    258     // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
    259     // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
    260     // and the available space is 300px, each column will become 100px in width.
    261     if (totalPercent > remainingLen) {
    262         int remainingPercent = remainingLen;
    263 
    264         for (int i = 0; i < gridLen; ++i) {
    265             if (grid[i].isPercent()) {
    266                 gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
    267                 remainingLen -= gridLayout[i];
    268             }
    269         }
    270     } else
    271         remainingLen -= totalPercent;
    272 
    273     // Relative columns/rows are our last priority. Divide the remaining space proportionally
    274     // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
    275     if (countRelative) {
    276         int lastRelative = 0;
    277         int remainingRelative = remainingLen;
    278 
    279         for (int i = 0; i < gridLen; ++i) {
    280             if (grid[i].isRelative()) {
    281                 gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative;
    282                 remainingLen -= gridLayout[i];
    283                 lastRelative = i;
    284             }
    285         }
    286 
    287         // If we could not evenly distribute the available space of all of the relative
    288         // columns/rows, the remainder will be added to the last column/row.
    289         // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
    290         // be 1px and will be added to the last column: 33px, 33px, 34px.
    291         if (remainingLen) {
    292             gridLayout[lastRelative] += remainingLen;
    293             remainingLen = 0;
    294         }
    295     }
    296 
    297     // If we still have some left over space we need to divide it over the already existing
    298     // columns/rows
    299     if (remainingLen) {
    300         // Our first priority is to spread if over the percentage columns. The remaining
    301         // space is spread evenly, for example: if we have a space of 100px, the columns
    302         // definition of 25%,25% used to result in two columns of 25px. After this the
    303         // columns will each be 50px in width.
    304         if (countPercent && totalPercent) {
    305             int remainingPercent = remainingLen;
    306             int changePercent = 0;
    307 
    308             for (int i = 0; i < gridLen; ++i) {
    309                 if (grid[i].isPercent()) {
    310                     changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
    311                     gridLayout[i] += changePercent;
    312                     remainingLen -= changePercent;
    313                 }
    314             }
    315         } else if (totalFixed) {
    316             // Our last priority is to spread the remaining space over the fixed columns.
    317             // For example if we have 100px of space and two column of each 40px, both
    318             // columns will become exactly 50px.
    319             int remainingFixed = remainingLen;
    320             int changeFixed = 0;
    321 
    322             for (int i = 0; i < gridLen; ++i) {
    323                 if (grid[i].isFixed()) {
    324                     changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
    325                     gridLayout[i] += changeFixed;
    326                     remainingLen -= changeFixed;
    327                 }
    328             }
    329         }
    330     }
    331 
    332     // If we still have some left over space we probably ended up with a remainder of
    333     // a division. We cannot spread it evenly anymore. If we have any percentage
    334     // columns/rows simply spread the remainder equally over all available percentage columns,
    335     // regardless of their size.
    336     if (remainingLen && countPercent) {
    337         int remainingPercent = remainingLen;
    338         int changePercent = 0;
    339 
    340         for (int i = 0; i < gridLen; ++i) {
    341             if (grid[i].isPercent()) {
    342                 changePercent = remainingPercent / countPercent;
    343                 gridLayout[i] += changePercent;
    344                 remainingLen -= changePercent;
    345             }
    346         }
    347     }
    348 
    349     // If we don't have any percentage columns/rows we only have fixed columns. Spread
    350     // the remainder equally over all fixed columns/rows.
    351     else if (remainingLen && countFixed) {
    352         int remainingFixed = remainingLen;
    353         int changeFixed = 0;
    354 
    355         for (int i = 0; i < gridLen; ++i) {
    356             if (grid[i].isFixed()) {
    357                 changeFixed = remainingFixed / countFixed;
    358                 gridLayout[i] += changeFixed;
    359                 remainingLen -= changeFixed;
    360             }
    361         }
    362     }
    363 
    364     // Still some left over. Add it to the last column, because it is impossible
    365     // spread it evenly or equally.
    366     if (remainingLen)
    367         gridLayout[gridLen - 1] += remainingLen;
    368 
    369     // now we have the final layout, distribute the delta over it
    370     bool worked = true;
    371     int* gridDelta = axis.m_deltas.data();
    372     for (int i = 0; i < gridLen; ++i) {
    373         if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
    374             worked = false;
    375         gridLayout[i] += gridDelta[i];
    376     }
    377     // if the deltas broke something, undo them
    378     if (!worked) {
    379         for (int i = 0; i < gridLen; ++i)
    380             gridLayout[i] -= gridDelta[i];
    381         axis.m_deltas.fill(0);
    382     }
    383 }
    384 
    385 void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
    386 {
    387     if (edgeInfo.allowBorder(LeftFrameEdge))
    388         m_cols.m_allowBorder[c] = true;
    389     if (edgeInfo.allowBorder(RightFrameEdge))
    390         m_cols.m_allowBorder[c + 1] = true;
    391     if (edgeInfo.preventResize(LeftFrameEdge))
    392         m_cols.m_preventResize[c] = true;
    393     if (edgeInfo.preventResize(RightFrameEdge))
    394         m_cols.m_preventResize[c + 1] = true;
    395 
    396     if (edgeInfo.allowBorder(TopFrameEdge))
    397         m_rows.m_allowBorder[r] = true;
    398     if (edgeInfo.allowBorder(BottomFrameEdge))
    399         m_rows.m_allowBorder[r + 1] = true;
    400     if (edgeInfo.preventResize(TopFrameEdge))
    401         m_rows.m_preventResize[r] = true;
    402     if (edgeInfo.preventResize(BottomFrameEdge))
    403         m_rows.m_preventResize[r + 1] = true;
    404 }
    405 
    406 void RenderFrameSet::computeEdgeInfo()
    407 {
    408     m_rows.m_preventResize.fill(frameSet()->noResize());
    409     m_rows.m_allowBorder.fill(false);
    410     m_cols.m_preventResize.fill(frameSet()->noResize());
    411     m_cols.m_allowBorder.fill(false);
    412 
    413     RenderObject* child = firstChild();
    414     if (!child)
    415         return;
    416 
    417     int rows = frameSet()->totalRows();
    418     int cols = frameSet()->totalCols();
    419     for (int r = 0; r < rows; ++r) {
    420         for (int c = 0; c < cols; ++c) {
    421             FrameEdgeInfo edgeInfo;
    422             if (child->isFrameSet())
    423                 edgeInfo = toRenderFrameSet(child)->edgeInfo();
    424             else
    425                 edgeInfo = toRenderFrame(child)->edgeInfo();
    426             fillFromEdgeInfo(edgeInfo, r, c);
    427             child = child->nextSibling();
    428             if (!child)
    429                 return;
    430         }
    431     }
    432 }
    433 
    434 FrameEdgeInfo RenderFrameSet::edgeInfo() const
    435 {
    436     FrameEdgeInfo result(frameSet()->noResize(), true);
    437 
    438     int rows = frameSet()->totalRows();
    439     int cols = frameSet()->totalCols();
    440     if (rows && cols) {
    441         result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
    442         result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
    443         result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
    444         result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
    445         result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
    446         result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
    447         result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
    448         result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
    449     }
    450 
    451     return result;
    452 }
    453 
    454 void RenderFrameSet::layout()
    455 {
    456     ASSERT(needsLayout());
    457 
    458     bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout();
    459     IntRect oldBounds;
    460     if (doFullRepaint)
    461         oldBounds = absoluteClippedOverflowRect();
    462 
    463     if (!parent()->isFrameSet() && !document()->printing()) {
    464 #ifdef FLATTEN_FRAMESET
    465         // Force a grid recalc.
    466         m_gridCalculated = false;
    467 #endif
    468         setWidth(view()->viewWidth());
    469         setHeight(view()->viewHeight());
    470     }
    471 
    472     size_t cols = frameSet()->totalCols();
    473     size_t rows = frameSet()->totalRows();
    474 
    475     if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
    476         m_rows.resize(rows);
    477         m_cols.resize(cols);
    478 #ifdef FLATTEN_FRAMESET
    479         m_gridCalculated = false;
    480 #endif
    481     }
    482 
    483 #ifdef FLATTEN_FRAMESET
    484     if (!m_gridCalculated) {
    485         m_gridCalculated = true;
    486         // Make all the child framesets recalculate their grid.
    487         RenderObject* child = firstChild();
    488         for (; child; child = child->nextSibling()) {
    489             if (child->isFrameSet())
    490                 static_cast<RenderFrameSet*>(child)->setGridNeedsLayout();
    491         }
    492 #endif
    493     int borderThickness = frameSet()->border();
    494     layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness);
    495     layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness);
    496 #ifdef FLATTEN_FRAMESET
    497     }
    498 #endif
    499 
    500     if (flattenFrameSet())
    501         positionFramesWithFlattening();
    502     else
    503         positionFrames();
    504 
    505     RenderBox::layout();
    506 
    507     computeEdgeInfo();
    508 
    509     if (doFullRepaint) {
    510         view()->repaintViewRectangle(oldBounds);
    511         IntRect newBounds = absoluteClippedOverflowRect();
    512         if (newBounds != oldBounds)
    513             view()->repaintViewRectangle(newBounds);
    514     }
    515 
    516     setNeedsLayout(false);
    517 }
    518 
    519 void RenderFrameSet::positionFrames()
    520 {
    521     RenderBox* child = firstChildBox();
    522     if (!child)
    523         return;
    524 
    525     int rows = frameSet()->totalRows();
    526     int cols = frameSet()->totalCols();
    527 
    528     int yPos = 0;
    529     int borderThickness = frameSet()->border();
    530 #ifdef FLATTEN_FRAMESET
    531     // Keep track of the maximum width of a row which will become the maximum width of the frameset.
    532     int maxWidth = 0;
    533     const Length* rowLengths = frameSet()->rowLengths();
    534     const Length* colLengths = frameSet()->colLengths();
    535 
    536     for (int r = 0; r < rows && child; r++) {
    537         int xPos = 0;
    538         int height = m_rows.m_sizes[r];
    539         int rowHeight = -1;
    540         if (rowLengths) {
    541             Length l = rowLengths[r];
    542             if (l.isFixed())
    543                 rowHeight = l.value();
    544         }
    545         for (int c = 0; c < cols && child; c++) {
    546             child->setX(xPos);
    547             child->setY(yPos);
    548             child->setWidth(m_cols.m_sizes[c]);
    549             child->setHeight(height);
    550             int colWidth = -1;
    551             if (colLengths) {
    552                 Length l = colLengths[c];
    553                 if (l.isFixed())
    554                     colWidth = l.value();
    555             }
    556             if (colWidth && rowHeight) {
    557                 child->setNeedsLayout(true);
    558                 child->layout();
    559             } else {
    560                 child->layoutIfNeeded();
    561             }
    562 
    563             ASSERT(child->width() >= m_cols.m_sizes[c]);
    564             m_cols.m_sizes[c] = child->width();
    565 
    566             height = max(child->height(), height);
    567             xPos += child->width() + borderThickness;
    568             child = (RenderBox*)child->nextSibling();
    569         }
    570         ASSERT(height >= m_rows.m_sizes[r]);
    571         m_rows.m_sizes[r] = height;
    572         maxWidth = max(xPos, maxWidth);
    573         yPos += height + borderThickness;
    574     }
    575 
    576     // Compute a new width and height according to the positioning of each expanded child frame.
    577     // Note: we subtract borderThickness because we only count borders between frames.
    578     int newWidth = maxWidth - borderThickness;
    579     int newHeight = yPos - borderThickness;
    580 
    581     // Distribute the extra width and height evenly across the grid.
    582     int dWidth = (width() - newWidth) / cols;
    583     int dHeight = (height() - newHeight) / rows;
    584     if (dWidth > 0) {
    585         int availableWidth = width() - (cols - 1) * borderThickness;
    586         for (int c = 0; c < cols; c++)
    587             availableWidth -= m_cols.m_sizes[c] += dWidth;
    588         // If the extra width did not distribute evenly, add the remainder to
    589         // the last column.
    590         if (availableWidth)
    591             m_cols.m_sizes[cols - 1] += availableWidth;
    592     }
    593     if (dHeight > 0) {
    594         int availableHeight = height() - (rows - 1) * borderThickness;
    595         for (int r = 0; r < rows; r++)
    596             availableHeight -= m_rows.m_sizes[r] += dHeight;
    597         // If the extra height did not distribute evenly, add the remainder to
    598         // the last row.
    599         if (availableHeight)
    600             m_rows.m_sizes[rows - 1] += availableHeight;
    601     }
    602     // Ensure the rows and columns are filled by falling through to the normal
    603     // layout
    604     setHeight(max(height(), newHeight));
    605     setWidth(max(width(), newWidth));
    606     child = (RenderBox*)firstChild();
    607     yPos = 0;
    608 #endif // FLATTEN_FRAMESET
    609 
    610     for (int r = 0; r < rows; r++) {
    611         int xPos = 0;
    612         int height = m_rows.m_sizes[r];
    613         for (int c = 0; c < cols; c++) {
    614             child->setLocation(xPos, yPos);
    615             int width = m_cols.m_sizes[c];
    616 
    617             // has to be resized and itself resize its contents
    618             if (width != child->width() || height != child->height()) {
    619                 child->setWidth(width);
    620                 child->setHeight(height);
    621                 child->setNeedsLayout(true);
    622                 child->layout();
    623             }
    624 
    625             xPos += width + borderThickness;
    626 
    627             child = child->nextSiblingBox();
    628             if (!child)
    629                 return;
    630         }
    631         yPos += height + borderThickness;
    632     }
    633 
    634     // all the remaining frames are hidden to avoid ugly spurious unflowed frames
    635     for (; child; child = child->nextSiblingBox()) {
    636         child->setWidth(0);
    637         child->setHeight(0);
    638         child->setNeedsLayout(false);
    639     }
    640 }
    641 
    642 void RenderFrameSet::positionFramesWithFlattening()
    643 {
    644     RenderBox* child = firstChildBox();
    645     if (!child)
    646         return;
    647 
    648     int rows = frameSet()->totalRows();
    649     int cols = frameSet()->totalCols();
    650 
    651     int borderThickness = frameSet()->border();
    652     bool repaintNeeded = false;
    653 
    654     // calculate frameset height based on actual content height to eliminate scrolling
    655     bool out = false;
    656     for (int r = 0; r < rows && !out; r++) {
    657         int extra = 0;
    658         int height = m_rows.m_sizes[r];
    659 
    660         for (int c = 0; c < cols; c++) {
    661             IntRect oldFrameRect = child->frameRect();
    662 
    663             int width = m_cols.m_sizes[c];
    664 
    665             bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed();
    666             bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed();
    667 
    668             // has to be resized and itself resize its contents
    669             if (!fixedWidth)
    670                 child->setWidth(width ? width + extra / (cols - c) : 0);
    671             else
    672                 child->setWidth(width);
    673             child->setHeight(height);
    674 
    675             child->setNeedsLayout(true);
    676 
    677             if (child->isFrameSet())
    678                 toRenderFrameSet(child)->layout();
    679             else
    680                 toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight);
    681 
    682             if (child->height() > m_rows.m_sizes[r])
    683                 m_rows.m_sizes[r] = child->height();
    684             if (child->width() > m_cols.m_sizes[c])
    685                 m_cols.m_sizes[c] = child->width();
    686 
    687             if (child->frameRect() != oldFrameRect)
    688                 repaintNeeded = true;
    689 
    690             // difference between calculated frame width and the width it actually decides to have
    691             extra += width - m_cols.m_sizes[c];
    692 
    693             child = child->nextSiblingBox();
    694             if (!child) {
    695                 out = true;
    696                 break;
    697             }
    698         }
    699     }
    700 
    701     int xPos = 0;
    702     int yPos = 0;
    703     out = false;
    704     child = firstChildBox();
    705     for (int r = 0; r < rows && !out; r++) {
    706         xPos = 0;
    707         for (int c = 0; c < cols; c++) {
    708             // ensure the rows and columns are filled
    709             IntRect oldRect = child->frameRect();
    710 
    711             child->setLocation(xPos, yPos);
    712             child->setHeight(m_rows.m_sizes[r]);
    713             child->setWidth(m_cols.m_sizes[c]);
    714 
    715             if (child->frameRect() != oldRect) {
    716                 repaintNeeded = true;
    717 
    718                 // update to final size
    719                 child->setNeedsLayout(true);
    720                 if (child->isFrameSet())
    721                     toRenderFrameSet(child)->layout();
    722                 else
    723                     toRenderFrame(child)->layoutWithFlattening(true, true);
    724             }
    725 
    726             xPos += m_cols.m_sizes[c] + borderThickness;
    727             child = child->nextSiblingBox();
    728             if (!child) {
    729                 out = true;
    730                 break;
    731             }
    732         }
    733         yPos += m_rows.m_sizes[r] + borderThickness;
    734     }
    735 
    736     setWidth(xPos - borderThickness);
    737     setHeight(yPos - borderThickness);
    738 
    739     if (repaintNeeded)
    740         repaint();
    741 
    742     // all the remaining frames are hidden to avoid ugly spurious unflowed frames
    743     for (; child; child = child->nextSiblingBox()) {
    744         child->setWidth(0);
    745         child->setHeight(0);
    746         child->setNeedsLayout(false);
    747     }
    748 }
    749 
    750 bool RenderFrameSet::flattenFrameSet() const
    751 {
    752     return document()->frame() && document()->frame()->settings()->frameSetFlatteningEnabled();
    753 }
    754 
    755 void RenderFrameSet::startResizing(GridAxis& axis, int position)
    756 {
    757     int split = hitTestSplit(axis, position);
    758     if (split == noSplit || !axis.m_allowBorder[split] || axis.m_preventResize[split]) {
    759         axis.m_splitBeingResized = noSplit;
    760         return;
    761     }
    762     axis.m_splitBeingResized = split;
    763     axis.m_splitResizeOffset = position - splitPosition(axis, split);
    764 }
    765 
    766 void RenderFrameSet::continueResizing(GridAxis& axis, int position)
    767 {
    768     if (needsLayout())
    769         return;
    770     if (axis.m_splitBeingResized == noSplit)
    771         return;
    772     int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
    773     int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
    774     if (delta == 0)
    775         return;
    776     axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
    777     axis.m_deltas[axis.m_splitBeingResized] -= delta;
    778     setNeedsLayout(true);
    779 }
    780 
    781 bool RenderFrameSet::userResize(MouseEvent* evt)
    782 {
    783     if (flattenFrameSet())
    784         return false;
    785 
    786     if (!m_isResizing) {
    787         if (needsLayout())
    788             return false;
    789         if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) {
    790             FloatPoint pos = localToAbsolute();
    791             startResizing(m_cols, evt->absoluteLocation().x() - pos.x());
    792             startResizing(m_rows, evt->absoluteLocation().y() - pos.y());
    793             if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
    794                 setIsResizing(true);
    795                 return true;
    796             }
    797         }
    798     } else {
    799         if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) {
    800             FloatPoint pos = localToAbsolute();
    801             continueResizing(m_cols, evt->absoluteLocation().x() - pos.x());
    802             continueResizing(m_rows, evt->absoluteLocation().y() - pos.y());
    803             if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) {
    804                 setIsResizing(false);
    805                 return true;
    806             }
    807         }
    808     }
    809 
    810     return false;
    811 }
    812 
    813 void RenderFrameSet::setIsResizing(bool isResizing)
    814 {
    815     m_isResizing = isResizing;
    816     for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
    817         if (ancestor->isFrameSet())
    818             toRenderFrameSet(ancestor)->m_isChildResizing = isResizing;
    819     }
    820     if (Frame* frame = document()->frame())
    821         frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0);
    822 }
    823 
    824 bool RenderFrameSet::isResizingRow() const
    825 {
    826     return m_isResizing && m_rows.m_splitBeingResized != noSplit;
    827 }
    828 
    829 bool RenderFrameSet::isResizingColumn() const
    830 {
    831     return m_isResizing && m_cols.m_splitBeingResized != noSplit;
    832 }
    833 
    834 bool RenderFrameSet::canResizeRow(const IntPoint& p) const
    835 {
    836     int r = hitTestSplit(m_rows, p.y());
    837     return r != noSplit && m_rows.m_allowBorder[r] && !m_rows.m_preventResize[r];
    838 }
    839 
    840 bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
    841 {
    842     int c = hitTestSplit(m_cols, p.x());
    843     return c != noSplit && m_cols.m_allowBorder[c] && !m_cols.m_preventResize[c];
    844 }
    845 
    846 int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
    847 {
    848     if (needsLayout())
    849         return 0;
    850 
    851     int borderThickness = frameSet()->border();
    852 
    853     int size = axis.m_sizes.size();
    854     if (!size)
    855         return 0;
    856 
    857     int position = 0;
    858     for (int i = 0; i < split && i < size; ++i)
    859         position += axis.m_sizes[i] + borderThickness;
    860     return position - borderThickness;
    861 }
    862 
    863 int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
    864 {
    865     if (needsLayout())
    866         return noSplit;
    867 
    868     int borderThickness = frameSet()->border();
    869     if (borderThickness <= 0)
    870         return noSplit;
    871 
    872     size_t size = axis.m_sizes.size();
    873     if (!size)
    874         return noSplit;
    875 
    876     int splitPosition = axis.m_sizes[0];
    877     for (size_t i = 1; i < size; ++i) {
    878         if (position >= splitPosition && position < splitPosition + borderThickness)
    879             return i;
    880         splitPosition += borderThickness + axis.m_sizes[i];
    881     }
    882     return noSplit;
    883 }
    884 
    885 bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const
    886 {
    887     return child->isFrame() || child->isFrameSet();
    888 }
    889 
    890 } // namespace WebCore
    891