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