1 /** 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2000 Simon Hausmann <hausmann (at) kde.org> 4 * (C) 2000 Stefan Schimanski (1Stein (at) 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 "core/rendering/RenderFrameSet.h" 26 27 #include "core/dom/Document.h" 28 #include "core/events/MouseEvent.h" 29 #include "core/frame/LocalFrame.h" 30 #include "core/html/HTMLDimension.h" 31 #include "core/html/HTMLFrameSetElement.h" 32 #include "core/page/EventHandler.h" 33 #include "core/rendering/GraphicsContextAnnotator.h" 34 #include "core/rendering/PaintInfo.h" 35 #include "core/rendering/RenderFrame.h" 36 #include "core/rendering/RenderView.h" 37 #include "platform/Cursor.h" 38 #include "platform/graphics/GraphicsContext.h" 39 40 namespace blink { 41 42 RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet) 43 : RenderBox(frameSet) 44 , m_isResizing(false) 45 , m_isChildResizing(false) 46 { 47 setInline(false); 48 } 49 50 RenderFrameSet::~RenderFrameSet() 51 { 52 } 53 54 void RenderFrameSet::trace(Visitor* visitor) 55 { 56 visitor->trace(m_children); 57 RenderBox::trace(visitor); 58 } 59 60 RenderFrameSet::GridAxis::GridAxis() 61 : m_splitBeingResized(noSplit) 62 { 63 } 64 65 inline HTMLFrameSetElement* RenderFrameSet::frameSet() const 66 { 67 return toHTMLFrameSetElement(node()); 68 } 69 70 static Color borderStartEdgeColor() 71 { 72 return Color(170, 170, 170); 73 } 74 75 static Color borderEndEdgeColor() 76 { 77 return Color::black; 78 } 79 80 static Color borderFillColor() 81 { 82 return Color(208, 208, 208); 83 } 84 85 void 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 context->fillRect(borderRect, frameSet()->hasBorderColor() ? resolveColor(CSSPropertyBorderLeftColor) : borderFillColor()); 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()); 100 context->fillRect(IntRect(IntPoint(borderRect.maxX() - 1, borderRect.y()), IntSize(1, height())), borderEndEdgeColor()); 101 } 102 } 103 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 context->fillRect(borderRect, frameSet()->hasBorderColor() ? resolveColor(CSSPropertyBorderLeftColor) : borderFillColor()); 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.location(), IntSize(width(), 1)), borderStartEdgeColor()); 119 context->fillRect(IntRect(IntPoint(borderRect.x(), borderRect.maxY() - 1), IntSize(width(), 1)), borderEndEdgeColor()); 120 } 121 } 122 123 void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 124 { 125 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); 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 = frameSet()->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 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 162 void RenderFrameSet::computePreferredLogicalWidths() 163 { 164 m_minPreferredLogicalWidth = 0; 165 m_maxPreferredLogicalWidth = 0; 166 clearPreferredLogicalWidthsDirty(); 167 } 168 169 void RenderFrameSet::GridAxis::resize(int size) 170 { 171 m_sizes.resize(size); 172 m_deltas.resize(size); 173 m_deltas.fill(0); 174 175 // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset 176 // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about 177 // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info. 178 m_preventResize.resize(size + 1); 179 m_allowBorder.resize(size + 1); 180 } 181 182 void RenderFrameSet::layOutAxis(GridAxis& axis, const Vector<HTMLDimension>& grid, int availableLen) 183 { 184 availableLen = max(availableLen, 0); 185 186 int* gridLayout = axis.m_sizes.data(); 187 188 if (grid.isEmpty()) { 189 gridLayout[0] = availableLen; 190 return; 191 } 192 193 int gridLen = axis.m_sizes.size(); 194 ASSERT(gridLen); 195 196 int totalRelative = 0; 197 int totalFixed = 0; 198 int totalPercent = 0; 199 int countRelative = 0; 200 int countFixed = 0; 201 int countPercent = 0; 202 203 // First we need to investigate how many columns of each type we have and 204 // how much space these columns are going to require. 205 for (int i = 0; i < gridLen; ++i) { 206 // Count the total length of all of the fixed columns/rows -> totalFixed 207 // Count the number of columns/rows which are fixed -> countFixed 208 if (grid[i].isAbsolute()) { 209 gridLayout[i] = max<int>(grid[i].value(), 0); 210 totalFixed += gridLayout[i]; 211 countFixed++; 212 } 213 214 // Count the total percentage of all of the percentage columns/rows -> totalPercent 215 // Count the number of columns/rows which are percentages -> countPercent 216 if (grid[i].isPercentage()) { 217 gridLayout[i] = max<int>(grid[i].value() * availableLen / 100., 0); 218 totalPercent += gridLayout[i]; 219 countPercent++; 220 } 221 222 // Count the total relative of all the relative columns/rows -> totalRelative 223 // Count the number of columns/rows which are relative -> countRelative 224 if (grid[i].isRelative()) { 225 totalRelative += max<int>(grid[i].value(), 1); 226 countRelative++; 227 } 228 } 229 230 int remainingLen = availableLen; 231 232 // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed 233 // columns/rows we need to proportionally adjust their size. 234 if (totalFixed > remainingLen) { 235 int remainingFixed = remainingLen; 236 237 for (int i = 0; i < gridLen; ++i) { 238 if (grid[i].isAbsolute()) { 239 gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed; 240 remainingLen -= gridLayout[i]; 241 } 242 } 243 } else 244 remainingLen -= totalFixed; 245 246 // Percentage columns/rows are our second priority. Divide the remaining space proportionally 247 // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative 248 // to 100%, but to the total percentage. For example, if there are three columns, each of 75%, 249 // and the available space is 300px, each column will become 100px in width. 250 if (totalPercent > remainingLen) { 251 int remainingPercent = remainingLen; 252 253 for (int i = 0; i < gridLen; ++i) { 254 if (grid[i].isPercentage()) { 255 gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent; 256 remainingLen -= gridLayout[i]; 257 } 258 } 259 } else 260 remainingLen -= totalPercent; 261 262 // Relative columns/rows are our last priority. Divide the remaining space proportionally 263 // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*. 264 if (countRelative) { 265 int lastRelative = 0; 266 int remainingRelative = remainingLen; 267 268 for (int i = 0; i < gridLen; ++i) { 269 if (grid[i].isRelative()) { 270 gridLayout[i] = (max(grid[i].value(), 1.) * remainingRelative) / totalRelative; 271 remainingLen -= gridLayout[i]; 272 lastRelative = i; 273 } 274 } 275 276 // If we could not evenly distribute the available space of all of the relative 277 // columns/rows, the remainder will be added to the last column/row. 278 // For example: if we have a space of 100px and three columns (*,*,*), the remainder will 279 // be 1px and will be added to the last column: 33px, 33px, 34px. 280 if (remainingLen) { 281 gridLayout[lastRelative] += remainingLen; 282 remainingLen = 0; 283 } 284 } 285 286 // If we still have some left over space we need to divide it over the already existing 287 // columns/rows 288 if (remainingLen) { 289 // Our first priority is to spread if over the percentage columns. The remaining 290 // space is spread evenly, for example: if we have a space of 100px, the columns 291 // definition of 25%,25% used to result in two columns of 25px. After this the 292 // columns will each be 50px in width. 293 if (countPercent && totalPercent) { 294 int remainingPercent = remainingLen; 295 int changePercent = 0; 296 297 for (int i = 0; i < gridLen; ++i) { 298 if (grid[i].isPercentage()) { 299 changePercent = (remainingPercent * gridLayout[i]) / totalPercent; 300 gridLayout[i] += changePercent; 301 remainingLen -= changePercent; 302 } 303 } 304 } else if (totalFixed) { 305 // Our last priority is to spread the remaining space over the fixed columns. 306 // For example if we have 100px of space and two column of each 40px, both 307 // columns will become exactly 50px. 308 int remainingFixed = remainingLen; 309 int changeFixed = 0; 310 311 for (int i = 0; i < gridLen; ++i) { 312 if (grid[i].isAbsolute()) { 313 changeFixed = (remainingFixed * gridLayout[i]) / totalFixed; 314 gridLayout[i] += changeFixed; 315 remainingLen -= changeFixed; 316 } 317 } 318 } 319 } 320 321 // If we still have some left over space we probably ended up with a remainder of 322 // a division. We cannot spread it evenly anymore. If we have any percentage 323 // columns/rows simply spread the remainder equally over all available percentage columns, 324 // regardless of their size. 325 if (remainingLen && countPercent) { 326 int remainingPercent = remainingLen; 327 int changePercent = 0; 328 329 for (int i = 0; i < gridLen; ++i) { 330 if (grid[i].isPercentage()) { 331 changePercent = remainingPercent / countPercent; 332 gridLayout[i] += changePercent; 333 remainingLen -= changePercent; 334 } 335 } 336 } else if (remainingLen && countFixed) { 337 // If we don't have any percentage columns/rows we only have 338 // fixed columns. Spread the remainder equally over all fixed 339 // columns/rows. 340 int remainingFixed = remainingLen; 341 int changeFixed = 0; 342 343 for (int i = 0; i < gridLen; ++i) { 344 if (grid[i].isAbsolute()) { 345 changeFixed = remainingFixed / countFixed; 346 gridLayout[i] += changeFixed; 347 remainingLen -= changeFixed; 348 } 349 } 350 } 351 352 // Still some left over. Add it to the last column, because it is impossible 353 // spread it evenly or equally. 354 if (remainingLen) 355 gridLayout[gridLen - 1] += remainingLen; 356 357 // now we have the final layout, distribute the delta over it 358 bool worked = true; 359 int* gridDelta = axis.m_deltas.data(); 360 for (int i = 0; i < gridLen; ++i) { 361 if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0) 362 worked = false; 363 gridLayout[i] += gridDelta[i]; 364 } 365 // if the deltas broke something, undo them 366 if (!worked) { 367 for (int i = 0; i < gridLen; ++i) 368 gridLayout[i] -= gridDelta[i]; 369 axis.m_deltas.fill(0); 370 } 371 } 372 373 void RenderFrameSet::notifyFrameEdgeInfoChanged() 374 { 375 if (needsLayout()) 376 return; 377 // FIXME: We should only recompute the edge info with respect to the frame that changed 378 // and its adjacent frame(s) instead of recomputing the edge info for the entire frameset. 379 computeEdgeInfo(); 380 } 381 382 void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c) 383 { 384 if (edgeInfo.allowBorder(LeftFrameEdge)) 385 m_cols.m_allowBorder[c] = true; 386 if (edgeInfo.allowBorder(RightFrameEdge)) 387 m_cols.m_allowBorder[c + 1] = true; 388 if (edgeInfo.preventResize(LeftFrameEdge)) 389 m_cols.m_preventResize[c] = true; 390 if (edgeInfo.preventResize(RightFrameEdge)) 391 m_cols.m_preventResize[c + 1] = true; 392 393 if (edgeInfo.allowBorder(TopFrameEdge)) 394 m_rows.m_allowBorder[r] = true; 395 if (edgeInfo.allowBorder(BottomFrameEdge)) 396 m_rows.m_allowBorder[r + 1] = true; 397 if (edgeInfo.preventResize(TopFrameEdge)) 398 m_rows.m_preventResize[r] = true; 399 if (edgeInfo.preventResize(BottomFrameEdge)) 400 m_rows.m_preventResize[r + 1] = true; 401 } 402 403 void RenderFrameSet::computeEdgeInfo() 404 { 405 m_rows.m_preventResize.fill(frameSet()->noResize()); 406 m_rows.m_allowBorder.fill(false); 407 m_cols.m_preventResize.fill(frameSet()->noResize()); 408 m_cols.m_allowBorder.fill(false); 409 410 RenderObject* child = firstChild(); 411 if (!child) 412 return; 413 414 size_t rows = m_rows.m_sizes.size(); 415 size_t cols = m_cols.m_sizes.size(); 416 for (size_t r = 0; r < rows; ++r) { 417 for (size_t c = 0; c < cols; ++c) { 418 FrameEdgeInfo edgeInfo; 419 if (child->isFrameSet()) 420 edgeInfo = toRenderFrameSet(child)->edgeInfo(); 421 else 422 edgeInfo = toRenderFrame(child)->edgeInfo(); 423 fillFromEdgeInfo(edgeInfo, r, c); 424 child = child->nextSibling(); 425 if (!child) 426 return; 427 } 428 } 429 } 430 431 FrameEdgeInfo RenderFrameSet::edgeInfo() const 432 { 433 FrameEdgeInfo result(frameSet()->noResize(), true); 434 435 int rows = frameSet()->totalRows(); 436 int cols = frameSet()->totalCols(); 437 if (rows && cols) { 438 result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]); 439 result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]); 440 result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]); 441 result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]); 442 result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]); 443 result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]); 444 result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]); 445 result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]); 446 } 447 448 return result; 449 } 450 451 void RenderFrameSet::layout() 452 { 453 ASSERT(needsLayout()); 454 455 if (!parent()->isFrameSet() && !document().printing()) { 456 setWidth(view()->viewWidth()); 457 setHeight(view()->viewHeight()); 458 } 459 460 unsigned cols = frameSet()->totalCols(); 461 unsigned rows = frameSet()->totalRows(); 462 463 if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) { 464 m_rows.resize(rows); 465 m_cols.resize(cols); 466 } 467 468 LayoutUnit borderThickness = frameSet()->border(); 469 layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness); 470 layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness); 471 472 positionFrames(); 473 474 RenderBox::layout(); 475 476 computeEdgeInfo(); 477 478 updateLayerTransformAfterLayout(); 479 480 clearNeedsLayout(); 481 } 482 483 static void clearNeedsLayoutOnHiddenFrames(RenderBox* frame) 484 { 485 for (; frame; frame = frame->nextSiblingBox()) { 486 frame->setWidth(0); 487 frame->setHeight(0); 488 frame->clearNeedsLayout(); 489 clearNeedsLayoutOnHiddenFrames(frame->firstChildBox()); 490 } 491 } 492 493 void RenderFrameSet::positionFrames() 494 { 495 RenderBox* child = firstChildBox(); 496 if (!child) 497 return; 498 499 int rows = frameSet()->totalRows(); 500 int cols = frameSet()->totalCols(); 501 502 int yPos = 0; 503 int borderThickness = frameSet()->border(); 504 for (int r = 0; r < rows; r++) { 505 int xPos = 0; 506 int height = m_rows.m_sizes[r]; 507 for (int c = 0; c < cols; c++) { 508 child->setLocation(IntPoint(xPos, yPos)); 509 int width = m_cols.m_sizes[c]; 510 511 // has to be resized and itself resize its contents 512 if (width != child->width() || height != child->height()) { 513 child->setWidth(width); 514 child->setHeight(height); 515 child->setNeedsLayoutAndFullPaintInvalidation(); 516 child->layout(); 517 } 518 519 xPos += width + borderThickness; 520 521 child = child->nextSiblingBox(); 522 if (!child) 523 return; 524 } 525 yPos += height + borderThickness; 526 } 527 528 // All the remaining frames are hidden to avoid ugly spurious unflowed frames. 529 clearNeedsLayoutOnHiddenFrames(child); 530 } 531 532 void RenderFrameSet::startResizing(GridAxis& axis, int position) 533 { 534 int split = hitTestSplit(axis, position); 535 if (split == noSplit || axis.m_preventResize[split]) { 536 axis.m_splitBeingResized = noSplit; 537 return; 538 } 539 axis.m_splitBeingResized = split; 540 axis.m_splitResizeOffset = position - splitPosition(axis, split); 541 } 542 543 void RenderFrameSet::continueResizing(GridAxis& axis, int position) 544 { 545 if (needsLayout()) 546 return; 547 if (axis.m_splitBeingResized == noSplit) 548 return; 549 int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized); 550 int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset; 551 if (!delta) 552 return; 553 axis.m_deltas[axis.m_splitBeingResized - 1] += delta; 554 axis.m_deltas[axis.m_splitBeingResized] -= delta; 555 setNeedsLayoutAndFullPaintInvalidation(); 556 } 557 558 bool RenderFrameSet::userResize(MouseEvent* evt) 559 { 560 if (!m_isResizing) { 561 if (needsLayout()) 562 return false; 563 if (evt->type() == EventTypeNames::mousedown && evt->button() == LeftButton) { 564 FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms); 565 startResizing(m_cols, localPos.x()); 566 startResizing(m_rows, localPos.y()); 567 if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) { 568 setIsResizing(true); 569 return true; 570 } 571 } 572 } else { 573 if (evt->type() == EventTypeNames::mousemove || (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton)) { 574 FloatPoint localPos = absoluteToLocal(evt->absoluteLocation(), UseTransforms); 575 continueResizing(m_cols, localPos.x()); 576 continueResizing(m_rows, localPos.y()); 577 if (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton) { 578 setIsResizing(false); 579 return true; 580 } 581 } 582 } 583 584 return false; 585 } 586 587 void RenderFrameSet::setIsResizing(bool isResizing) 588 { 589 m_isResizing = isResizing; 590 for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { 591 if (ancestor->isFrameSet()) 592 toRenderFrameSet(ancestor)->m_isChildResizing = isResizing; 593 } 594 if (LocalFrame* frame = this->frame()) 595 frame->eventHandler().setResizingFrameSet(isResizing ? frameSet() : 0); 596 } 597 598 bool RenderFrameSet::canResizeRow(const IntPoint& p) const 599 { 600 int r = hitTestSplit(m_rows, p.y()); 601 return r != noSplit && !m_rows.m_preventResize[r]; 602 } 603 604 bool RenderFrameSet::canResizeColumn(const IntPoint& p) const 605 { 606 int c = hitTestSplit(m_cols, p.x()); 607 return c != noSplit && !m_cols.m_preventResize[c]; 608 } 609 610 int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const 611 { 612 if (needsLayout()) 613 return 0; 614 615 int borderThickness = frameSet()->border(); 616 617 int size = axis.m_sizes.size(); 618 if (!size) 619 return 0; 620 621 int position = 0; 622 for (int i = 0; i < split && i < size; ++i) 623 position += axis.m_sizes[i] + borderThickness; 624 return position - borderThickness; 625 } 626 627 int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const 628 { 629 if (needsLayout()) 630 return noSplit; 631 632 int borderThickness = frameSet()->border(); 633 if (borderThickness <= 0) 634 return noSplit; 635 636 size_t size = axis.m_sizes.size(); 637 if (!size) 638 return noSplit; 639 640 int splitPosition = axis.m_sizes[0]; 641 for (size_t i = 1; i < size; ++i) { 642 if (position >= splitPosition && position < splitPosition + borderThickness) 643 return i; 644 splitPosition += borderThickness + axis.m_sizes[i]; 645 } 646 return noSplit; 647 } 648 649 bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const 650 { 651 return child->isFrame() || child->isFrameSet(); 652 } 653 654 CursorDirective RenderFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const 655 { 656 IntPoint roundedPoint = roundedIntPoint(point); 657 if (canResizeRow(roundedPoint)) { 658 cursor = rowResizeCursor(); 659 return SetCursor; 660 } 661 if (canResizeColumn(roundedPoint)) { 662 cursor = columnResizeCursor(); 663 return SetCursor; 664 } 665 return RenderBox::getCursor(point, cursor); 666 } 667 668 } // namespace blink 669