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