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