• 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 "PaintInfo.h"
38 #include "RenderFrame.h"
39 #include "RenderView.h"
40 #include "Settings.h"
41 
42 namespace WebCore {
43 
RenderFrameSet(HTMLFrameSetElement * frameSet)44 RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
45     : RenderBox(frameSet)
46     , m_isResizing(false)
47     , m_isChildResizing(false)
48 #ifdef ANDROID_FLATTEN_FRAMESET
49     , m_gridCalculated(false)
50 #endif
51 {
52     setInline(false);
53 }
54 
~RenderFrameSet()55 RenderFrameSet::~RenderFrameSet()
56 {
57 }
58 
GridAxis()59 RenderFrameSet::GridAxis::GridAxis()
60     : m_splitBeingResized(noSplit)
61 {
62 }
63 
frameSet() const64 inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
65 {
66     return static_cast<HTMLFrameSetElement*>(node());
67 }
68 
borderStartEdgeColor()69 static Color borderStartEdgeColor()
70 {
71     return Color(170, 170, 170);
72 }
73 
borderEndEdgeColor()74 static Color borderEndEdgeColor()
75 {
76     return Color::black;
77 }
78 
borderFillColor()79 static Color borderFillColor()
80 {
81     return Color(208, 208, 208);
82 }
83 
paintColumnBorder(const PaintInfo & paintInfo,const IntRect & borderRect)84 void 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 
paintRowBorder(const PaintInfo & paintInfo,const IntRect & borderRect)104 void 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 
paint(PaintInfo & paintInfo,int tx,int ty)124 void 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 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction action)163 bool 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 
resize(int size)181 void 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 
layOutAxis(GridAxis & axis,const Length * grid,int availableLen)194 void 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 
fillFromEdgeInfo(const FrameEdgeInfo & edgeInfo,int r,int c)386 void 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 
computeEdgeInfo()407 void 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 
edgeInfo() const435 FrameEdgeInfo 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 
layout()455 void 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 
positionFrames()520 void 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 
positionFramesWithFlattening()643 void 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 
flattenFrameSet() const751 bool RenderFrameSet::flattenFrameSet() const
752 {
753     return frame() && frame()->settings()->frameFlatteningEnabled();
754 }
755 
startResizing(GridAxis & axis,int position)756 void 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 
continueResizing(GridAxis & axis,int position)767 void 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 
userResize(MouseEvent * evt)782 bool 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 
setIsResizing(bool isResizing)814 void 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 
isResizingRow() const825 bool RenderFrameSet::isResizingRow() const
826 {
827     return m_isResizing && m_rows.m_splitBeingResized != noSplit;
828 }
829 
isResizingColumn() const830 bool RenderFrameSet::isResizingColumn() const
831 {
832     return m_isResizing && m_cols.m_splitBeingResized != noSplit;
833 }
834 
canResizeRow(const IntPoint & p) const835 bool 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 
canResizeColumn(const IntPoint & p) const841 bool 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 
splitPosition(const GridAxis & axis,int split) const847 int 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 
hitTestSplit(const GridAxis & axis,int position) const864 int 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 
isChildAllowed(RenderObject * child,RenderStyle *) const886 bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const
887 {
888     return child->isFrame() || child->isFrameSet();
889 }
890 
891 } // namespace WebCore
892