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