| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights
reserved. | |
| 3 * | |
| 4 * Portions are Copyright (C) 1998 Netscape Communications Corporation. | |
| 5 * | |
| 6 * Other contributors: | |
| 7 * Robert O'Callahan <roc+@cs.cmu.edu> | |
| 8 * David Baron <dbaron@fas.harvard.edu> | |
| 9 * Christian Biesinger <cbiesinger@web.de> | |
| 10 * Randall Jesup <rjesup@wgate.com> | |
| 11 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> | |
| 12 * Josh Soref <timeless@mac.com> | |
| 13 * Boris Zbarsky <bzbarsky@mit.edu> | |
| 14 * | |
| 15 * This library is free software; you can redistribute it and/or | |
| 16 * modify it under the terms of the GNU Lesser General Public | |
| 17 * License as published by the Free Software Foundation; either | |
| 18 * version 2.1 of the License, or (at your option) any later version. | |
| 19 * | |
| 20 * This library is distributed in the hope that it will be useful, | |
| 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 23 * Lesser General Public License for more details. | |
| 24 * | |
| 25 * You should have received a copy of the GNU Lesser General Public | |
| 26 * License along with this library; if not, write to the Free Software | |
| 27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 US
A | |
| 28 * | |
| 29 * Alternatively, the contents of this file may be used under the terms | |
| 30 * of either the Mozilla Public License Version 1.1, found at | |
| 31 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public | |
| 32 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html | |
| 33 * (the "GPL"), in which case the provisions of the MPL or the GPL are | |
| 34 * applicable instead of those above. If you wish to allow use of your | |
| 35 * version of this file only under the terms of one of those two | |
| 36 * licenses (the MPL or the GPL) and not to allow others to use your | |
| 37 * version of this file under the LGPL, indicate your decision by | |
| 38 * deletingthe provisions above and replace them with the notice and | |
| 39 * other provisions required by the MPL or the GPL, as the case may be. | |
| 40 * If you do not delete the provisions above, a recipient may use your | |
| 41 * version of this file under any of the LGPL, the MPL or the GPL. | |
| 42 */ | |
| 43 | |
| 44 #include "sky/engine/config.h" | |
| 45 #include "sky/engine/core/rendering/RenderLayer.h" | |
| 46 | |
| 47 #include "sky/engine/core/dom/Node.h" | |
| 48 #include "sky/engine/core/dom/shadow/ShadowRoot.h" | |
| 49 #include "sky/engine/core/editing/FrameSelection.h" | |
| 50 #include "sky/engine/core/frame/FrameView.h" | |
| 51 #include "sky/engine/core/frame/LocalFrame.h" | |
| 52 #include "sky/engine/core/inspector/InspectorTraceEvents.h" | |
| 53 #include "sky/engine/core/page/EventHandler.h" | |
| 54 #include "sky/engine/core/page/FocusController.h" | |
| 55 #include "sky/engine/core/page/Page.h" | |
| 56 #include "sky/engine/core/rendering/HitTestResult.h" | |
| 57 #include "sky/engine/core/rendering/RenderGeometryMap.h" | |
| 58 #include "sky/engine/core/rendering/RenderView.h" | |
| 59 #include "sky/engine/platform/graphics/GraphicsContextStateSaver.h" | |
| 60 #include "sky/engine/platform/scroll/ScrollAnimator.h" | |
| 61 #include "sky/engine/platform/scroll/Scrollbar.h" | |
| 62 #include "sky/engine/public/platform/Platform.h" | |
| 63 | |
| 64 namespace blink { | |
| 65 | |
| 66 RenderLayerScrollableArea::RenderLayerScrollableArea(RenderLayer& layer) | |
| 67 : m_layer(layer) | |
| 68 , m_scrollsOverflow(false) | |
| 69 , m_scrollDimensionsDirty(true) | |
| 70 , m_nextTopmostScrollChild(0) | |
| 71 , m_topmostScrollChild(0) | |
| 72 , m_needsCompositedScrolling(false) | |
| 73 { | |
| 74 ScrollableArea::setConstrainsScrollingToContentEdge(false); | |
| 75 | |
| 76 Node* node = box().node(); | |
| 77 if (node && node->isElementNode()) { | |
| 78 // We save and restore only the scrollOffset as the other scroll values
are recalculated. | |
| 79 Element* element = toElement(node); | |
| 80 m_scrollOffset = element->savedLayerScrollOffset(); | |
| 81 if (!m_scrollOffset.isZero()) | |
| 82 scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset.width
(), m_scrollOffset.height())); | |
| 83 element->setSavedLayerScrollOffset(IntSize()); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 RenderLayerScrollableArea::~RenderLayerScrollableArea() | |
| 88 { | |
| 89 if (!box().documentBeingDestroyed()) { | |
| 90 Node* node = box().node(); | |
| 91 if (node && node->isElementNode()) | |
| 92 toElement(node)->setSavedLayerScrollOffset(m_scrollOffset); | |
| 93 } | |
| 94 | |
| 95 destroyScrollbar(HorizontalScrollbar); | |
| 96 destroyScrollbar(VerticalScrollbar); | |
| 97 } | |
| 98 | |
| 99 HostWindow* RenderLayerScrollableArea::hostWindow() const | |
| 100 { | |
| 101 return box().frame()->page(); | |
| 102 } | |
| 103 | |
| 104 bool RenderLayerScrollableArea::isActive() const | |
| 105 { | |
| 106 Page* page = box().frame()->page(); | |
| 107 return page && page->focusController().isActive(); | |
| 108 } | |
| 109 | |
| 110 static int cornerStart(const RenderStyle* style, int minX, int maxX, int thickne
ss) | |
| 111 { | |
| 112 if (style->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
| 113 return minX + style->borderLeftWidth(); | |
| 114 return maxX - thickness - style->borderRightWidth(); | |
| 115 } | |
| 116 | |
| 117 IntRect RenderLayerScrollableArea::scrollCornerRect() const | |
| 118 { | |
| 119 // We have a scrollbar corner when a scrollbar is visible and not filling th
e entire length of the box. | |
| 120 // This happens when both scrollbars are present. | |
| 121 const Scrollbar* horizontalBar = horizontalScrollbar(); | |
| 122 const Scrollbar* verticalBar = verticalScrollbar(); | |
| 123 if (!horizontalBar || !verticalBar) | |
| 124 return IntRect(); | |
| 125 | |
| 126 const RenderStyle* style = box().style(); | |
| 127 int horizontalThickness = verticalBar->width(); | |
| 128 int verticalThickness = horizontalBar->height(); | |
| 129 const IntRect& bounds = box().pixelSnappedBorderBoxRect(); | |
| 130 return IntRect(cornerStart(style, bounds.x(), bounds.maxX(), horizontalThick
ness), | |
| 131 bounds.maxY() - verticalThickness - style->borderBottomWidth(), | |
| 132 horizontalThickness, verticalThickness); | |
| 133 } | |
| 134 | |
| 135 IntRect RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Sc
rollbar* scrollbar, const IntRect& scrollbarRect) const | |
| 136 { | |
| 137 RenderView* view = box().view(); | |
| 138 if (!view) | |
| 139 return scrollbarRect; | |
| 140 | |
| 141 IntRect rect = scrollbarRect; | |
| 142 rect.move(scrollbarOffset(scrollbar)); | |
| 143 | |
| 144 return view->frameView()->convertFromRenderer(box(), rect); | |
| 145 } | |
| 146 | |
| 147 IntRect RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Sc
rollbar* scrollbar, const IntRect& parentRect) const | |
| 148 { | |
| 149 RenderView* view = box().view(); | |
| 150 if (!view) | |
| 151 return parentRect; | |
| 152 | |
| 153 IntRect rect = view->frameView()->convertToRenderer(box(), parentRect); | |
| 154 rect.move(-scrollbarOffset(scrollbar)); | |
| 155 return rect; | |
| 156 } | |
| 157 | |
| 158 IntPoint RenderLayerScrollableArea::convertFromScrollbarToContainingView(const S
crollbar* scrollbar, const IntPoint& scrollbarPoint) const | |
| 159 { | |
| 160 RenderView* view = box().view(); | |
| 161 if (!view) | |
| 162 return scrollbarPoint; | |
| 163 | |
| 164 IntPoint point = scrollbarPoint; | |
| 165 point.move(scrollbarOffset(scrollbar)); | |
| 166 return view->frameView()->convertFromRenderer(box(), point); | |
| 167 } | |
| 168 | |
| 169 IntPoint RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const S
crollbar* scrollbar, const IntPoint& parentPoint) const | |
| 170 { | |
| 171 RenderView* view = box().view(); | |
| 172 if (!view) | |
| 173 return parentPoint; | |
| 174 | |
| 175 IntPoint point = view->frameView()->convertToRenderer(box(), parentPoint); | |
| 176 | |
| 177 point.move(-scrollbarOffset(scrollbar)); | |
| 178 return point; | |
| 179 } | |
| 180 | |
| 181 int RenderLayerScrollableArea::scrollSize(ScrollbarOrientation orientation) cons
t | |
| 182 { | |
| 183 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition()
; | |
| 184 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scr
ollDimensions.height(); | |
| 185 } | |
| 186 | |
| 187 void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset) | |
| 188 { | |
| 189 // Ensure that the dimensions will be computed if they need to be (for overf
low:hidden blocks). | |
| 190 if (m_scrollDimensionsDirty) | |
| 191 computeScrollDimensions(); | |
| 192 | |
| 193 if (scrollOffset() == toIntSize(newScrollOffset)) | |
| 194 return; | |
| 195 | |
| 196 setScrollOffset(toIntSize(newScrollOffset)); | |
| 197 | |
| 198 LocalFrame* frame = box().frame(); | |
| 199 ASSERT(frame); | |
| 200 | |
| 201 RefPtr<FrameView> frameView = box().frameView(); | |
| 202 | |
| 203 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ScrollLayer",
"data", InspectorScrollLayerEvent::data(&box())); | |
| 204 | |
| 205 // Update the positions of our child layers (if needed as only fixed layers
should be impacted by a scroll). | |
| 206 // We don't update compositing layers, because we need to do a deep update f
rom the compositing ancestor. | |
| 207 if (!frameView->isInPerformLayout()) { | |
| 208 // If we're in the middle of layout, we'll just update layers once layou
t has finished. | |
| 209 // FIXME(sky): Do we still need this? | |
| 210 layer()->clipper().clearClipRectsIncludingDescendants(); | |
| 211 } | |
| 212 | |
| 213 // The caret rect needs to be invalidated after scrolling | |
| 214 frame->selection().setCaretRectNeedsUpdate(); | |
| 215 | |
| 216 // Schedule the scroll DOM event. | |
| 217 if (box().node()) | |
| 218 box().node()->document().enqueueScrollEventForNode(box().node()); | |
| 219 } | |
| 220 | |
| 221 IntPoint RenderLayerScrollableArea::scrollPosition() const | |
| 222 { | |
| 223 return IntPoint(m_scrollOffset); | |
| 224 } | |
| 225 | |
| 226 IntPoint RenderLayerScrollableArea::minimumScrollPosition() const | |
| 227 { | |
| 228 return -scrollOrigin(); | |
| 229 } | |
| 230 | |
| 231 IntPoint RenderLayerScrollableArea::maximumScrollPosition() const | |
| 232 { | |
| 233 if (!box().hasOverflowClip()) | |
| 234 return -scrollOrigin(); | |
| 235 return -scrollOrigin() + IntPoint(pixelSnappedScrollWidth(), pixelSnappedScr
ollHeight()) - enclosingIntRect(box().clientBoxRect()).size(); | |
| 236 } | |
| 237 | |
| 238 IntRect RenderLayerScrollableArea::visibleContentRect(IncludeScrollbarsInRect sc
rollbarInclusion) const | |
| 239 { | |
| 240 return IntRect(IntPoint(scrollXOffset(), scrollYOffset()), | |
| 241 IntSize(max(0, layer()->size().width()), max(0, layer()->size().height()
))); | |
| 242 } | |
| 243 | |
| 244 int RenderLayerScrollableArea::visibleHeight() const | |
| 245 { | |
| 246 return layer()->size().height(); | |
| 247 } | |
| 248 | |
| 249 int RenderLayerScrollableArea::visibleWidth() const | |
| 250 { | |
| 251 return layer()->size().width(); | |
| 252 } | |
| 253 | |
| 254 IntSize RenderLayerScrollableArea::contentsSize() const | |
| 255 { | |
| 256 return IntSize(scrollWidth(), scrollHeight()); | |
| 257 } | |
| 258 | |
| 259 IntSize RenderLayerScrollableArea::overhangAmount() const | |
| 260 { | |
| 261 return IntSize(); | |
| 262 } | |
| 263 | |
| 264 IntRect RenderLayerScrollableArea::scrollableAreaBoundingBox() const | |
| 265 { | |
| 266 return box().absoluteBoundingBoxRect(); | |
| 267 } | |
| 268 | |
| 269 bool RenderLayerScrollableArea::userInputScrollable(ScrollbarOrientation orienta
tion) const | |
| 270 { | |
| 271 EOverflow overflowStyle = (orientation == HorizontalScrollbar) ? | |
| 272 box().style()->overflowX() : box().style()->overflowY(); | |
| 273 return (overflowStyle == OSCROLL || overflowStyle == OAUTO || overflowStyle
== OOVERLAY); | |
| 274 } | |
| 275 | |
| 276 bool RenderLayerScrollableArea::shouldPlaceVerticalScrollbarOnLeft() const | |
| 277 { | |
| 278 return box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft(); | |
| 279 } | |
| 280 | |
| 281 int RenderLayerScrollableArea::pageStep(ScrollbarOrientation orientation) const | |
| 282 { | |
| 283 int length = (orientation == HorizontalScrollbar) ? | |
| 284 box().pixelSnappedClientWidth() : box().pixelSnappedClientHeight(); | |
| 285 int minPageStep = static_cast<float>(length) * ScrollableArea::minFractionTo
StepWhenPaging(); | |
| 286 int pageStep = max(minPageStep, length - ScrollableArea::maxOverlapBetweenPa
ges()); | |
| 287 | |
| 288 return max(pageStep, 1); | |
| 289 } | |
| 290 | |
| 291 RenderBox& RenderLayerScrollableArea::box() const | |
| 292 { | |
| 293 return *m_layer.renderBox(); | |
| 294 } | |
| 295 | |
| 296 RenderLayer* RenderLayerScrollableArea::layer() const | |
| 297 { | |
| 298 return &m_layer; | |
| 299 } | |
| 300 | |
| 301 LayoutUnit RenderLayerScrollableArea::scrollWidth() const | |
| 302 { | |
| 303 if (m_scrollDimensionsDirty) | |
| 304 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions(); | |
| 305 return m_overflowRect.width(); | |
| 306 } | |
| 307 | |
| 308 LayoutUnit RenderLayerScrollableArea::scrollHeight() const | |
| 309 { | |
| 310 if (m_scrollDimensionsDirty) | |
| 311 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions(); | |
| 312 return m_overflowRect.height(); | |
| 313 } | |
| 314 | |
| 315 int RenderLayerScrollableArea::pixelSnappedScrollWidth() const | |
| 316 { | |
| 317 return snapSizeToPixel(scrollWidth(), box().clientLeft() + box().x()); | |
| 318 } | |
| 319 | |
| 320 int RenderLayerScrollableArea::pixelSnappedScrollHeight() const | |
| 321 { | |
| 322 return snapSizeToPixel(scrollHeight(), box().clientTop() + box().y()); | |
| 323 } | |
| 324 | |
| 325 void RenderLayerScrollableArea::computeScrollDimensions() | |
| 326 { | |
| 327 m_scrollDimensionsDirty = false; | |
| 328 | |
| 329 m_overflowRect = box().layoutOverflowRect(); | |
| 330 | |
| 331 int scrollableLeftOverflow = m_overflowRect.x() - box().borderLeft(); | |
| 332 int scrollableTopOverflow = m_overflowRect.y() - box().borderTop(); | |
| 333 setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow)); | |
| 334 } | |
| 335 | |
| 336 void RenderLayerScrollableArea::scrollToOffset(const IntSize& scrollOffset, Scro
llOffsetClamping clamp) | |
| 337 { | |
| 338 IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(s
crollOffset) : scrollOffset; | |
| 339 if (newScrollOffset != adjustedScrollOffset()) | |
| 340 scrollToOffsetWithoutAnimation(-scrollOrigin() + newScrollOffset); | |
| 341 } | |
| 342 | |
| 343 void RenderLayerScrollableArea::updateAfterLayout() | |
| 344 { | |
| 345 m_scrollDimensionsDirty = true; | |
| 346 IntSize originalScrollOffset = adjustedScrollOffset(); | |
| 347 | |
| 348 computeScrollDimensions(); | |
| 349 | |
| 350 // Layout may cause us to be at an invalid scroll position. In this case we
need | |
| 351 // to pull our scroll offsets back to the max (or push them up to the min). | |
| 352 IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset()); | |
| 353 if (clampedScrollOffset != adjustedScrollOffset()) | |
| 354 scrollToOffset(clampedScrollOffset); | |
| 355 | |
| 356 if (originalScrollOffset != adjustedScrollOffset()) | |
| 357 scrollToOffsetWithoutAnimation(-scrollOrigin() + adjustedScrollOffset())
; | |
| 358 | |
| 359 bool hasHorizontalOverflow = this->hasHorizontalOverflow(); | |
| 360 bool hasVerticalOverflow = this->hasVerticalOverflow(); | |
| 361 | |
| 362 // overflow:auto may need to lay out again if scrollbars got added/removed. | |
| 363 if (box().hasAutoHorizontalScrollbar()) | |
| 364 setHasHorizontalScrollbar(hasHorizontalOverflow); | |
| 365 if (box().hasAutoVerticalScrollbar()) | |
| 366 setHasVerticalScrollbar(hasVerticalOverflow); | |
| 367 | |
| 368 // Set up the range (and page step/line step). | |
| 369 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
| 370 int clientWidth = box().pixelSnappedClientWidth(); | |
| 371 horizontalScrollbar->setProportion(clientWidth, overflowRect().width()); | |
| 372 } | |
| 373 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
| 374 int clientHeight = box().pixelSnappedClientHeight(); | |
| 375 verticalScrollbar->setProportion(clientHeight, overflowRect().height()); | |
| 376 } | |
| 377 | |
| 378 bool hasOverflow = hasScrollableHorizontalOverflow() || hasScrollableVertica
lOverflow(); | |
| 379 updateScrollableAreaSet(hasOverflow); | |
| 380 | |
| 381 if (hasOverflow) { | |
| 382 positionOverflowControls(IntSize()); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 bool RenderLayerScrollableArea::hasHorizontalOverflow() const | |
| 387 { | |
| 388 ASSERT(!m_scrollDimensionsDirty); | |
| 389 | |
| 390 return pixelSnappedScrollWidth() > box().pixelSnappedClientWidth(); | |
| 391 } | |
| 392 | |
| 393 bool RenderLayerScrollableArea::hasVerticalOverflow() const | |
| 394 { | |
| 395 ASSERT(!m_scrollDimensionsDirty); | |
| 396 | |
| 397 return pixelSnappedScrollHeight() > box().pixelSnappedClientHeight(); | |
| 398 } | |
| 399 | |
| 400 bool RenderLayerScrollableArea::hasScrollableHorizontalOverflow() const | |
| 401 { | |
| 402 return hasHorizontalOverflow() && box().scrollsOverflowX(); | |
| 403 } | |
| 404 | |
| 405 bool RenderLayerScrollableArea::hasScrollableVerticalOverflow() const | |
| 406 { | |
| 407 return hasVerticalOverflow() && box().scrollsOverflowY(); | |
| 408 } | |
| 409 | |
| 410 static bool overflowRequiresScrollbar(EOverflow overflow) | |
| 411 { | |
| 412 return overflow == OSCROLL; | |
| 413 } | |
| 414 | |
| 415 static bool overflowDefinesAutomaticScrollbar(EOverflow overflow) | |
| 416 { | |
| 417 return overflow == OAUTO || overflow == OOVERLAY; | |
| 418 } | |
| 419 | |
| 420 void RenderLayerScrollableArea::updateAfterStyleChange(const RenderStyle* oldSty
le) | |
| 421 { | |
| 422 if (!m_scrollDimensionsDirty) | |
| 423 updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollab
leVerticalOverflow()); | |
| 424 | |
| 425 EOverflow overflowX = box().style()->overflowX(); | |
| 426 EOverflow overflowY = box().style()->overflowY(); | |
| 427 | |
| 428 // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep
any automatic scrollbar that was already present. | |
| 429 // FIXME(sky): We only have overlay scrollbars, so we never do a relayout | |
| 430 // due to scrollbars. We probably don't need to do this at all. Also, | |
| 431 // updateScrollbarsAfterLayout not longer exists. | |
| 432 bool needsHorizontalScrollbar = (hasHorizontalScrollbar() && overflowDefines
AutomaticScrollbar(overflowX)) || overflowRequiresScrollbar(overflowX); | |
| 433 bool needsVerticalScrollbar = (hasVerticalScrollbar() && overflowDefinesAuto
maticScrollbar(overflowY)) || overflowRequiresScrollbar(overflowY); | |
| 434 setHasHorizontalScrollbar(needsHorizontalScrollbar); | |
| 435 setHasVerticalScrollbar(needsVerticalScrollbar); | |
| 436 } | |
| 437 | |
| 438 bool RenderLayerScrollableArea::updateAfterCompositingChange() | |
| 439 { | |
| 440 const bool layersChanged = m_topmostScrollChild != m_nextTopmostScrollChild; | |
| 441 m_topmostScrollChild = m_nextTopmostScrollChild; | |
| 442 m_nextTopmostScrollChild = nullptr; | |
| 443 return layersChanged; | |
| 444 } | |
| 445 | |
| 446 void RenderLayerScrollableArea::updateAfterOverflowRecalc() | |
| 447 { | |
| 448 computeScrollDimensions(); | |
| 449 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
| 450 int clientWidth = box().pixelSnappedClientWidth(); | |
| 451 horizontalScrollbar->setProportion(clientWidth, overflowRect().width()); | |
| 452 } | |
| 453 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
| 454 int clientHeight = box().pixelSnappedClientHeight(); | |
| 455 verticalScrollbar->setProportion(clientHeight, overflowRect().height()); | |
| 456 } | |
| 457 | |
| 458 bool hasHorizontalOverflow = this->hasHorizontalOverflow(); | |
| 459 bool hasVerticalOverflow = this->hasVerticalOverflow(); | |
| 460 bool autoHorizontalScrollBarChanged = box().hasAutoHorizontalScrollbar() &&
(hasHorizontalScrollbar() != hasHorizontalOverflow); | |
| 461 bool autoVerticalScrollBarChanged = box().hasAutoVerticalScrollbar() && (has
VerticalScrollbar() != hasVerticalOverflow); | |
| 462 if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) | |
| 463 box().setNeedsLayout(); | |
| 464 } | |
| 465 | |
| 466 IntSize RenderLayerScrollableArea::clampScrollOffset(const IntSize& scrollOffset
) const | |
| 467 { | |
| 468 int maxX = scrollWidth() - box().pixelSnappedClientWidth(); | |
| 469 int maxY = scrollHeight() - box().pixelSnappedClientHeight(); | |
| 470 | |
| 471 int x = std::max(std::min(scrollOffset.width(), maxX), 0); | |
| 472 int y = std::max(std::min(scrollOffset.height(), maxY), 0); | |
| 473 return IntSize(x, y); | |
| 474 } | |
| 475 | |
| 476 IntRect RenderLayerScrollableArea::rectForHorizontalScrollbar(const IntRect& bor
derBoxRect) const | |
| 477 { | |
| 478 if (!m_hBar) | |
| 479 return IntRect(); | |
| 480 | |
| 481 const IntRect& scrollCorner = scrollCornerRect(); | |
| 482 | |
| 483 return IntRect(horizontalScrollbarStart(borderBoxRect.x()), | |
| 484 borderBoxRect.maxY() - box().borderBottom() - m_hBar->height(), | |
| 485 borderBoxRect.width() - (box().borderLeft() + box().borderRight()) - scr
ollCorner.width(), | |
| 486 m_hBar->height()); | |
| 487 } | |
| 488 | |
| 489 IntRect RenderLayerScrollableArea::rectForVerticalScrollbar(const IntRect& borde
rBoxRect) const | |
| 490 { | |
| 491 if (!m_vBar) | |
| 492 return IntRect(); | |
| 493 | |
| 494 const IntRect& scrollCorner = scrollCornerRect(); | |
| 495 | |
| 496 return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX(
)), | |
| 497 borderBoxRect.y() + box().borderTop(), | |
| 498 m_vBar->width(), | |
| 499 borderBoxRect.height() - (box().borderTop() + box().borderBottom()) - sc
rollCorner.height()); | |
| 500 } | |
| 501 | |
| 502 LayoutUnit RenderLayerScrollableArea::verticalScrollbarStart(int minX, int maxX)
const | |
| 503 { | |
| 504 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
| 505 return minX + box().borderLeft(); | |
| 506 return maxX - box().borderRight() - m_vBar->width(); | |
| 507 } | |
| 508 | |
| 509 LayoutUnit RenderLayerScrollableArea::horizontalScrollbarStart(int minX) const | |
| 510 { | |
| 511 return minX + box().borderLeft(); | |
| 512 } | |
| 513 | |
| 514 IntSize RenderLayerScrollableArea::scrollbarOffset(const Scrollbar* scrollbar) c
onst | |
| 515 { | |
| 516 if (scrollbar == m_vBar.get()) | |
| 517 return IntSize(verticalScrollbarStart(0, box().width()), box().borderTop
()); | |
| 518 | |
| 519 if (scrollbar == m_hBar.get()) | |
| 520 return IntSize(horizontalScrollbarStart(0), box().height() - box().borde
rBottom() - scrollbar->height()); | |
| 521 | |
| 522 ASSERT_NOT_REACHED(); | |
| 523 return IntSize(); | |
| 524 } | |
| 525 | |
| 526 PassRefPtr<Scrollbar> RenderLayerScrollableArea::createScrollbar(ScrollbarOrient
ation orientation) | |
| 527 { | |
| 528 RefPtr<Scrollbar> widget = Scrollbar::create(this, orientation); | |
| 529 if (orientation == HorizontalScrollbar) | |
| 530 didAddScrollbar(widget.get(), HorizontalScrollbar); | |
| 531 else | |
| 532 didAddScrollbar(widget.get(), VerticalScrollbar); | |
| 533 return widget.release(); | |
| 534 } | |
| 535 | |
| 536 void RenderLayerScrollableArea::destroyScrollbar(ScrollbarOrientation orientatio
n) | |
| 537 { | |
| 538 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar :
m_vBar; | |
| 539 if (!scrollbar) | |
| 540 return; | |
| 541 | |
| 542 willRemoveScrollbar(scrollbar.get(), orientation); | |
| 543 | |
| 544 scrollbar->disconnectFromScrollableArea(); | |
| 545 scrollbar = nullptr; | |
| 546 } | |
| 547 | |
| 548 void RenderLayerScrollableArea::setHasHorizontalScrollbar(bool hasScrollbar) | |
| 549 { | |
| 550 if (hasScrollbar == hasHorizontalScrollbar()) | |
| 551 return; | |
| 552 | |
| 553 if (hasScrollbar) { | |
| 554 m_hBar = createScrollbar(HorizontalScrollbar); | |
| 555 } else { | |
| 556 destroyScrollbar(HorizontalScrollbar); | |
| 557 } | |
| 558 } | |
| 559 | |
| 560 void RenderLayerScrollableArea::setHasVerticalScrollbar(bool hasScrollbar) | |
| 561 { | |
| 562 if (hasScrollbar == hasVerticalScrollbar()) | |
| 563 return; | |
| 564 | |
| 565 if (hasScrollbar) { | |
| 566 m_vBar = createScrollbar(VerticalScrollbar); | |
| 567 } else { | |
| 568 destroyScrollbar(VerticalScrollbar); | |
| 569 } | |
| 570 } | |
| 571 | |
| 572 void RenderLayerScrollableArea::positionOverflowControls(const IntSize& offsetFr
omRoot) | |
| 573 { | |
| 574 if (!hasScrollbar()) | |
| 575 return; | |
| 576 | |
| 577 const IntRect borderBox = box().pixelSnappedBorderBoxRect(); | |
| 578 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
| 579 IntRect vBarRect = rectForVerticalScrollbar(borderBox); | |
| 580 vBarRect.move(offsetFromRoot); | |
| 581 verticalScrollbar->setFrameRect(vBarRect); | |
| 582 } | |
| 583 | |
| 584 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
| 585 IntRect hBarRect = rectForHorizontalScrollbar(borderBox); | |
| 586 hBarRect.move(offsetFromRoot); | |
| 587 horizontalScrollbar->setFrameRect(hBarRect); | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 void RenderLayerScrollableArea::paintOverflowControls(GraphicsContext* context,
const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayCont
rols) | |
| 592 { | |
| 593 // Don't do anything if we have no overflow. | |
| 594 if (!box().hasOverflowClip()) | |
| 595 return; | |
| 596 | |
| 597 IntPoint adjustedPaintOffset = paintOffset; | |
| 598 if (paintingOverlayControls) | |
| 599 adjustedPaintOffset = m_cachedOverlayScrollbarOffset; | |
| 600 | |
| 601 // Move the scrollbar widgets if necessary. We normally move and resize widg
ets during layout, | |
| 602 // but sometimes widgets can move without layout occurring (most notably whe
n you scroll a | |
| 603 // document that contains fixed positioned elements). | |
| 604 positionOverflowControls(toIntSize(adjustedPaintOffset)); | |
| 605 | |
| 606 // Overlay scrollbars paint in a second pass through the layer tree so that
they will paint | |
| 607 // on top of everything else. If this is the normal painting pass, paintingO
verlayControls | |
| 608 // will be false, and we should just tell the root layer that there are over
lay scrollbars | |
| 609 // that need to be painted. That will cause the second pass through the laye
r tree to run, | |
| 610 // and we'll paint the scrollbars then. In the meantime, cache tx and ty so
that the | |
| 611 // second pass doesn't need to re-enter the RenderTree to get it right. | |
| 612 if (hasOverlayScrollbars() && !paintingOverlayControls) { | |
| 613 m_cachedOverlayScrollbarOffset = paintOffset; | |
| 614 IntRect localDamgeRect = damageRect; | |
| 615 localDamgeRect.moveBy(-paintOffset); | |
| 616 if (!overflowControlsIntersectRect(localDamgeRect)) | |
| 617 return; | |
| 618 | |
| 619 box().view()->layer()->setContainsDirtyOverlayScrollbars(true); | |
| 620 return; | |
| 621 } | |
| 622 | |
| 623 // This check is required to avoid painting custom CSS scrollbars twice. | |
| 624 if (paintingOverlayControls && !hasOverlayScrollbars()) | |
| 625 return; | |
| 626 | |
| 627 // Now that we're sure the scrollbars are in the right place, paint them. | |
| 628 if (m_hBar) | |
| 629 m_hBar->paint(context, damageRect); | |
| 630 if (m_vBar) | |
| 631 m_vBar->paint(context, damageRect); | |
| 632 } | |
| 633 | |
| 634 bool RenderLayerScrollableArea::overflowControlsIntersectRect(const IntRect& loc
alRect) const | |
| 635 { | |
| 636 const IntRect borderBox = box().pixelSnappedBorderBoxRect(); | |
| 637 | |
| 638 if (rectForHorizontalScrollbar(borderBox).intersects(localRect)) | |
| 639 return true; | |
| 640 | |
| 641 if (rectForVerticalScrollbar(borderBox).intersects(localRect)) | |
| 642 return true; | |
| 643 | |
| 644 return false; | |
| 645 } | |
| 646 | |
| 647 LayoutRect RenderLayerScrollableArea::exposeRect(const LayoutRect& rect, const S
crollAlignment& alignX, const ScrollAlignment& alignY) | |
| 648 { | |
| 649 LayoutRect localExposeRect(box().absoluteToLocalQuad(FloatQuad(FloatRect(rec
t)), UseTransforms).boundingBox()); | |
| 650 LayoutRect layerBounds(0, 0, box().clientWidth(), box().clientHeight()); | |
| 651 LayoutRect r = ScrollAlignment::getRectToExpose(layerBounds, localExposeRect
, alignX, alignY); | |
| 652 | |
| 653 IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() + toI
ntSize(roundedIntRect(r).location())); | |
| 654 if (clampedScrollOffset == adjustedScrollOffset()) | |
| 655 return rect; | |
| 656 | |
| 657 IntSize oldScrollOffset = adjustedScrollOffset(); | |
| 658 scrollToOffset(clampedScrollOffset); | |
| 659 IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset; | |
| 660 localExposeRect.move(-scrollOffsetDifference); | |
| 661 return LayoutRect(box().localToAbsoluteQuad(FloatQuad(FloatRect(localExposeR
ect)), UseTransforms).boundingBox()); | |
| 662 } | |
| 663 | |
| 664 void RenderLayerScrollableArea::updateScrollableAreaSet(bool hasOverflow) | |
| 665 { | |
| 666 LocalFrame* frame = box().frame(); | |
| 667 if (!frame) | |
| 668 return; | |
| 669 | |
| 670 FrameView* frameView = frame->view(); | |
| 671 if (!frameView) | |
| 672 return; | |
| 673 | |
| 674 // FIXME: Does this need to be fixed later for OOPI? | |
| 675 bool isVisibleToHitTest = box().visibleToHitTesting(); | |
| 676 bool didScrollOverflow = m_scrollsOverflow; | |
| 677 | |
| 678 m_scrollsOverflow = hasOverflow && isVisibleToHitTest; | |
| 679 if (didScrollOverflow == scrollsOverflow()) | |
| 680 return; | |
| 681 | |
| 682 if (m_scrollsOverflow) | |
| 683 frameView->addScrollableArea(this); | |
| 684 else | |
| 685 frameView->removeScrollableArea(this); | |
| 686 } | |
| 687 | |
| 688 void RenderLayerScrollableArea::setTopmostScrollChild(RenderLayer* scrollChild) | |
| 689 { | |
| 690 // We only want to track the topmost scroll child for scrollable areas with | |
| 691 // overlay scrollbars. | |
| 692 if (!hasOverlayScrollbars()) | |
| 693 return; | |
| 694 m_nextTopmostScrollChild = scrollChild; | |
| 695 } | |
| 696 | |
| 697 } // namespace blink | |
| OLD | NEW |