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