Add WTF::move()
[WebKit-https.git] / Source / WebCore / rendering / RenderFrameSet.cpp
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
46 namespace WebCore {
47
48 RenderFrameSet::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
56 RenderFrameSet::~RenderFrameSet()
57 {
58 }
59
60 HTMLFrameSetElement& RenderFrameSet::frameSetElement() const
61 {
62     return toHTMLFrameSetElement(nodeForNonAnonymous());
63 }
64
65 RenderFrameSet::GridAxis::GridAxis()
66     : m_splitBeingResized(noSplit)
67 {
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     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
105 void 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
125 void 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
162 void 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
175 void 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
366 void 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
375 void 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
396 void 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
424 FrameEdgeInfo 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
444 void 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
495 void 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
543 void 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
651 bool RenderFrameSet::flattenFrameSet() const
652 {
653     return frame().settings().frameFlatteningEnabled();
654 }
655
656 void 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
667 void 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
682 bool 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
714 void 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
722 bool RenderFrameSet::isResizingRow() const
723 {
724     return m_isResizing && m_rows.m_splitBeingResized != noSplit;
725 }
726
727 bool RenderFrameSet::isResizingColumn() const
728 {
729     return m_isResizing && m_cols.m_splitBeingResized != noSplit;
730 }
731
732 bool 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
738 bool 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
744 int 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
761 int 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
783 bool RenderFrameSet::isChildAllowed(const RenderObject& child, const RenderStyle&) const
784 {
785     return child.isFrame() || child.isFrameSet();
786 }
787
788 CursorDirective 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