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