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