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