Use "= default" to denote default constructor or destructor
[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, 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() = default;
57
58 HTMLFrameSetElement& RenderFrameSet::frameSetElement() const
59 {
60     return downcast<HTMLFrameSetElement>(nodeForNonAnonymous());
61 }
62
63 RenderFrameSet::GridAxis::GridAxis()
64     : m_splitBeingResized(noSplit)
65 {
66 }
67
68 static const Color& borderStartEdgeColor()
69 {
70     static NeverDestroyed<Color> color(170, 170, 170);
71     return color;
72 }
73
74 static const Color& borderEndEdgeColor()
75 {
76     static NeverDestroyed<Color> color = Color::black;
77     return color;
78 }
79
80 static const Color& borderFillColor()
81 {
82     static NeverDestroyed<Color> color(208, 208, 208);
83     return color;
84 }
85
86 void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
87 {
88     if (!paintInfo.rect.intersects(borderRect))
89         return;
90         
91     // FIXME: We should do something clever when borders from distinct framesets meet at a join.
92     
93     // Fill first.
94     GraphicsContext& context = paintInfo.context();
95     context.fillRect(borderRect, frameSetElement().hasBorderColor() ? style().visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor());
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());
101         context.fillRect(IntRect(IntPoint(borderRect.maxX() - 1, borderRect.y()), IntSize(1, height())), borderEndEdgeColor());
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     context.fillRect(borderRect, frameSetElement().hasBorderColor() ? style().visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor());
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());
120         context.fillRect(IntRect(IntPoint(borderRect.x(), borderRect.maxY() - 1), IntSize(width(), 1)), borderEndEdgeColor());
121     }
122 }
123
124 void 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 = frameSetElement().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             downcast<RenderElement>(*child).paint(paintInfo, adjustedPaintOffset);
144             xPos += m_cols.m_sizes[c];
145             if (borderThickness && m_cols.m_allowBorder[c + 1]) {
146                 paintColumnBorder(paintInfo, snappedIntRect(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, snappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness)));
156             yPos += borderThickness;
157         }
158     }
159 }
160
161 void 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
174 void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
175 {
176     availableLen = std::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] = std::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].isPercentOrCalculated()) {
209             gridLayout[i] = std::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 += std::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].isPercentOrCalculated()) {
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] = (std::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].isPercentOrCalculated()) {
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].isPercentOrCalculated()) {
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
365 void 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
374 void 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
395 void RenderFrameSet::computeEdgeInfo()
396 {
397     m_rows.m_preventResize.fill(frameSetElement().noResize());
398     m_rows.m_allowBorder.fill(false);
399     m_cols.m_preventResize.fill(frameSetElement().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 (is<RenderFrameSet>(*child))
412                 edgeInfo = downcast<RenderFrameSet>(*child).edgeInfo();
413             else
414                 edgeInfo = downcast<RenderFrame>(*child).edgeInfo();
415             fillFromEdgeInfo(edgeInfo, r, c);
416             child = child->nextSibling();
417             if (!child)
418                 return;
419         }
420     }
421 }
422
423 FrameEdgeInfo RenderFrameSet::edgeInfo() const
424 {
425     FrameEdgeInfo result(frameSetElement().noResize(), true);
426     
427     int rows = frameSetElement().totalRows();
428     int cols = frameSetElement().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
443 void 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 = frameSetElement().totalCols();
462     unsigned rows = frameSetElement().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 = frameSetElement().border();
470     layOutAxis(m_rows, frameSetElement().rowLengths(), height() - (rows - 1) * borderThickness);
471     layOutAxis(m_cols, frameSetElement().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, snappedIntRect(oldBounds));
486         LayoutRect newBounds = clippedOverflowRectForRepaint(repaintContainer);
487         if (newBounds != oldBounds)
488             repaintUsingContainer(repaintContainer, snappedIntRect(newBounds));
489     }
490
491     clearNeedsLayout();
492 }
493
494 void RenderFrameSet::positionFrames()
495 {
496     RenderBox* child = firstChildBox();
497     if (!child)
498         return;
499
500     int rows = frameSetElement().totalRows();
501     int cols = frameSetElement().totalCols();
502
503     int yPos = 0;
504     int borderThickness = frameSetElement().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 #if PLATFORM(IOS)
517                 // FIXME: Is this iOS-specific?
518                 child->setNeedsLayout(MarkOnlyThis);
519 #else
520                 child->setNeedsLayout();
521 #endif
522                 child->layout();
523             }
524
525             xPos += width + borderThickness;
526
527             child = child->nextSiblingBox();
528             if (!child)
529                 return;
530         }
531         yPos += height + borderThickness;
532     }
533
534     // all the remaining frames are hidden to avoid ugly spurious unflowed frames
535     for (auto* descendant = child; descendant; descendant = downcast<RenderBox>(RenderObjectTraversal::next(*descendant, this))) {
536         descendant->setWidth(0);
537         descendant->setHeight(0);
538         descendant->clearNeedsLayout();
539     }
540 }
541
542 void RenderFrameSet::positionFramesWithFlattening()
543 {
544     RenderBox* child = firstChildBox();
545     if (!child)
546         return;
547
548     int rows = frameSetElement().totalRows();
549     int cols = frameSetElement().totalCols();
550
551     int borderThickness = frameSetElement().border();
552     bool repaintNeeded = false;
553
554     // calculate frameset height based on actual content height to eliminate scrolling
555     bool out = false;
556     for (int r = 0; r < rows && !out; ++r) {
557         int extra = 0;
558         int height = m_rows.m_sizes[r];
559
560         for (int c = 0; c < cols; ++c) {
561             IntRect oldFrameRect = snappedIntRect(child->frameRect());
562
563             int width = m_cols.m_sizes[c];
564
565             bool fixedWidth = frameSetElement().colLengths() && frameSetElement().colLengths()[c].isFixed();
566             bool fixedHeight = frameSetElement().rowLengths() && frameSetElement().rowLengths()[r].isFixed();
567
568             // has to be resized and itself resize its contents
569             if (!fixedWidth)
570                 child->setWidth(width ? width + extra / (cols - c) : 0);
571             else
572                 child->setWidth(width);
573             child->setHeight(height);
574
575             child->setNeedsLayout();
576
577             if (is<RenderFrameSet>(*child))
578                 downcast<RenderFrameSet>(*child).layout();
579             else
580                 downcast<RenderFrame>(*child).layoutWithFlattening(fixedWidth, fixedHeight);
581
582             if (child->height() > m_rows.m_sizes[r])
583                 m_rows.m_sizes[r] = child->height();
584             if (child->width() > m_cols.m_sizes[c])
585                 m_cols.m_sizes[c] = child->width();
586
587             if (child->frameRect() != oldFrameRect)
588                 repaintNeeded = true;
589
590             // difference between calculated frame width and the width it actually decides to have
591             extra += width - m_cols.m_sizes[c];
592
593             child = child->nextSiblingBox();
594             if (!child) {
595                 out = true;
596                 break;
597             }
598         }
599     }
600
601     int xPos = 0;
602     int yPos = 0;
603     out = false;
604     child = firstChildBox();
605     for (int r = 0; r < rows && !out; ++r) {
606         xPos = 0;
607         for (int c = 0; c < cols; ++c) {
608             // ensure the rows and columns are filled
609             IntRect oldRect = snappedIntRect(child->frameRect());
610
611             child->setLocation(IntPoint(xPos, yPos));
612             child->setHeight(m_rows.m_sizes[r]);
613             child->setWidth(m_cols.m_sizes[c]);
614
615             if (child->frameRect() != oldRect) {
616                 repaintNeeded = true;
617
618                 // update to final size
619                 child->setNeedsLayout();
620                 if (is<RenderFrameSet>(*child))
621                     downcast<RenderFrameSet>(*child).layout();
622                 else
623                     downcast<RenderFrame>(*child).layoutWithFlattening(true, true);
624             }
625
626             xPos += m_cols.m_sizes[c] + borderThickness;
627             child = child->nextSiblingBox();
628             if (!child) {
629                 out = true;
630                 break;
631             }
632         }
633         yPos += m_rows.m_sizes[r] + borderThickness;
634     }
635
636     setWidth(xPos - borderThickness);
637     setHeight(yPos - borderThickness);
638
639     if (repaintNeeded)
640         repaint();
641
642     // all the remaining frames are hidden to avoid ugly spurious unflowed frames
643     for (; child; child = child->nextSiblingBox()) {
644         child->setWidth(0);
645         child->setHeight(0);
646         child->clearNeedsLayout();
647     }
648 }
649
650 bool RenderFrameSet::flattenFrameSet() const
651 {
652     return view().frameView().effectiveFrameFlattening() != FrameFlattening::Disabled;
653 }
654
655 void RenderFrameSet::startResizing(GridAxis& axis, int position)
656 {
657     int split = hitTestSplit(axis, position);
658     if (split == noSplit || axis.m_preventResize[split]) {
659         axis.m_splitBeingResized = noSplit;
660         return;
661     }
662     axis.m_splitBeingResized = split;
663     axis.m_splitResizeOffset = position - splitPosition(axis, split);
664 }
665
666 void RenderFrameSet::continueResizing(GridAxis& axis, int position)
667 {
668     if (needsLayout())
669         return;
670     if (axis.m_splitBeingResized == noSplit)
671         return;
672     int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
673     int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
674     if (!delta)
675         return;
676     axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
677     axis.m_deltas[axis.m_splitBeingResized] -= delta;
678     setNeedsLayout();
679 }
680
681 bool RenderFrameSet::userResize(MouseEvent& event)
682 {
683     if (flattenFrameSet())
684         return false;
685
686     if (!m_isResizing) {
687         if (needsLayout())
688             return false;
689         if (event.type() == eventNames().mousedownEvent && event.button() == LeftButton) {
690             FloatPoint localPos = absoluteToLocal(event.absoluteLocation(), UseTransforms);
691             startResizing(m_cols, localPos.x());
692             startResizing(m_rows, localPos.y());
693             if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
694                 setIsResizing(true);
695                 return true;
696             }
697         }
698     } else {
699         if (event.type() == eventNames().mousemoveEvent || (event.type() == eventNames().mouseupEvent && event.button() == LeftButton)) {
700             FloatPoint localPos = absoluteToLocal(event.absoluteLocation(), UseTransforms);
701             continueResizing(m_cols, localPos.x());
702             continueResizing(m_rows, localPos.y());
703             if (event.type() == eventNames().mouseupEvent && event.button() == LeftButton) {
704                 setIsResizing(false);
705                 return true;
706             }
707         }
708     }
709
710     return false;
711 }
712
713 void RenderFrameSet::setIsResizing(bool isResizing)
714 {
715     m_isResizing = isResizing;
716     for (auto& ancestor : ancestorsOfType<RenderFrameSet>(*this))
717         ancestor.m_isChildResizing = isResizing;
718     frame().eventHandler().setResizingFrameSet(isResizing ? &frameSetElement() : nullptr);
719 }
720
721 bool RenderFrameSet::isResizingRow() const
722 {
723     return m_isResizing && m_rows.m_splitBeingResized != noSplit;
724 }
725
726 bool RenderFrameSet::isResizingColumn() const
727 {
728     return m_isResizing && m_cols.m_splitBeingResized != noSplit;
729 }
730
731 bool RenderFrameSet::canResizeRow(const IntPoint& p) const
732 {
733     int r = hitTestSplit(m_rows, p.y());
734     return r != noSplit && !m_rows.m_preventResize[r];
735 }
736
737 bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
738 {
739     int c = hitTestSplit(m_cols, p.x());
740     return c != noSplit && !m_cols.m_preventResize[c];
741 }
742
743 int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
744 {
745     if (needsLayout())
746         return 0;
747
748     int borderThickness = frameSetElement().border();
749
750     int size = axis.m_sizes.size();
751     if (!size)
752         return 0;
753
754     int position = 0;
755     for (int i = 0; i < split && i < size; ++i)
756         position += axis.m_sizes[i] + borderThickness;
757     return position - borderThickness;
758 }
759
760 int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
761 {
762     if (needsLayout())
763         return noSplit;
764
765     int borderThickness = frameSetElement().border();
766     if (borderThickness <= 0)
767         return noSplit;
768
769     size_t size = axis.m_sizes.size();
770     if (!size)
771         return noSplit;
772
773     int splitPosition = axis.m_sizes[0];
774     for (size_t i = 1; i < size; ++i) {
775         if (position >= splitPosition && position < splitPosition + borderThickness)
776             return i;
777         splitPosition += borderThickness + axis.m_sizes[i];
778     }
779     return noSplit;
780 }
781
782 bool RenderFrameSet::isChildAllowed(const RenderObject& child, const RenderStyle&) const
783 {
784     return child.isFrame() || child.isFrameSet();
785 }
786
787 CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
788 {
789     IntPoint roundedPoint = roundedIntPoint(point);
790     if (canResizeRow(roundedPoint)) {
791         cursor = rowResizeCursor();
792         return SetCursor;
793     }
794     if (canResizeColumn(roundedPoint)) {
795         cursor = columnResizeCursor();
796         return SetCursor;
797     }
798     return RenderBox::getCursor(point, cursor);
799 }
800
801 } // namespace WebCore