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