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