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
47namespace WebCore {
48
49WTF_MAKE_ISO_ALLOCATED_IMPL(RenderFrameSet);
50
51RenderFrameSet::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
59RenderFrameSet::~RenderFrameSet() = default;
60
61HTMLFrameSetElement& RenderFrameSet::frameSetElement() const
62{
63 return downcast<HTMLFrameSetElement>(nodeForNonAnonymous());
64}
65
66RenderFrameSet::GridAxis::GridAxis()
67 : m_splitBeingResized(noSplit)
68{
69}
70
71static const Color& borderStartEdgeColor()
72{
73 static NeverDestroyed<Color> color(170, 170, 170);
74 return color;
75}
76
77static const Color& borderEndEdgeColor()
78{
79 static NeverDestroyed<Color> color = Color::black;
80 return color;
81}
82
83static const Color& borderFillColor()
84{
85 static NeverDestroyed<Color> color(208, 208, 208);
86 return color;
87}
88
89void 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
108void 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
127void 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;
143 for (size_t r = 0; r < rows; r++) {
144 LayoutUnit xPos;
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
164void 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
177void 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
368void 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
377void 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
398void 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
426FrameEdgeInfo 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
446void 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
497static void resetFrameRendererAndDescendents(RenderBox* frameSetChild, RenderFrameSet& parentFrameSet)
498{
499 if (!frameSetChild)
500 return;
501
502 for (auto* descendant = frameSetChild; descendant; descendant = downcast<RenderBox>(RenderObjectTraversal::next(*descendant, &parentFrameSet))) {
503 descendant->setWidth(0);
504 descendant->setHeight(0);
505 descendant->clearNeedsLayout();
506 }
507}
508
509void RenderFrameSet::positionFrames()
510{
511 RenderBox* child = firstChildBox();
512 if (!child)
513 return;
514
515 int rows = frameSetElement().totalRows();
516 int cols = frameSetElement().totalCols();
517
518 int yPos = 0;
519 int borderThickness = frameSetElement().border();
520 for (int r = 0; r < rows; r++) {
521 int xPos = 0;
522 int height = m_rows.m_sizes[r];
523 for (int c = 0; c < cols; c++) {
524 child->setLocation(IntPoint(xPos, yPos));
525 int width = m_cols.m_sizes[c];
526
527 // has to be resized and itself resize its contents
528 if (width != child->width() || height != child->height()) {
529 child->setWidth(width);
530 child->setHeight(height);
531#if PLATFORM(IOS_FAMILY)
532 // FIXME: Is this iOS-specific?
533 child->setNeedsLayout(MarkOnlyThis);
534#else
535 child->setNeedsLayout();
536#endif
537 child->layout();
538 }
539
540 xPos += width + borderThickness;
541
542 child = child->nextSiblingBox();
543 if (!child)
544 return;
545 }
546 yPos += height + borderThickness;
547 }
548
549 resetFrameRendererAndDescendents(child, *this);
550}
551
552void RenderFrameSet::positionFramesWithFlattening()
553{
554 RenderBox* child = firstChildBox();
555 if (!child)
556 return;
557
558 int rows = frameSetElement().totalRows();
559 int cols = frameSetElement().totalCols();
560
561 int borderThickness = frameSetElement().border();
562 bool repaintNeeded = false;
563
564 // calculate frameset height based on actual content height to eliminate scrolling
565 bool out = false;
566 for (int r = 0; r < rows && !out; ++r) {
567 int extra = 0;
568 int height = m_rows.m_sizes[r];
569
570 for (int c = 0; c < cols; ++c) {
571 IntRect oldFrameRect = snappedIntRect(child->frameRect());
572
573 int width = m_cols.m_sizes[c];
574
575 bool fixedWidth = frameSetElement().colLengths() && frameSetElement().colLengths()[c].isFixed();
576 bool fixedHeight = frameSetElement().rowLengths() && frameSetElement().rowLengths()[r].isFixed();
577
578 // has to be resized and itself resize its contents
579 if (!fixedWidth)
580 child->setWidth(width ? width + extra / (cols - c) : 0);
581 else
582 child->setWidth(width);
583 child->setHeight(height);
584
585 child->setNeedsLayout();
586
587 if (is<RenderFrameSet>(*child))
588 downcast<RenderFrameSet>(*child).layout();
589 else
590 downcast<RenderFrame>(*child).layoutWithFlattening(fixedWidth, fixedHeight);
591
592 if (child->height() > m_rows.m_sizes[r])
593 m_rows.m_sizes[r] = child->height();
594 if (child->width() > m_cols.m_sizes[c])
595 m_cols.m_sizes[c] = child->width();
596
597 if (child->frameRect() != oldFrameRect)
598 repaintNeeded = true;
599
600 // difference between calculated frame width and the width it actually decides to have
601 extra += width - m_cols.m_sizes[c];
602
603 child = child->nextSiblingBox();
604 if (!child) {
605 out = true;
606 break;
607 }
608 }
609 }
610
611 int xPos = 0;
612 int yPos = 0;
613 out = false;
614 child = firstChildBox();
615 for (int r = 0; r < rows && !out; ++r) {
616 xPos = 0;
617 for (int c = 0; c < cols; ++c) {
618 // ensure the rows and columns are filled
619 IntRect oldRect = snappedIntRect(child->frameRect());
620
621 child->setLocation(IntPoint(xPos, yPos));
622 child->setHeight(m_rows.m_sizes[r]);
623 child->setWidth(m_cols.m_sizes[c]);
624
625 if (child->frameRect() != oldRect) {
626 repaintNeeded = true;
627
628 // update to final size
629 child->setNeedsLayout();
630 if (is<RenderFrameSet>(*child))
631 downcast<RenderFrameSet>(*child).layout();
632 else
633 downcast<RenderFrame>(*child).layoutWithFlattening(true, true);
634 }
635
636 xPos += m_cols.m_sizes[c] + borderThickness;
637 child = child->nextSiblingBox();
638 if (!child) {
639 out = true;
640 break;
641 }
642 }
643 yPos += m_rows.m_sizes[r] + borderThickness;
644 }
645
646 setWidth(xPos - borderThickness);
647 setHeight(yPos - borderThickness);
648
649 if (repaintNeeded)
650 repaint();
651
652 resetFrameRendererAndDescendents(child, *this);
653}
654
655bool RenderFrameSet::flattenFrameSet() const
656{
657 return view().frameView().effectiveFrameFlattening() != FrameFlattening::Disabled;
658}
659
660void RenderFrameSet::startResizing(GridAxis& axis, int position)
661{
662 int split = hitTestSplit(axis, position);
663 if (split == noSplit || axis.m_preventResize[split]) {
664 axis.m_splitBeingResized = noSplit;
665 return;
666 }
667 axis.m_splitBeingResized = split;
668 axis.m_splitResizeOffset = position - splitPosition(axis, split);
669}
670
671void RenderFrameSet::continueResizing(GridAxis& axis, int position)
672{
673 if (needsLayout())
674 return;
675 if (axis.m_splitBeingResized == noSplit)
676 return;
677 int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
678 int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
679 if (!delta)
680 return;
681 axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
682 axis.m_deltas[axis.m_splitBeingResized] -= delta;
683 setNeedsLayout();
684}
685
686bool RenderFrameSet::userResize(MouseEvent& event)
687{
688 if (flattenFrameSet())
689 return false;
690
691 if (!m_isResizing) {
692 if (needsLayout())
693 return false;
694 if (event.type() == eventNames().mousedownEvent && event.button() == LeftButton) {
695 FloatPoint localPos = absoluteToLocal(event.absoluteLocation(), UseTransforms);
696 startResizing(m_cols, localPos.x());
697 startResizing(m_rows, localPos.y());
698 if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
699 setIsResizing(true);
700 return true;
701 }
702 }
703 } else {
704 if (event.type() == eventNames().mousemoveEvent || (event.type() == eventNames().mouseupEvent && event.button() == LeftButton)) {
705 FloatPoint localPos = absoluteToLocal(event.absoluteLocation(), UseTransforms);
706 continueResizing(m_cols, localPos.x());
707 continueResizing(m_rows, localPos.y());
708 if (event.type() == eventNames().mouseupEvent && event.button() == LeftButton) {
709 setIsResizing(false);
710 return true;
711 }
712 }
713 }
714
715 return false;
716}
717
718void RenderFrameSet::setIsResizing(bool isResizing)
719{
720 m_isResizing = isResizing;
721 for (auto& ancestor : ancestorsOfType<RenderFrameSet>(*this))
722 ancestor.m_isChildResizing = isResizing;
723 frame().eventHandler().setResizingFrameSet(isResizing ? &frameSetElement() : nullptr);
724}
725
726bool RenderFrameSet::isResizingRow() const
727{
728 return m_isResizing && m_rows.m_splitBeingResized != noSplit;
729}
730
731bool RenderFrameSet::isResizingColumn() const
732{
733 return m_isResizing && m_cols.m_splitBeingResized != noSplit;
734}
735
736bool RenderFrameSet::canResizeRow(const IntPoint& p) const
737{
738 int r = hitTestSplit(m_rows, p.y());
739 return r != noSplit && !m_rows.m_preventResize[r];
740}
741
742bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
743{
744 int c = hitTestSplit(m_cols, p.x());
745 return c != noSplit && !m_cols.m_preventResize[c];
746}
747
748int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
749{
750 if (needsLayout())
751 return 0;
752
753 int borderThickness = frameSetElement().border();
754
755 int size = axis.m_sizes.size();
756 if (!size)
757 return 0;
758
759 int position = 0;
760 for (int i = 0; i < split && i < size; ++i)
761 position += axis.m_sizes[i] + borderThickness;
762 return position - borderThickness;
763}
764
765int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
766{
767 if (needsLayout())
768 return noSplit;
769
770 int borderThickness = frameSetElement().border();
771 if (borderThickness <= 0)
772 return noSplit;
773
774 size_t size = axis.m_sizes.size();
775 if (!size)
776 return noSplit;
777
778 int splitPosition = axis.m_sizes[0];
779 for (size_t i = 1; i < size; ++i) {
780 if (position >= splitPosition && position < splitPosition + borderThickness)
781 return i;
782 splitPosition += borderThickness + axis.m_sizes[i];
783 }
784 return noSplit;
785}
786
787bool RenderFrameSet::isChildAllowed(const RenderObject& child, const RenderStyle&) const
788{
789 return child.isFrame() || child.isFrameSet();
790}
791
792CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
793{
794 IntPoint roundedPoint = roundedIntPoint(point);
795 if (canResizeRow(roundedPoint)) {
796 cursor = rowResizeCursor();
797 return SetCursor;
798 }
799 if (canResizeColumn(roundedPoint)) {
800 cursor = columnResizeCursor();
801 return SetCursor;
802 }
803 return RenderBox::getCursor(point, cursor);
804}
805
806} // namespace WebCore
807