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