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 "core/rendering/RenderFrameSet.h"
     26 
     27 #include "core/dom/Document.h"
     28 #include "core/events/MouseEvent.h"
     29 #include "core/frame/LocalFrame.h"
     30 #include "core/html/HTMLDimension.h"
     31 #include "core/html/HTMLFrameSetElement.h"
     32 #include "core/page/EventHandler.h"
     33 #include "core/rendering/GraphicsContextAnnotator.h"
     34 #include "core/rendering/PaintInfo.h"
     35 #include "core/rendering/RenderFrame.h"
     36 #include "core/rendering/RenderView.h"
     37 #include "platform/Cursor.h"
     38 #include "platform/graphics/GraphicsContext.h"
     39 
     40 namespace blink {
     41 
     42 RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
     43     : RenderBox(frameSet)
     44     , m_isResizing(false)
     45     , m_isChildResizing(false)
     46 {
     47     setInline(false);
     48 }
     49 
     50 RenderFrameSet::~RenderFrameSet()
     51 {
     52 }
     53 
     54 void RenderFrameSet::trace(Visitor* visitor)
     55 {
     56     visitor->trace(m_children);
     57     RenderBox::trace(visitor);
     58 }
     59 
     60 RenderFrameSet::GridAxis::GridAxis()
     61     : m_splitBeingResized(noSplit)
     62 {
     63 }
     64 
     65 inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
     66 {
     67     return toHTMLFrameSetElement(node());
     68 }
     69 
     70 static Color borderStartEdgeColor()
     71 {
     72     return Color(170, 170, 170);
     73 }
     74 
     75 static Color borderEndEdgeColor()
     76 {
     77     return Color::black;
     78 }
     79 
     80 static Color borderFillColor()
     81 {
     82     return Color(208, 208, 208);
     83 }
     84 
     85 void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
     86 {
     87     if (!paintInfo.rect.intersects(borderRect))
     88         return;
     89 
     90     // FIXME: We should do something clever when borders from distinct framesets meet at a join.
     91 
     92     // Fill first.
     93     GraphicsContext* context = paintInfo.context;
     94     context->fillRect(borderRect, frameSet()->hasBorderColor() ? resolveColor(CSSPropertyBorderLeftColor) : borderFillColor());
     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());
    100         context->fillRect(IntRect(IntPoint(borderRect.maxX() - 1, borderRect.y()), IntSize(1, height())), borderEndEdgeColor());
    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     context->fillRect(borderRect, frameSet()->hasBorderColor() ? resolveColor(CSSPropertyBorderLeftColor) : borderFillColor());
    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.location(), IntSize(width(), 1)), borderStartEdgeColor());
    119         context->fillRect(IntRect(IntPoint(borderRect.x(), borderRect.maxY() - 1), IntSize(width(), 1)), borderEndEdgeColor());
    120     }
    121 }
    122 
    123 void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    124 {
    125     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    126 
    127     if (paintInfo.phase != PaintPhaseForeground)
    128         return;
    129 
    130     RenderObject* child = firstChild();
    131     if (!child)
    132         return;
    133 
    134     LayoutPoint adjustedPaintOffset = paintOffset + location();
    135 
    136     size_t rows = m_rows.m_sizes.size();
    137     size_t cols = m_cols.m_sizes.size();
    138     LayoutUnit borderThickness = frameSet()->border();
    139 
    140     LayoutUnit yPos = 0;
    141     for (size_t r = 0; r < rows; r++) {
    142         LayoutUnit xPos = 0;
    143         for (size_t c = 0; c < cols; c++) {
    144             child->paint(paintInfo, adjustedPaintOffset);
    145             xPos += m_cols.m_sizes[c];
    146             if (borderThickness && m_cols.m_allowBorder[c + 1]) {
    147                 paintColumnBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x() + xPos, adjustedPaintOffset.y() + 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, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness)));
    157             yPos += borderThickness;
    158         }
    159     }
    160 }
    161 
    162 void RenderFrameSet::computePreferredLogicalWidths()
    163 {
    164     m_minPreferredLogicalWidth = 0;
    165     m_maxPreferredLogicalWidth = 0;
    166     clearPreferredLogicalWidthsDirty();
    167 }
    168 
    169 void RenderFrameSet::GridAxis::resize(int size)
    170 {
    171     m_sizes.resize(size);
    172     m_deltas.resize(size);
    173     m_deltas.fill(0);
    174 
    175     // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset
    176     // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
    177     // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info.
    178     m_preventResize.resize(size + 1);
    179     m_allowBorder.resize(size + 1);
    180 }
    181 
    182 void RenderFrameSet::layOutAxis(GridAxis& axis, const Vector<HTMLDimension>& grid, int availableLen)
    183 {
    184     availableLen = max(availableLen, 0);
    185 
    186     int* gridLayout = axis.m_sizes.data();
    187 
    188     if (grid.isEmpty()) {
    189         gridLayout[0] = availableLen;
    190         return;
    191     }
    192 
    193     int gridLen = axis.m_sizes.size();
    194     ASSERT(gridLen);
    195 
    196     int totalRelative = 0;
    197     int totalFixed = 0;
    198     int totalPercent = 0;
    199     int countRelative = 0;
    200     int countFixed = 0;
    201     int countPercent = 0;
    202 
    203     // First we need to investigate how many columns of each type we have and
    204     // how much space these columns are going to require.
    205     for (int i = 0; i < gridLen; ++i) {
    206         // Count the total length of all of the fixed columns/rows -> totalFixed
    207         // Count the number of columns/rows which are fixed -> countFixed
    208         if (grid[i].isAbsolute()) {
    209             gridLayout[i] = max<int>(grid[i].value(), 0);
    210             totalFixed += gridLayout[i];
    211             countFixed++;
    212         }
    213 
    214         // Count the total percentage of all of the percentage columns/rows -> totalPercent
    215         // Count the number of columns/rows which are percentages -> countPercent
    216         if (grid[i].isPercentage()) {
    217             gridLayout[i] = max<int>(grid[i].value() * availableLen / 100., 0);
    218             totalPercent += gridLayout[i];
    219             countPercent++;
    220         }
    221 
    222         // Count the total relative of all the relative columns/rows -> totalRelative
    223         // Count the number of columns/rows which are relative -> countRelative
    224         if (grid[i].isRelative()) {
    225             totalRelative += max<int>(grid[i].value(), 1);
    226             countRelative++;
    227         }
    228     }
    229 
    230     int remainingLen = availableLen;
    231 
    232     // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
    233     // columns/rows we need to proportionally adjust their size.
    234     if (totalFixed > remainingLen) {
    235         int remainingFixed = remainingLen;
    236 
    237         for (int i = 0; i < gridLen; ++i) {
    238             if (grid[i].isAbsolute()) {
    239                 gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
    240                 remainingLen -= gridLayout[i];
    241             }
    242         }
    243     } else
    244         remainingLen -= totalFixed;
    245 
    246     // Percentage columns/rows are our second priority. Divide the remaining space proportionally
    247     // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
    248     // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
    249     // and the available space is 300px, each column will become 100px in width.
    250     if (totalPercent > remainingLen) {
    251         int remainingPercent = remainingLen;
    252 
    253         for (int i = 0; i < gridLen; ++i) {
    254             if (grid[i].isPercentage()) {
    255                 gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
    256                 remainingLen -= gridLayout[i];
    257             }
    258         }
    259     } else
    260         remainingLen -= totalPercent;
    261 
    262     // Relative columns/rows are our last priority. Divide the remaining space proportionally
    263     // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
    264     if (countRelative) {
    265         int lastRelative = 0;
    266         int remainingRelative = remainingLen;
    267 
    268         for (int i = 0; i < gridLen; ++i) {
    269             if (grid[i].isRelative()) {
    270                 gridLayout[i] = (max(grid[i].value(), 1.) * remainingRelative) / totalRelative;
    271                 remainingLen -= gridLayout[i];
    272                 lastRelative = i;
    273             }
    274         }
    275 
    276         // If we could not evenly distribute the available space of all of the relative
    277         // columns/rows, the remainder will be added to the last column/row.
    278         // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
    279         // be 1px and will be added to the last column: 33px, 33px, 34px.
    280         if (remainingLen) {
    281             gridLayout[lastRelative] += remainingLen;
    282             remainingLen = 0;
    283         }
    284     }
    285 
    286     // If we still have some left over space we need to divide it over the already existing
    287     // columns/rows
    288     if (remainingLen) {
    289         // Our first priority is to spread if over the percentage columns. The remaining
    290         // space is spread evenly, for example: if we have a space of 100px, the columns
    291         // definition of 25%,25% used to result in two columns of 25px. After this the
    292         // columns will each be 50px in width.
    293         if (countPercent && totalPercent) {
    294             int remainingPercent = remainingLen;
    295             int changePercent = 0;
    296 
    297             for (int i = 0; i < gridLen; ++i) {
    298                 if (grid[i].isPercentage()) {
    299                     changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
    300                     gridLayout[i] += changePercent;
    301                     remainingLen -= changePercent;
    302                 }
    303             }
    304         } else if (totalFixed) {
    305             // Our last priority is to spread the remaining space over the fixed columns.
    306             // For example if we have 100px of space and two column of each 40px, both
    307             // columns will become exactly 50px.
    308             int remainingFixed = remainingLen;
    309             int changeFixed = 0;
    310 
    311             for (int i = 0; i < gridLen; ++i) {
    312                 if (grid[i].isAbsolute()) {
    313                     changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
    314                     gridLayout[i] += changeFixed;
    315                     remainingLen -= changeFixed;
    316                 }
    317             }
    318         }
    319     }
    320 
    321     // If we still have some left over space we probably ended up with a remainder of
    322     // a division. We cannot spread it evenly anymore. If we have any percentage
    323     // columns/rows simply spread the remainder equally over all available percentage columns,
    324     // regardless of their size.
    325     if (remainingLen && countPercent) {
    326         int remainingPercent = remainingLen;
    327         int changePercent = 0;
    328 
    329         for (int i = 0; i < gridLen; ++i) {
    330             if (grid[i].isPercentage()) {
    331                 changePercent = remainingPercent / countPercent;
    332                 gridLayout[i] += changePercent;
    333                 remainingLen -= changePercent;
    334             }
    335         }
    336     } else if (remainingLen && countFixed) {
    337         // If we don't have any percentage columns/rows we only have
    338         // fixed columns. Spread the remainder equally over all fixed
    339         // columns/rows.
    340         int remainingFixed = remainingLen;
    341         int changeFixed = 0;
    342 
    343         for (int i = 0; i < gridLen; ++i) {
    344             if (grid[i].isAbsolute()) {
    345                 changeFixed = remainingFixed / countFixed;
    346                 gridLayout[i] += changeFixed;
    347                 remainingLen -= changeFixed;
    348             }
    349         }
    350     }
    351 
    352     // Still some left over. Add it to the last column, because it is impossible
    353     // spread it evenly or equally.
    354     if (remainingLen)
    355         gridLayout[gridLen - 1] += remainingLen;
    356 
    357     // now we have the final layout, distribute the delta over it
    358     bool worked = true;
    359     int* gridDelta = axis.m_deltas.data();
    360     for (int i = 0; i < gridLen; ++i) {
    361         if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
    362             worked = false;
    363         gridLayout[i] += gridDelta[i];
    364     }
    365     // if the deltas broke something, undo them
    366     if (!worked) {
    367         for (int i = 0; i < gridLen; ++i)
    368             gridLayout[i] -= gridDelta[i];
    369         axis.m_deltas.fill(0);
    370     }
    371 }
    372 
    373 void RenderFrameSet::notifyFrameEdgeInfoChanged()
    374 {
    375     if (needsLayout())
    376         return;
    377     // FIXME: We should only recompute the edge info with respect to the frame that changed
    378     // and its adjacent frame(s) instead of recomputing the edge info for the entire frameset.
    379     computeEdgeInfo();
    380 }
    381 
    382 void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
    383 {
    384     if (edgeInfo.allowBorder(LeftFrameEdge))
    385         m_cols.m_allowBorder[c] = true;
    386     if (edgeInfo.allowBorder(RightFrameEdge))
    387         m_cols.m_allowBorder[c + 1] = true;
    388     if (edgeInfo.preventResize(LeftFrameEdge))
    389         m_cols.m_preventResize[c] = true;
    390     if (edgeInfo.preventResize(RightFrameEdge))
    391         m_cols.m_preventResize[c + 1] = true;
    392 
    393     if (edgeInfo.allowBorder(TopFrameEdge))
    394         m_rows.m_allowBorder[r] = true;
    395     if (edgeInfo.allowBorder(BottomFrameEdge))
    396         m_rows.m_allowBorder[r + 1] = true;
    397     if (edgeInfo.preventResize(TopFrameEdge))
    398         m_rows.m_preventResize[r] = true;
    399     if (edgeInfo.preventResize(BottomFrameEdge))
    400         m_rows.m_preventResize[r + 1] = true;
    401 }
    402 
    403 void RenderFrameSet::computeEdgeInfo()
    404 {
    405     m_rows.m_preventResize.fill(frameSet()->noResize());
    406     m_rows.m_allowBorder.fill(false);
    407     m_cols.m_preventResize.fill(frameSet()->noResize());
    408     m_cols.m_allowBorder.fill(false);
    409 
    410     RenderObject* child = firstChild();
    411     if (!child)
    412         return;
    413 
    414     size_t rows = m_rows.m_sizes.size();
    415     size_t cols = m_cols.m_sizes.size();
    416     for (size_t r = 0; r < rows; ++r) {
    417         for (size_t c = 0; c < cols; ++c) {
    418             FrameEdgeInfo edgeInfo;
    419             if (child->isFrameSet())
    420                 edgeInfo = toRenderFrameSet(child)->edgeInfo();
    421             else
    422                 edgeInfo = toRenderFrame(child)->edgeInfo();
    423             fillFromEdgeInfo(edgeInfo, r, c);
    424             child = child->nextSibling();
    425             if (!child)
    426                 return;
    427         }
    428     }
    429 }
    430 
    431 FrameEdgeInfo RenderFrameSet::edgeInfo() const
    432 {
    433     FrameEdgeInfo result(frameSet()->noResize(), true);
    434 
    435     int rows = frameSet()->totalRows();
    436     int cols = frameSet()->totalCols();
    437     if (rows && cols) {
    438         result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
    439         result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
    440         result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
    441         result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
    442         result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
    443         result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
    444         result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
    445         result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
    446     }
    447 
    448     return result;
    449 }
    450 
    451 void RenderFrameSet::layout()
    452 {
    453     ASSERT(needsLayout());
    454 
    455     if (!parent()->isFrameSet() && !document().printing()) {
    456         setWidth(view()->viewWidth());
    457         setHeight(view()->viewHeight());
    458     }
    459 
    460     unsigned cols = frameSet()->totalCols();
    461     unsigned rows = frameSet()->totalRows();
    462 
    463     if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
    464         m_rows.resize(rows);
    465         m_cols.resize(cols);
    466     }
    467 
    468     LayoutUnit borderThickness = frameSet()->border();
    469     layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness);
    470     layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness);
    471 
    472     positionFrames();
    473 
    474     RenderBox::layout();
    475 
    476     computeEdgeInfo();
    477 
    478     updateLayerTransformAfterLayout();
    479 
    480     clearNeedsLayout();
    481 }
    482 
    483 static void clearNeedsLayoutOnHiddenFrames(RenderBox* frame)
    484 {
    485     for (; frame; frame = frame->nextSiblingBox()) {
    486         frame->setWidth(0);
    487         frame->setHeight(0);
    488         frame->clearNeedsLayout();
    489         clearNeedsLayoutOnHiddenFrames(frame->firstChildBox());
    490     }
    491 }
    492 
    493 void RenderFrameSet::positionFrames()
    494 {
    495     RenderBox* child = firstChildBox();
    496     if (!child)
    497         return;
    498 
    499     int rows = frameSet()->totalRows();
    500     int cols = frameSet()->totalCols();
    501 
    502     int yPos = 0;
    503     int borderThickness = frameSet()->border();
    504     for (int r = 0; r < rows; r++) {
    505         int xPos = 0;
    506         int height = m_rows.m_sizes[r];
    507         for (int c = 0; c < cols; c++) {
    508             child->setLocation(IntPoint(xPos, yPos));
    509             int width = m_cols.m_sizes[c];
    510 
    511             // has to be resized and itself resize its contents
    512             if (width != child->width() || height != child->height()) {
    513                 child->setWidth(width);
    514                 child->setHeight(height);
    515                 child->setNeedsLayoutAndFullPaintInvalidation();
    516                 child->layout();
    517             }
    518 
    519             xPos += width + borderThickness;
    520 
    521             child = child->nextSiblingBox();
    522             if (!child)
    523                 return;
    524         }
    525         yPos += height + borderThickness;
    526     }
    527 
    528     // All the remaining frames are hidden to avoid ugly spurious unflowed frames.
    529     clearNeedsLayoutOnHiddenFrames(child);
    530 }
    531 
    532 void RenderFrameSet::startResizing(GridAxis& axis, int position)
    533 {
    534     int split = hitTestSplit(axis, position);
    535     if (split == noSplit || axis.m_preventResize[split]) {
    536         axis.m_splitBeingResized = noSplit;
    537         return;
    538     }
    539     axis.m_splitBeingResized = split;
    540     axis.m_splitResizeOffset = position - splitPosition(axis, split);
    541 }
    542 
    543 void RenderFrameSet::continueResizing(GridAxis& axis, int position)
    544 {
    545     if (needsLayout())
    546         return;
    547     if (axis.m_splitBeingResized == noSplit)
    548         return;
    549     int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
    550     int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
    551     if (!delta)
    552         return;
    553     axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
    554     axis.m_deltas[axis.m_splitBeingResized] -= delta;
    555     setNeedsLayoutAndFullPaintInvalidation();
    556 }
    557 
    558 bool RenderFrameSet::userResize(MouseEvent* evt)
    559 {
    560     if (!m_isResizing) {
    561         if (needsLayout())
    562             return false;
    563         if (evt->type() == EventTypeNames::mousedown && evt->button() == LeftButton) {
    564             FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
    565             startResizing(m_cols, localPos.x());
    566             startResizing(m_rows, localPos.y());
    567             if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
    568                 setIsResizing(true);
    569                 return true;
    570             }
    571         }
    572     } else {
    573         if (evt->type() == EventTypeNames::mousemove || (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton)) {
    574             FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
    575             continueResizing(m_cols, localPos.x());
    576             continueResizing(m_rows, localPos.y());
    577             if (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton) {
    578                 setIsResizing(false);
    579                 return true;
    580             }
    581         }
    582     }
    583 
    584     return false;
    585 }
    586 
    587 void RenderFrameSet::setIsResizing(bool isResizing)
    588 {
    589     m_isResizing = isResizing;
    590     for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
    591         if (ancestor->isFrameSet())
    592             toRenderFrameSet(ancestor)->m_isChildResizing = isResizing;
    593     }
    594     if (LocalFrame* frame = this->frame())
    595         frame->eventHandler().setResizingFrameSet(isResizing ? frameSet() : 0);
    596 }
    597 
    598 bool RenderFrameSet::canResizeRow(const IntPoint& p) const
    599 {
    600     int r = hitTestSplit(m_rows, p.y());
    601     return r != noSplit && !m_rows.m_preventResize[r];
    602 }
    603 
    604 bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
    605 {
    606     int c = hitTestSplit(m_cols, p.x());
    607     return c != noSplit && !m_cols.m_preventResize[c];
    608 }
    609 
    610 int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
    611 {
    612     if (needsLayout())
    613         return 0;
    614 
    615     int borderThickness = frameSet()->border();
    616 
    617     int size = axis.m_sizes.size();
    618     if (!size)
    619         return 0;
    620 
    621     int position = 0;
    622     for (int i = 0; i < split && i < size; ++i)
    623         position += axis.m_sizes[i] + borderThickness;
    624     return position - borderThickness;
    625 }
    626 
    627 int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
    628 {
    629     if (needsLayout())
    630         return noSplit;
    631 
    632     int borderThickness = frameSet()->border();
    633     if (borderThickness <= 0)
    634         return noSplit;
    635 
    636     size_t size = axis.m_sizes.size();
    637     if (!size)
    638         return noSplit;
    639 
    640     int splitPosition = axis.m_sizes[0];
    641     for (size_t i = 1; i < size; ++i) {
    642         if (position >= splitPosition && position < splitPosition + borderThickness)
    643             return i;
    644         splitPosition += borderThickness + axis.m_sizes[i];
    645     }
    646     return noSplit;
    647 }
    648 
    649 bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const
    650 {
    651     return child->isFrame() || child->isFrameSet();
    652 }
    653 
    654 CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
    655 {
    656     IntPoint roundedPoint = roundedIntPoint(point);
    657     if (canResizeRow(roundedPoint)) {
    658         cursor = rowResizeCursor();
    659         return SetCursor;
    660     }
    661     if (canResizeColumn(roundedPoint)) {
    662         cursor = columnResizeCursor();
    663         return SetCursor;
    664     }
    665     return RenderBox::getCursor(point, cursor);
    666 }
    667 
    668 } // namespace blink
    669