1/**
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 2000 Simon Hausmann <hausmann@kde.org>
4 *           (C) 2000 Stefan Schimanski (1Stein@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 "Cursor.h"
28#include "Document.h"
29#include "EventHandler.h"
30#include "EventNames.h"
31#include "Frame.h"
32#include "FrameView.h"
33#include "GraphicsContext.h"
34#include "HTMLFrameSetElement.h"
35#include "HitTestRequest.h"
36#include "HitTestResult.h"
37#include "MouseEvent.h"
38#include "PaintInfo.h"
39#include "RenderFrame.h"
40#include "RenderLayer.h"
41#include "RenderView.h"
42#include "Settings.h"
43#include <wtf/StackStats.h>
44
45namespace WebCore {
46
47RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
48    : RenderBox(frameSet)
49    , m_isResizing(false)
50    , m_isChildResizing(false)
51{
52    setInline(false);
53}
54
55RenderFrameSet::~RenderFrameSet()
56{
57}
58
59RenderFrameSet::GridAxis::GridAxis()
60    : m_splitBeingResized(noSplit)
61{
62}
63
64inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
65{
66    return static_cast<HTMLFrameSetElement*>(node());
67}
68
69static Color borderStartEdgeColor()
70{
71    return Color(170, 170, 170);
72}
73
74static Color borderEndEdgeColor()
75{
76    return Color::black;
77}
78
79static Color borderFillColor()
80{
81    return Color(208, 208, 208);
82}
83
84void 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
104void 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
124void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
125{
126    if (paintInfo.phase != PaintPhaseForeground)
127        return;
128
129    RenderObject* child = firstChild();
130    if (!child)
131        return;
132
133    LayoutPoint adjustedPaintOffset = paintOffset + location();
134
135    size_t rows = m_rows.m_sizes.size();
136    size_t cols = m_cols.m_sizes.size();
137    LayoutUnit borderThickness = frameSet()->border();
138
139    LayoutUnit yPos = 0;
140    for (size_t r = 0; r < rows; r++) {
141        LayoutUnit xPos = 0;
142        for (size_t c = 0; c < cols; c++) {
143            child->paint(paintInfo, adjustedPaintOffset);
144            xPos += m_cols.m_sizes[c];
145            if (borderThickness && m_cols.m_allowBorder[c + 1]) {
146                paintColumnBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x() + xPos, adjustedPaintOffset.y() + yPos, borderThickness, height())));
147                xPos += borderThickness;
148            }
149            child = child->nextSibling();
150            if (!child)
151                return;
152        }
153        yPos += m_rows.m_sizes[r];
154        if (borderThickness && m_rows.m_allowBorder[r + 1]) {
155            paintRowBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness)));
156            yPos += borderThickness;
157        }
158    }
159}
160
161void RenderFrameSet::GridAxis::resize(int size)
162{
163    m_sizes.resize(size);
164    m_deltas.resize(size);
165    m_deltas.fill(0);
166
167    // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset
168    // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
169    // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info.
170    m_preventResize.resize(size + 1);
171    m_allowBorder.resize(size + 1);
172}
173
174void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
175{
176    availableLen = max(availableLen, 0);
177
178    int* gridLayout = axis.m_sizes.data();
179
180    if (!grid) {
181        gridLayout[0] = availableLen;
182        return;
183    }
184
185    int gridLen = axis.m_sizes.size();
186    ASSERT(gridLen);
187
188    int totalRelative = 0;
189    int totalFixed = 0;
190    int totalPercent = 0;
191    int countRelative = 0;
192    int countFixed = 0;
193    int countPercent = 0;
194
195    // First we need to investigate how many columns of each type we have and
196    // how much space these columns are going to require.
197    for (int i = 0; i < gridLen; ++i) {
198        // Count the total length of all of the fixed columns/rows -> totalFixed
199        // Count the number of columns/rows which are fixed -> countFixed
200        if (grid[i].isFixed()) {
201            gridLayout[i] = max(grid[i].intValue(), 0);
202            totalFixed += gridLayout[i];
203            countFixed++;
204        }
205
206        // Count the total percentage of all of the percentage columns/rows -> totalPercent
207        // Count the number of columns/rows which are percentages -> countPercent
208        if (grid[i].isPercent()) {
209            gridLayout[i] = max(intValueForLength(grid[i], availableLen), 0);
210            totalPercent += gridLayout[i];
211            countPercent++;
212        }
213
214        // Count the total relative of all the relative columns/rows -> totalRelative
215        // Count the number of columns/rows which are relative -> countRelative
216        if (grid[i].isRelative()) {
217            totalRelative += max(grid[i].intValue(), 1);
218            countRelative++;
219        }
220    }
221
222    int remainingLen = availableLen;
223
224    // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
225    // columns/rows we need to proportionally adjust their size.
226    if (totalFixed > remainingLen) {
227        int remainingFixed = remainingLen;
228
229        for (int i = 0; i < gridLen; ++i) {
230            if (grid[i].isFixed()) {
231                gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
232                remainingLen -= gridLayout[i];
233            }
234        }
235    } else
236        remainingLen -= totalFixed;
237
238    // Percentage columns/rows are our second priority. Divide the remaining space proportionally
239    // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
240    // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
241    // and the available space is 300px, each column will become 100px in width.
242    if (totalPercent > remainingLen) {
243        int remainingPercent = remainingLen;
244
245        for (int i = 0; i < gridLen; ++i) {
246            if (grid[i].isPercent()) {
247                gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
248                remainingLen -= gridLayout[i];
249            }
250        }
251    } else
252        remainingLen -= totalPercent;
253
254    // Relative columns/rows are our last priority. Divide the remaining space proportionally
255    // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
256    if (countRelative) {
257        int lastRelative = 0;
258        int remainingRelative = remainingLen;
259
260        for (int i = 0; i < gridLen; ++i) {
261            if (grid[i].isRelative()) {
262                gridLayout[i] = (max(grid[i].intValue(), 1) * remainingRelative) / totalRelative;
263                remainingLen -= gridLayout[i];
264                lastRelative = i;
265            }
266        }
267
268        // If we could not evenly distribute the available space of all of the relative
269        // columns/rows, the remainder will be added to the last column/row.
270        // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
271        // be 1px and will be added to the last column: 33px, 33px, 34px.
272        if (remainingLen) {
273            gridLayout[lastRelative] += remainingLen;
274            remainingLen = 0;
275        }
276    }
277
278    // If we still have some left over space we need to divide it over the already existing
279    // columns/rows
280    if (remainingLen) {
281        // Our first priority is to spread if over the percentage columns. The remaining
282        // space is spread evenly, for example: if we have a space of 100px, the columns
283        // definition of 25%,25% used to result in two columns of 25px. After this the
284        // columns will each be 50px in width.
285        if (countPercent && totalPercent) {
286            int remainingPercent = remainingLen;
287            int changePercent = 0;
288
289            for (int i = 0; i < gridLen; ++i) {
290                if (grid[i].isPercent()) {
291                    changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
292                    gridLayout[i] += changePercent;
293                    remainingLen -= changePercent;
294                }
295            }
296        } else if (totalFixed) {
297            // Our last priority is to spread the remaining space over the fixed columns.
298            // For example if we have 100px of space and two column of each 40px, both
299            // columns will become exactly 50px.
300            int remainingFixed = remainingLen;
301            int changeFixed = 0;
302
303            for (int i = 0; i < gridLen; ++i) {
304                if (grid[i].isFixed()) {
305                    changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
306                    gridLayout[i] += changeFixed;
307                    remainingLen -= changeFixed;
308                }
309            }
310        }
311    }
312
313    // If we still have some left over space we probably ended up with a remainder of
314    // a division. We cannot spread it evenly anymore. If we have any percentage
315    // columns/rows simply spread the remainder equally over all available percentage columns,
316    // regardless of their size.
317    if (remainingLen && countPercent) {
318        int remainingPercent = remainingLen;
319        int changePercent = 0;
320
321        for (int i = 0; i < gridLen; ++i) {
322            if (grid[i].isPercent()) {
323                changePercent = remainingPercent / countPercent;
324                gridLayout[i] += changePercent;
325                remainingLen -= changePercent;
326            }
327        }
328    } else if (remainingLen && countFixed) {
329        // If we don't have any percentage columns/rows we only have
330        // fixed columns. Spread the remainder equally over all fixed
331        // columns/rows.
332        int remainingFixed = remainingLen;
333        int changeFixed = 0;
334
335        for (int i = 0; i < gridLen; ++i) {
336            if (grid[i].isFixed()) {
337                changeFixed = remainingFixed / countFixed;
338                gridLayout[i] += changeFixed;
339                remainingLen -= changeFixed;
340            }
341        }
342    }
343
344    // Still some left over. Add it to the last column, because it is impossible
345    // spread it evenly or equally.
346    if (remainingLen)
347        gridLayout[gridLen - 1] += remainingLen;
348
349    // now we have the final layout, distribute the delta over it
350    bool worked = true;
351    int* gridDelta = axis.m_deltas.data();
352    for (int i = 0; i < gridLen; ++i) {
353        if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
354            worked = false;
355        gridLayout[i] += gridDelta[i];
356    }
357    // if the deltas broke something, undo them
358    if (!worked) {
359        for (int i = 0; i < gridLen; ++i)
360            gridLayout[i] -= gridDelta[i];
361        axis.m_deltas.fill(0);
362    }
363}
364
365void RenderFrameSet::notifyFrameEdgeInfoChanged()
366{
367    if (needsLayout())
368        return;
369    // FIXME: We should only recompute the edge info with respect to the frame that changed
370    // and its adjacent frame(s) instead of recomputing the edge info for the entire frameset.
371    computeEdgeInfo();
372}
373
374void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
375{
376    if (edgeInfo.allowBorder(LeftFrameEdge))
377        m_cols.m_allowBorder[c] = true;
378    if (edgeInfo.allowBorder(RightFrameEdge))
379        m_cols.m_allowBorder[c + 1] = true;
380    if (edgeInfo.preventResize(LeftFrameEdge))
381        m_cols.m_preventResize[c] = true;
382    if (edgeInfo.preventResize(RightFrameEdge))
383        m_cols.m_preventResize[c + 1] = true;
384
385    if (edgeInfo.allowBorder(TopFrameEdge))
386        m_rows.m_allowBorder[r] = true;
387    if (edgeInfo.allowBorder(BottomFrameEdge))
388        m_rows.m_allowBorder[r + 1] = true;
389    if (edgeInfo.preventResize(TopFrameEdge))
390        m_rows.m_preventResize[r] = true;
391    if (edgeInfo.preventResize(BottomFrameEdge))
392        m_rows.m_preventResize[r + 1] = true;
393}
394
395void RenderFrameSet::computeEdgeInfo()
396{
397    m_rows.m_preventResize.fill(frameSet()->noResize());
398    m_rows.m_allowBorder.fill(false);
399    m_cols.m_preventResize.fill(frameSet()->noResize());
400    m_cols.m_allowBorder.fill(false);
401
402    RenderObject* child = firstChild();
403    if (!child)
404        return;
405
406    size_t rows = m_rows.m_sizes.size();
407    size_t cols = m_cols.m_sizes.size();
408    for (size_t r = 0; r < rows; ++r) {
409        for (size_t c = 0; c < cols; ++c) {
410            FrameEdgeInfo edgeInfo;
411            if (child->isFrameSet())
412                edgeInfo = toRenderFrameSet(child)->edgeInfo();
413            else
414                edgeInfo = toRenderFrame(child)->edgeInfo();
415            fillFromEdgeInfo(edgeInfo, r, c);
416            child = child->nextSibling();
417            if (!child)
418                return;
419        }
420    }
421}
422
423FrameEdgeInfo RenderFrameSet::edgeInfo() const
424{
425    FrameEdgeInfo result(frameSet()->noResize(), true);
426
427    int rows = frameSet()->totalRows();
428    int cols = frameSet()->totalCols();
429    if (rows && cols) {
430        result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
431        result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
432        result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
433        result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
434        result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
435        result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
436        result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
437        result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
438    }
439
440    return result;
441}
442
443void RenderFrameSet::layout()
444{
445    StackStats::LayoutCheckPoint layoutCheckPoint;
446    ASSERT(needsLayout());
447
448    bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout();
449    LayoutRect oldBounds;
450    RenderLayerModelObject* repaintContainer = 0;
451    if (doFullRepaint) {
452        repaintContainer = containerForRepaint();
453        oldBounds = clippedOverflowRectForRepaint(repaintContainer);
454    }
455
456    if (!parent()->isFrameSet() && !document()->printing()) {
457        setWidth(view()->viewWidth());
458        setHeight(view()->viewHeight());
459    }
460
461    unsigned cols = frameSet()->totalCols();
462    unsigned rows = frameSet()->totalRows();
463
464    if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
465        m_rows.resize(rows);
466        m_cols.resize(cols);
467    }
468
469    LayoutUnit borderThickness = frameSet()->border();
470    layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness);
471    layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness);
472
473    if (flattenFrameSet())
474        positionFramesWithFlattening();
475    else
476        positionFrames();
477
478    RenderBox::layout();
479
480    computeEdgeInfo();
481
482    updateLayerTransform();
483
484    if (doFullRepaint) {
485        repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds));
486        LayoutRect newBounds = clippedOverflowRectForRepaint(repaintContainer);
487        if (newBounds != oldBounds)
488            repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds));
489    }
490
491    setNeedsLayout(false);
492}
493
494void RenderFrameSet::positionFrames()
495{
496    RenderBox* child = firstChildBox();
497    if (!child)
498        return;
499
500    int rows = frameSet()->totalRows();
501    int cols = frameSet()->totalCols();
502
503    int yPos = 0;
504    int borderThickness = frameSet()->border();
505    for (int r = 0; r < rows; r++) {
506        int xPos = 0;
507        int height = m_rows.m_sizes[r];
508        for (int c = 0; c < cols; c++) {
509            child->setLocation(IntPoint(xPos, yPos));
510            int width = m_cols.m_sizes[c];
511
512            // has to be resized and itself resize its contents
513            if (width != child->width() || height != child->height()) {
514                child->setWidth(width);
515                child->setHeight(height);
516                child->setNeedsLayout(true);
517                child->layout();
518            }
519
520            xPos += width + borderThickness;
521
522            child = child->nextSiblingBox();
523            if (!child)
524                return;
525        }
526        yPos += height + borderThickness;
527    }
528
529    // all the remaining frames are hidden to avoid ugly spurious unflowed frames
530    for (; child; child = child->nextSiblingBox()) {
531        child->setWidth(0);
532        child->setHeight(0);
533        child->setNeedsLayout(false);
534    }
535}
536
537void RenderFrameSet::positionFramesWithFlattening()
538{
539    RenderBox* child = firstChildBox();
540    if (!child)
541        return;
542
543    int rows = frameSet()->totalRows();
544    int cols = frameSet()->totalCols();
545
546    int borderThickness = frameSet()->border();
547    bool repaintNeeded = false;
548
549    // calculate frameset height based on actual content height to eliminate scrolling
550    bool out = false;
551    for (int r = 0; r < rows && !out; r++) {
552        int extra = 0;
553        int height = m_rows.m_sizes[r];
554
555        for (int c = 0; c < cols; c++) {
556            IntRect oldFrameRect = pixelSnappedIntRect(child->frameRect());
557
558            int width = m_cols.m_sizes[c];
559
560            bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed();
561            bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed();
562
563            // has to be resized and itself resize its contents
564            if (!fixedWidth)
565                child->setWidth(width ? width + extra / (cols - c) : 0);
566            else
567                child->setWidth(width);
568            child->setHeight(height);
569
570            child->setNeedsLayout(true);
571
572            if (child->isFrameSet())
573                toRenderFrameSet(child)->layout();
574            else
575                toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight);
576
577            if (child->height() > m_rows.m_sizes[r])
578                m_rows.m_sizes[r] = child->height();
579            if (child->width() > m_cols.m_sizes[c])
580                m_cols.m_sizes[c] = child->width();
581
582            if (child->frameRect() != oldFrameRect)
583                repaintNeeded = true;
584
585            // difference between calculated frame width and the width it actually decides to have
586            extra += width - m_cols.m_sizes[c];
587
588            child = child->nextSiblingBox();
589            if (!child) {
590                out = true;
591                break;
592            }
593        }
594    }
595
596    int xPos = 0;
597    int yPos = 0;
598    out = false;
599    child = firstChildBox();
600    for (int r = 0; r < rows && !out; r++) {
601        xPos = 0;
602        for (int c = 0; c < cols; c++) {
603            // ensure the rows and columns are filled
604            IntRect oldRect = pixelSnappedIntRect(child->frameRect());
605
606            child->setLocation(IntPoint(xPos, yPos));
607            child->setHeight(m_rows.m_sizes[r]);
608            child->setWidth(m_cols.m_sizes[c]);
609
610            if (child->frameRect() != oldRect) {
611                repaintNeeded = true;
612
613                // update to final size
614                child->setNeedsLayout(true);
615                if (child->isFrameSet())
616                    toRenderFrameSet(child)->layout();
617                else
618                    toRenderFrame(child)->layoutWithFlattening(true, true);
619            }
620
621            xPos += m_cols.m_sizes[c] + borderThickness;
622            child = child->nextSiblingBox();
623            if (!child) {
624                out = true;
625                break;
626            }
627        }
628        yPos += m_rows.m_sizes[r] + borderThickness;
629    }
630
631    setWidth(xPos - borderThickness);
632    setHeight(yPos - borderThickness);
633
634    if (repaintNeeded)
635        repaint();
636
637    // all the remaining frames are hidden to avoid ugly spurious unflowed frames
638    for (; child; child = child->nextSiblingBox()) {
639        child->setWidth(0);
640        child->setHeight(0);
641        child->setNeedsLayout(false);
642    }
643}
644
645bool RenderFrameSet::flattenFrameSet() const
646{
647    return frame() && frame()->settings() && frame()->settings()->frameFlatteningEnabled();
648}
649
650void RenderFrameSet::startResizing(GridAxis& axis, int position)
651{
652    int split = hitTestSplit(axis, position);
653    if (split == noSplit || axis.m_preventResize[split]) {
654        axis.m_splitBeingResized = noSplit;
655        return;
656    }
657    axis.m_splitBeingResized = split;
658    axis.m_splitResizeOffset = position - splitPosition(axis, split);
659}
660
661void RenderFrameSet::continueResizing(GridAxis& axis, int position)
662{
663    if (needsLayout())
664        return;
665    if (axis.m_splitBeingResized == noSplit)
666        return;
667    int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
668    int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
669    if (!delta)
670        return;
671    axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
672    axis.m_deltas[axis.m_splitBeingResized] -= delta;
673    setNeedsLayout(true);
674}
675
676bool RenderFrameSet::userResize(MouseEvent* evt)
677{
678    if (flattenFrameSet())
679        return false;
680
681    if (!m_isResizing) {
682        if (needsLayout())
683            return false;
684        if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) {
685            FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
686            startResizing(m_cols, localPos.x());
687            startResizing(m_rows, localPos.y());
688            if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
689                setIsResizing(true);
690                return true;
691            }
692        }
693    } else {
694        if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) {
695            FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms);
696            continueResizing(m_cols, localPos.x());
697            continueResizing(m_rows, localPos.y());
698            if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) {
699                setIsResizing(false);
700                return true;
701            }
702        }
703    }
704
705    return false;
706}
707
708void RenderFrameSet::setIsResizing(bool isResizing)
709{
710    m_isResizing = isResizing;
711    for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
712        if (ancestor->isFrameSet())
713            toRenderFrameSet(ancestor)->m_isChildResizing = isResizing;
714    }
715    if (Frame* frame = this->frame())
716        frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0);
717}
718
719bool RenderFrameSet::isResizingRow() const
720{
721    return m_isResizing && m_rows.m_splitBeingResized != noSplit;
722}
723
724bool RenderFrameSet::isResizingColumn() const
725{
726    return m_isResizing && m_cols.m_splitBeingResized != noSplit;
727}
728
729bool RenderFrameSet::canResizeRow(const IntPoint& p) const
730{
731    int r = hitTestSplit(m_rows, p.y());
732    return r != noSplit && !m_rows.m_preventResize[r];
733}
734
735bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
736{
737    int c = hitTestSplit(m_cols, p.x());
738    return c != noSplit && !m_cols.m_preventResize[c];
739}
740
741int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
742{
743    if (needsLayout())
744        return 0;
745
746    int borderThickness = frameSet()->border();
747
748    int size = axis.m_sizes.size();
749    if (!size)
750        return 0;
751
752    int position = 0;
753    for (int i = 0; i < split && i < size; ++i)
754        position += axis.m_sizes[i] + borderThickness;
755    return position - borderThickness;
756}
757
758int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
759{
760    if (needsLayout())
761        return noSplit;
762
763    int borderThickness = frameSet()->border();
764    if (borderThickness <= 0)
765        return noSplit;
766
767    size_t size = axis.m_sizes.size();
768    if (!size)
769        return noSplit;
770
771    int splitPosition = axis.m_sizes[0];
772    for (size_t i = 1; i < size; ++i) {
773        if (position >= splitPosition && position < splitPosition + borderThickness)
774            return i;
775        splitPosition += borderThickness + axis.m_sizes[i];
776    }
777    return noSplit;
778}
779
780bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const
781{
782    return child->isFrame() || child->isFrameSet();
783}
784
785CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
786{
787    IntPoint roundedPoint = roundedIntPoint(point);
788    if (canResizeRow(roundedPoint)) {
789        cursor = rowResizeCursor();
790        return SetCursor;
791    }
792    if (canResizeColumn(roundedPoint)) {
793        cursor = columnResizeCursor();
794        return SetCursor;
795    }
796    return RenderBox::getCursor(point, cursor);
797}
798
799} // namespace WebCore
800