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