2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
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>
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.
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.
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 USA
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.
45 #include "core/rendering/RenderLayer.h"
47 #include "core/css/PseudoStyleRequest.h"
48 #include "core/dom/shadow/ShadowRoot.h"
49 #include "core/editing/FrameSelection.h"
50 #include "core/html/HTMLFrameOwnerElement.h"
51 #include "core/inspector/InspectorInstrumentation.h"
52 #include "core/page/EventHandler.h"
53 #include "core/page/FocusController.h"
54 #include "core/frame/Frame.h"
55 #include "core/frame/FrameView.h"
56 #include "core/page/Page.h"
57 #include "core/page/scrolling/ScrollingCoordinator.h"
58 #include "core/rendering/CompositedLayerMapping.h"
59 #include "core/rendering/LayoutRectRecorder.h"
60 #include "core/rendering/RenderGeometryMap.h"
61 #include "core/rendering/RenderLayerCompositor.h"
62 #include "core/rendering/RenderScrollbar.h"
63 #include "core/rendering/RenderScrollbarPart.h"
64 #include "core/rendering/RenderView.h"
65 #include "platform/PlatformGestureEvent.h"
66 #include "platform/PlatformMouseEvent.h"
67 #include "platform/graphics/GraphicsContextStateSaver.h"
68 #include "platform/graphics/GraphicsLayer.h"
69 #include "platform/scroll/ScrollAnimator.h"
70 #include "platform/scroll/ScrollbarTheme.h"
71 #include "public/platform/Platform.h"
75 const int ResizerControlExpandRatioForTouch = 2;
77 RenderLayerScrollableArea::RenderLayerScrollableArea(RenderBox* box)
79 , m_inResizeMode(false)
80 , m_scrollDimensionsDirty(true)
81 , m_inOverflowRelayout(false)
82 , m_needsCompositedScrolling(false)
83 , m_willUseCompositedScrollingHasBeenRecorded(false)
84 , m_isScrollableAreaHasBeenRecorded(false)
85 , m_forceNeedsCompositedScrolling(DoNotForceCompositedScrolling)
89 ScrollableArea::setConstrainsScrollingToContentEdge(false);
91 Node* node = m_box->node();
92 if (node && node->isElementNode()) {
93 // We save and restore only the scrollOffset as the other scroll values are recalculated.
94 Element* element = toElement(node);
95 m_scrollOffset = element->savedLayerScrollOffset();
96 if (!m_scrollOffset.isZero())
97 scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset.width(), m_scrollOffset.height()));
98 element->setSavedLayerScrollOffset(IntSize());
101 updateResizerAreaSet();
104 RenderLayerScrollableArea::~RenderLayerScrollableArea()
106 if (inResizeMode() && !m_box->documentBeingDestroyed()) {
107 if (Frame* frame = m_box->frame())
108 frame->eventHandler().resizeScrollableAreaDestroyed();
111 if (Frame* frame = m_box->frame()) {
112 if (FrameView* frameView = frame->view()) {
113 frameView->removeScrollableArea(this);
117 if (m_box->frame() && m_box->frame()->page()) {
118 if (ScrollingCoordinator* scrollingCoordinator = m_box->frame()->page()->scrollingCoordinator())
119 scrollingCoordinator->willDestroyScrollableArea(this);
122 if (!m_box->documentBeingDestroyed()) {
123 Node* node = m_box->node();
124 if (node && node->isElementNode())
125 toElement(node)->setSavedLayerScrollOffset(m_scrollOffset);
128 if (Frame* frame = m_box->frame()) {
129 if (FrameView* frameView = frame->view())
130 frameView->removeResizerArea(m_box);
133 destroyScrollbar(HorizontalScrollbar);
134 destroyScrollbar(VerticalScrollbar);
137 m_scrollCorner->destroy();
139 m_resizer->destroy();
142 ScrollableArea* RenderLayerScrollableArea::enclosingScrollableArea() const
144 if (RenderBox* enclosingScrollableBox = m_box->enclosingScrollableBox())
145 return enclosingScrollableBox->layer()->scrollableArea();
147 // FIXME: We should return the frame view here (or possibly an ancestor frame view,
148 // if the frame view isn't scrollable.
152 GraphicsLayer* RenderLayerScrollableArea::layerForScrolling() const
154 return m_box->hasCompositedLayerMapping() ? m_box->compositedLayerMapping()->scrollingContentsLayer() : 0;
157 GraphicsLayer* RenderLayerScrollableArea::layerForHorizontalScrollbar() const
159 return m_box->hasCompositedLayerMapping() ? m_box->compositedLayerMapping()->layerForHorizontalScrollbar() : 0;
162 GraphicsLayer* RenderLayerScrollableArea::layerForVerticalScrollbar() const
164 return m_box->hasCompositedLayerMapping() ? m_box->compositedLayerMapping()->layerForVerticalScrollbar() : 0;
167 GraphicsLayer* RenderLayerScrollableArea::layerForScrollCorner() const
169 return m_box->hasCompositedLayerMapping() ? m_box->compositedLayerMapping()->layerForScrollCorner() : 0;
172 void RenderLayerScrollableArea::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
174 if (scrollbar == m_vBar.get()) {
175 if (GraphicsLayer* layer = layerForVerticalScrollbar()) {
176 layer->setNeedsDisplayInRect(rect);
180 if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
181 layer->setNeedsDisplayInRect(rect);
186 IntRect scrollRect = rect;
187 // If we are not yet inserted into the tree, there is no need to repaint.
188 if (!m_box->parent())
191 if (scrollbar == m_vBar.get())
192 scrollRect.move(verticalScrollbarStart(0, m_box->width()), m_box->borderTop());
194 scrollRect.move(horizontalScrollbarStart(0), m_box->height() - m_box->borderBottom() - scrollbar->height());
196 if (scrollRect.isEmpty())
199 LayoutRect repaintRect = scrollRect;
200 m_box->flipForWritingMode(repaintRect);
202 IntRect intRect = pixelSnappedIntRect(repaintRect);
204 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && m_box->frameView()->isInPerformLayout()) {
205 if (scrollbar == m_vBar.get()) {
206 m_verticalBarDamage = intRect;
207 m_hasVerticalBarDamage = true;
209 m_horizontalBarDamage = intRect;
210 m_hasHorizontalBarDamage = true;
214 m_box->repaintRectangle(intRect);
218 void RenderLayerScrollableArea::invalidateScrollCornerRect(const IntRect& rect)
220 if (GraphicsLayer* layer = layerForScrollCorner()) {
221 layer->setNeedsDisplayInRect(rect);
226 m_scrollCorner->repaintRectangle(rect);
228 m_resizer->repaintRectangle(rect);
231 bool RenderLayerScrollableArea::isActive() const
233 Page* page = m_box->frame()->page();
234 return page && page->focusController().isActive();
237 bool RenderLayerScrollableArea::isScrollCornerVisible() const
239 return !scrollCornerRect().isEmpty();
242 static int cornerStart(const RenderStyle* style, int minX, int maxX, int thickness)
244 if (style->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
245 return minX + style->borderLeftWidth();
246 return maxX - thickness - style->borderRightWidth();
249 static IntRect cornerRect(const RenderStyle* style, const Scrollbar* horizontalScrollbar, const Scrollbar* verticalScrollbar, const IntRect& bounds)
251 int horizontalThickness;
252 int verticalThickness;
253 if (!verticalScrollbar && !horizontalScrollbar) {
254 // FIXME: This isn't right. We need to know the thickness of custom scrollbars
255 // even when they don't exist in order to set the resizer square size properly.
256 horizontalThickness = ScrollbarTheme::theme()->scrollbarThickness();
257 verticalThickness = horizontalThickness;
258 } else if (verticalScrollbar && !horizontalScrollbar) {
259 horizontalThickness = verticalScrollbar->width();
260 verticalThickness = horizontalThickness;
261 } else if (horizontalScrollbar && !verticalScrollbar) {
262 verticalThickness = horizontalScrollbar->height();
263 horizontalThickness = verticalThickness;
265 horizontalThickness = verticalScrollbar->width();
266 verticalThickness = horizontalScrollbar->height();
268 return IntRect(cornerStart(style, bounds.x(), bounds.maxX(), horizontalThickness),
269 bounds.maxY() - verticalThickness - style->borderBottomWidth(),
270 horizontalThickness, verticalThickness);
274 IntRect RenderLayerScrollableArea::scrollCornerRect() const
276 // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box.
277 // This happens when:
278 // (a) A resizer is present and at least one scrollbar is present
279 // (b) Both scrollbars are present.
280 bool hasHorizontalBar = horizontalScrollbar();
281 bool hasVerticalBar = verticalScrollbar();
282 bool hasResizer = m_box->style()->resize() != RESIZE_NONE;
283 if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar)))
284 return cornerRect(m_box->style(), horizontalScrollbar(), verticalScrollbar(), m_box->pixelSnappedBorderBoxRect());
288 IntRect RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
290 RenderView* view = m_box->view();
292 return scrollbarRect;
294 IntRect rect = scrollbarRect;
295 rect.move(scrollbarOffset(scrollbar));
297 return view->frameView()->convertFromRenderer(m_box, rect);
300 IntRect RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
302 RenderView* view = m_box->view();
306 IntRect rect = view->frameView()->convertToRenderer(m_box, parentRect);
307 rect.move(-scrollbarOffset(scrollbar));
311 IntPoint RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
313 RenderView* view = m_box->view();
315 return scrollbarPoint;
317 IntPoint point = scrollbarPoint;
318 point.move(scrollbarOffset(scrollbar));
319 return view->frameView()->convertFromRenderer(m_box, point);
322 IntPoint RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
324 RenderView* view = m_box->view();
328 IntPoint point = view->frameView()->convertToRenderer(m_box, parentPoint);
330 point.move(-scrollbarOffset(scrollbar));
334 int RenderLayerScrollableArea::scrollSize(ScrollbarOrientation orientation) const
336 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition();
337 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height();
340 void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset)
342 if (!m_box->isMarquee()) {
343 // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
344 if (m_scrollDimensionsDirty)
345 computeScrollDimensions();
348 if (scrollOffset() == toIntSize(newScrollOffset))
351 setScrollOffset(toIntSize(newScrollOffset));
353 Frame* frame = m_box->frame();
356 RefPtr<FrameView> frameView = m_box->frameView();
358 InspectorInstrumentation::willScrollLayer(m_box);
360 // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
361 // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
362 if (!frameView->isInPerformLayout()) {
363 // If we're in the middle of layout, we'll just update layers once layout has finished.
364 layer()->updateLayerPositionsAfterOverflowScroll();
365 // Update regions, scrolling may change the clip of a particular region.
366 frameView->updateAnnotatedRegions();
367 // FIXME: We shouldn't call updateWidgetPositions() here since it might tear down the render tree,
368 // for now we just crash to avoid allowing an attacker to use after free.
369 frameView->updateWidgetPositions();
370 RELEASE_ASSERT(frameView->renderView());
371 updateCompositingLayersAfterScroll();
374 RenderLayerModelObject* repaintContainer = m_box->containerForRepaint();
375 // The caret rect needs to be invalidated after scrolling
376 frame->selection().setCaretRectNeedsUpdate();
378 FloatQuad quadForFakeMouseMoveEvent = FloatQuad(layer()->repainter().repaintRect());
379 if (repaintContainer)
380 quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
381 frame->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
383 bool requiresRepaint = true;
385 if (m_box->view()->compositor()->inCompositingMode()) {
386 bool onlyScrolledCompositedLayers = scrollsOverflow()
387 && !layer()->hasVisibleNonLayerContent()
388 && !layer()->hasNonCompositedChild()
389 && !layer()->hasBlockSelectionGapBounds()
390 && !m_box->isMarquee();
392 if (usesCompositedScrolling() || onlyScrolledCompositedLayers)
393 requiresRepaint = false;
396 // Just schedule a full repaint of our object.
397 if (requiresRepaint) {
398 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && m_box->frameView()->isInPerformLayout())
399 m_box->setShouldDoFullRepaintAfterLayout(true);
401 m_box->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(layer()->repainter().repaintRect()));
404 // Schedule the scroll DOM event.
406 m_box->node()->document().enqueueScrollEventForNode(m_box->node());
408 InspectorInstrumentation::didScrollLayer(m_box);
411 IntPoint RenderLayerScrollableArea::scrollPosition() const
413 return IntPoint(m_scrollOffset);
416 IntPoint RenderLayerScrollableArea::minimumScrollPosition() const
418 return -scrollOrigin();
421 IntPoint RenderLayerScrollableArea::maximumScrollPosition() const
423 if (!m_box->hasOverflowClip())
424 return -scrollOrigin();
426 return -scrollOrigin() + enclosingIntRect(m_overflowRect).size() - enclosingIntRect(m_box->clientBoxRect()).size();
429 IntRect RenderLayerScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
431 int verticalScrollbarWidth = 0;
432 int horizontalScrollbarHeight = 0;
433 if (scrollbarInclusion == IncludeScrollbars) {
434 verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) ? verticalScrollbar()->width() : 0;
435 horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0;
438 return IntRect(IntPoint(scrollXOffset(), scrollYOffset()),
439 IntSize(max(0, layer()->size().width() - verticalScrollbarWidth), max(0, layer()->size().height() - horizontalScrollbarHeight)));
442 int RenderLayerScrollableArea::visibleHeight() const
444 return layer()->size().height();
447 int RenderLayerScrollableArea::visibleWidth() const
449 return layer()->size().width();
452 IntSize RenderLayerScrollableArea::contentsSize() const
454 return IntSize(scrollWidth(), scrollHeight());
457 IntSize RenderLayerScrollableArea::overhangAmount() const
462 IntPoint RenderLayerScrollableArea::lastKnownMousePosition() const
464 return m_box->frame() ? m_box->frame()->eventHandler().lastKnownMousePosition() : IntPoint();
467 bool RenderLayerScrollableArea::shouldSuspendScrollAnimations() const
469 RenderView* view = m_box->view();
472 return view->frameView()->shouldSuspendScrollAnimations();
475 bool RenderLayerScrollableArea::scrollbarsCanBeActive() const
477 RenderView* view = m_box->view();
480 return view->frameView()->scrollbarsCanBeActive();
483 IntRect RenderLayerScrollableArea::scrollableAreaBoundingBox() const
485 return m_box->absoluteBoundingBoxRect();
488 bool RenderLayerScrollableArea::userInputScrollable(ScrollbarOrientation orientation) const
490 if (m_box->isIntristicallyScrollable(orientation))
493 EOverflow overflowStyle = (orientation == HorizontalScrollbar) ?
494 m_box->style()->overflowX() : m_box->style()->overflowY();
495 return (overflowStyle == OSCROLL || overflowStyle == OAUTO || overflowStyle == OOVERLAY);
498 bool RenderLayerScrollableArea::shouldPlaceVerticalScrollbarOnLeft() const
500 return m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft();
503 int RenderLayerScrollableArea::pageStep(ScrollbarOrientation orientation) const
505 int length = (orientation == HorizontalScrollbar) ?
506 m_box->pixelSnappedClientWidth() : m_box->pixelSnappedClientHeight();
507 int minPageStep = static_cast<float>(length) * ScrollableArea::minFractionToStepWhenPaging();
508 int pageStep = max(minPageStep, length - ScrollableArea::maxOverlapBetweenPages());
510 return max(pageStep, 1);
513 RenderLayer* RenderLayerScrollableArea::layer() const
515 return m_box->layer();
518 int RenderLayerScrollableArea::scrollWidth() const
520 if (m_scrollDimensionsDirty)
521 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
522 return snapSizeToPixel(m_overflowRect.width(), m_box->clientLeft() + m_box->x());
525 int RenderLayerScrollableArea::scrollHeight() const
527 if (m_scrollDimensionsDirty)
528 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions();
529 return snapSizeToPixel(m_overflowRect.height(), m_box->clientTop() + m_box->y());
532 void RenderLayerScrollableArea::computeScrollDimensions()
534 m_scrollDimensionsDirty = false;
536 m_overflowRect = m_box->layoutOverflowRect();
537 m_box->flipForWritingMode(m_overflowRect);
539 int scrollableLeftOverflow = m_overflowRect.x() - m_box->borderLeft() - (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? m_box->verticalScrollbarWidth() : 0);
540 int scrollableTopOverflow = m_overflowRect.y() - m_box->borderTop();
541 setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
544 void RenderLayerScrollableArea::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp)
546 IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset;
547 if (newScrollOffset != adjustedScrollOffset())
548 scrollToOffsetWithoutAnimation(-scrollOrigin() + newScrollOffset);
551 void RenderLayerScrollableArea::updateAfterLayout()
553 // List box parts handle the scrollbars by themselves so we have nothing to do.
554 if (m_box->style()->appearance() == ListboxPart)
557 LayoutRectRecorder recorder(*m_box);
559 m_scrollDimensionsDirty = true;
560 IntSize originalScrollOffset = adjustedScrollOffset();
562 computeScrollDimensions();
564 if (!m_box->isMarquee()) {
565 // Layout may cause us to be at an invalid scroll position. In this case we need
566 // to pull our scroll offsets back to the max (or push them up to the min).
567 IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset());
568 if (clampedScrollOffset != adjustedScrollOffset())
569 scrollToOffset(clampedScrollOffset);
572 if (originalScrollOffset != adjustedScrollOffset())
573 scrollToOffsetWithoutAnimation(-scrollOrigin() + adjustedScrollOffset());
575 bool hasHorizontalOverflow = this->hasHorizontalOverflow();
576 bool hasVerticalOverflow = this->hasVerticalOverflow();
578 // overflow:scroll should just enable/disable.
579 if (m_box->style()->overflowX() == OSCROLL)
580 horizontalScrollbar()->setEnabled(hasHorizontalOverflow);
581 if (m_box->style()->overflowY() == OSCROLL)
582 verticalScrollbar()->setEnabled(hasVerticalOverflow);
584 // overflow:auto may need to lay out again if scrollbars got added/removed.
585 bool autoHorizontalScrollBarChanged = m_box->hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow);
586 bool autoVerticalScrollBarChanged = m_box->hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow);
588 if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) {
589 if (m_box->hasAutoHorizontalScrollbar())
590 setHasHorizontalScrollbar(hasHorizontalOverflow);
591 if (m_box->hasAutoVerticalScrollbar())
592 setHasVerticalScrollbar(hasVerticalOverflow);
594 layer()->updateSelfPaintingLayer();
596 // Force an update since we know the scrollbars have changed things.
597 if (m_box->document().hasAnnotatedRegions())
598 m_box->document().setAnnotatedRegionsDirty(true);
600 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
603 if (m_box->style()->overflowX() == OAUTO || m_box->style()->overflowY() == OAUTO) {
604 if (!m_inOverflowRelayout) {
605 // Our proprietary overflow: overlay value doesn't trigger a layout.
606 m_inOverflowRelayout = true;
607 SubtreeLayoutScope layoutScope(m_box);
608 layoutScope.setNeedsLayout(m_box);
609 if (m_box->isRenderBlock()) {
610 RenderBlock* block = toRenderBlock(m_box);
611 block->scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged);
612 block->layoutBlock(true);
616 m_inOverflowRelayout = false;
621 // Set up the range (and page step/line step).
622 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
623 int clientWidth = m_box->pixelSnappedClientWidth();
624 horizontalScrollbar->setProportion(clientWidth, overflowRect().width());
626 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) {
627 int clientHeight = m_box->pixelSnappedClientHeight();
628 verticalScrollbar->setProportion(clientHeight, overflowRect().height());
631 updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
634 // FIXME: We should not be allowing repaint during layout. crbug.com/336251
635 AllowRepaintScope scoper(m_box->view()->frameView());
637 // FIXME: Remove incremental compositing updates after fixing the chicken/egg issues
638 // https://code.google.com/p/chromium/issues/detail?id=343756
639 DisableCompositingQueryAsserts disabler;
640 m_box->view()->compositor()->updateLayerCompositingState(m_box->layer());
644 bool RenderLayerScrollableArea::hasHorizontalOverflow() const
646 ASSERT(!m_scrollDimensionsDirty);
648 return scrollWidth() > m_box->pixelSnappedClientWidth();
651 bool RenderLayerScrollableArea::hasVerticalOverflow() const
653 ASSERT(!m_scrollDimensionsDirty);
655 return scrollHeight() > m_box->pixelSnappedClientHeight();
658 bool RenderLayerScrollableArea::hasScrollableHorizontalOverflow() const
660 return hasHorizontalOverflow() && m_box->scrollsOverflowX();
663 bool RenderLayerScrollableArea::hasScrollableVerticalOverflow() const
665 return hasVerticalOverflow() && m_box->scrollsOverflowY();
668 static bool overflowRequiresScrollbar(EOverflow overflow)
670 return overflow == OSCROLL;
673 static bool overflowDefinesAutomaticScrollbar(EOverflow overflow)
675 return overflow == OAUTO || overflow == OOVERLAY;
678 void RenderLayerScrollableArea::updateAfterStyleChange(const RenderStyle* oldStyle)
680 // List box parts handle the scrollbars by themselves so we have nothing to do.
681 if (m_box->style()->appearance() == ListboxPart)
684 // RenderView shouldn't provide scrollbars on its own.
685 if (m_box->isRenderView())
688 if (!m_scrollDimensionsDirty)
689 updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
691 EOverflow overflowX = m_box->style()->overflowX();
692 EOverflow overflowY = m_box->style()->overflowY();
694 // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep any automatic scrollbar that was already present.
695 bool needsHorizontalScrollbar = (hasHorizontalScrollbar() && overflowDefinesAutomaticScrollbar(overflowX)) || overflowRequiresScrollbar(overflowX);
696 bool needsVerticalScrollbar = (hasVerticalScrollbar() && overflowDefinesAutomaticScrollbar(overflowY)) || overflowRequiresScrollbar(overflowY);
697 setHasHorizontalScrollbar(needsHorizontalScrollbar);
698 setHasVerticalScrollbar(needsVerticalScrollbar);
700 // With overflow: scroll, scrollbars are always visible but may be disabled.
701 // When switching to another value, we need to re-enable them (see bug 11985).
702 if (needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL) {
703 ASSERT(hasHorizontalScrollbar());
704 m_hBar->setEnabled(true);
707 if (needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL) {
708 ASSERT(hasVerticalScrollbar());
709 m_vBar->setEnabled(true);
712 // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa).
714 m_hBar->styleChanged();
716 m_vBar->styleChanged();
718 updateScrollCornerStyle();
719 updateResizerAreaSet();
720 updateResizerStyle();
723 IntSize RenderLayerScrollableArea::clampScrollOffset(const IntSize& scrollOffset) const
725 int maxX = scrollWidth() - m_box->pixelSnappedClientWidth();
726 int maxY = scrollHeight() - m_box->pixelSnappedClientHeight();
728 int x = std::max(std::min(scrollOffset.width(), maxX), 0);
729 int y = std::max(std::min(scrollOffset.height(), maxY), 0);
730 return IntSize(x, y);
733 IntRect RenderLayerScrollableArea::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const
738 const IntRect& scrollCorner = scrollCornerRect();
740 return IntRect(horizontalScrollbarStart(borderBoxRect.x()),
741 borderBoxRect.maxY() - m_box->borderBottom() - m_hBar->height(),
742 borderBoxRect.width() - (m_box->borderLeft() + m_box->borderRight()) - scrollCorner.width(),
746 IntRect RenderLayerScrollableArea::rectForVerticalScrollbar(const IntRect& borderBoxRect) const
751 const IntRect& scrollCorner = scrollCornerRect();
753 return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()),
754 borderBoxRect.y() + m_box->borderTop(),
756 borderBoxRect.height() - (m_box->borderTop() + m_box->borderBottom()) - scrollCorner.height());
759 LayoutUnit RenderLayerScrollableArea::verticalScrollbarStart(int minX, int maxX) const
761 if (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
762 return minX + m_box->borderLeft();
763 return maxX - m_box->borderRight() - m_vBar->width();
766 LayoutUnit RenderLayerScrollableArea::horizontalScrollbarStart(int minX) const
768 int x = minX + m_box->borderLeft();
769 if (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
770 x += m_vBar ? m_vBar->width() : resizerCornerRect(m_box->pixelSnappedBorderBoxRect(), ResizerForPointer).width();
774 IntSize RenderLayerScrollableArea::scrollbarOffset(const Scrollbar* scrollbar) const
776 if (scrollbar == m_vBar.get())
777 return IntSize(verticalScrollbarStart(0, m_box->width()), m_box->borderTop());
779 if (scrollbar == m_hBar.get())
780 return IntSize(horizontalScrollbarStart(0), m_box->height() - m_box->borderBottom() - scrollbar->height());
782 ASSERT_NOT_REACHED();
786 static inline RenderObject* rendererForScrollbar(RenderObject* renderer)
788 if (Node* node = renderer->node()) {
789 if (ShadowRoot* shadowRoot = node->containingShadowRoot()) {
790 if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
791 return shadowRoot->host()->renderer();
798 PassRefPtr<Scrollbar> RenderLayerScrollableArea::createScrollbar(ScrollbarOrientation orientation)
800 RefPtr<Scrollbar> widget;
801 RenderObject* actualRenderer = rendererForScrollbar(m_box);
802 bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR);
803 if (hasCustomScrollbarStyle) {
804 widget = RenderScrollbar::createCustomScrollbar(this, orientation, actualRenderer->node());
806 widget = Scrollbar::create(this, orientation, RegularScrollbar);
807 if (orientation == HorizontalScrollbar)
808 didAddScrollbar(widget.get(), HorizontalScrollbar);
810 didAddScrollbar(widget.get(), VerticalScrollbar);
812 m_box->document().view()->addChild(widget.get());
813 return widget.release();
816 void RenderLayerScrollableArea::destroyScrollbar(ScrollbarOrientation orientation)
818 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
822 if (!scrollbar->isCustomScrollbar())
823 willRemoveScrollbar(scrollbar.get(), orientation);
825 scrollbar->removeFromParent();
826 scrollbar->disconnectFromScrollableArea();
830 void RenderLayerScrollableArea::setHasHorizontalScrollbar(bool hasScrollbar)
832 if (hasScrollbar == hasHorizontalScrollbar())
836 m_hBar = createScrollbar(HorizontalScrollbar);
838 destroyScrollbar(HorizontalScrollbar);
840 // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
842 m_hBar->styleChanged();
844 m_vBar->styleChanged();
846 // Force an update since we know the scrollbars have changed things.
847 if (m_box->document().hasAnnotatedRegions())
848 m_box->document().setAnnotatedRegionsDirty(true);
851 void RenderLayerScrollableArea::setHasVerticalScrollbar(bool hasScrollbar)
853 if (hasScrollbar == hasVerticalScrollbar())
857 m_vBar = createScrollbar(VerticalScrollbar);
859 destroyScrollbar(VerticalScrollbar);
861 // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style.
863 m_hBar->styleChanged();
865 m_vBar->styleChanged();
867 // Force an update since we know the scrollbars have changed things.
868 if (m_box->document().hasAnnotatedRegions())
869 m_box->document().setAnnotatedRegionsDirty(true);
872 int RenderLayerScrollableArea::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const
874 if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting())))
876 return m_vBar->width();
879 int RenderLayerScrollableArea::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const
881 if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting())))
883 return m_hBar->height();
886 void RenderLayerScrollableArea::positionOverflowControls()
888 RenderGeometryMap geometryMap(UseTransforms);
889 RenderView* view = m_box->view();
890 if (m_box->layer() != view->layer() && m_box->layer()->parent())
891 geometryMap.pushMappingsToAncestor(m_box->layer()->parent(), 0);
893 LayoutPoint offsetFromRoot = LayoutPoint(geometryMap.absolutePoint(FloatPoint()));
894 positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot)));
897 void RenderLayerScrollableArea::positionOverflowControls(const IntSize& offsetFromRoot)
899 if (!hasScrollbar() && !m_box->canResize())
902 const IntRect borderBox = m_box->pixelSnappedBorderBoxRect();
903 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) {
904 IntRect vBarRect = rectForVerticalScrollbar(borderBox);
905 vBarRect.move(offsetFromRoot);
906 verticalScrollbar->setFrameRect(vBarRect);
909 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
910 IntRect hBarRect = rectForHorizontalScrollbar(borderBox);
911 hBarRect.move(offsetFromRoot);
912 horizontalScrollbar->setFrameRect(hBarRect);
915 const IntRect& scrollCorner = scrollCornerRect();
917 m_scrollCorner->setFrameRect(scrollCorner);
920 m_resizer->setFrameRect(resizerCornerRect(borderBox, ResizerForPointer));
922 // FIXME, this should eventually be removed, once we are certain that composited
923 // controls get correctly positioned on a compositor update. For now, conservatively
924 // leaving this unchanged.
925 if (m_box->hasCompositedLayerMapping())
926 m_box->compositedLayerMapping()->positionOverflowControlsLayers(offsetFromRoot);
929 bool RenderLayerScrollableArea::scrollsOverflow() const
931 if (FrameView* frameView = m_box->view()->frameView())
932 return frameView->containsScrollableArea(this);
937 void RenderLayerScrollableArea::updateScrollCornerStyle()
939 RenderObject* actualRenderer = rendererForScrollbar(m_box);
940 RefPtr<RenderStyle> corner = m_box->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), actualRenderer->style()) : PassRefPtr<RenderStyle>(0);
942 if (!m_scrollCorner) {
943 m_scrollCorner = RenderScrollbarPart::createAnonymous(&m_box->document());
944 m_scrollCorner->setParent(m_box);
946 m_scrollCorner->setStyle(corner.release());
947 } else if (m_scrollCorner) {
948 m_scrollCorner->destroy();
953 void RenderLayerScrollableArea::paintOverflowControls(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
955 // Don't do anything if we have no overflow.
956 if (!m_box->hasOverflowClip())
959 IntPoint adjustedPaintOffset = paintOffset;
960 if (paintingOverlayControls)
961 adjustedPaintOffset = m_cachedOverlayScrollbarOffset;
963 // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout,
964 // but sometimes widgets can move without layout occurring (most notably when you scroll a
965 // document that contains fixed positioned elements).
966 positionOverflowControls(toIntSize(adjustedPaintOffset));
968 // Overlay scrollbars paint in a second pass through the layer tree so that they will paint
969 // on top of everything else. If this is the normal painting pass, paintingOverlayControls
970 // will be false, and we should just tell the root layer that there are overlay scrollbars
971 // that need to be painted. That will cause the second pass through the layer tree to run,
972 // and we'll paint the scrollbars then. In the meantime, cache tx and ty so that the
973 // second pass doesn't need to re-enter the RenderTree to get it right.
974 if (hasOverlayScrollbars() && !paintingOverlayControls) {
975 m_cachedOverlayScrollbarOffset = paintOffset;
976 // It's not necessary to do the second pass if the scrollbars paint into layers.
977 if ((m_hBar && layerForHorizontalScrollbar()) || (m_vBar && layerForVerticalScrollbar()))
979 IntRect localDamgeRect = damageRect;
980 localDamgeRect.moveBy(-paintOffset);
981 if (!overflowControlsIntersectRect(localDamgeRect))
984 RenderView* renderView = m_box->view();
986 RenderLayer* paintingRoot = layer()->enclosingCompositingLayer();
988 paintingRoot = renderView->layer();
990 paintingRoot->setContainsDirtyOverlayScrollbars(true);
994 // This check is required to avoid painting custom CSS scrollbars twice.
995 if (paintingOverlayControls && !hasOverlayScrollbars())
998 // Now that we're sure the scrollbars are in the right place, paint them.
999 if (m_hBar && !layerForHorizontalScrollbar())
1000 m_hBar->paint(context, damageRect);
1001 if (m_vBar && !layerForVerticalScrollbar())
1002 m_vBar->paint(context, damageRect);
1004 if (layerForScrollCorner())
1007 // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the
1009 paintScrollCorner(context, adjustedPaintOffset, damageRect);
1011 // Paint our resizer last, since it sits on top of the scroll corner.
1012 paintResizer(context, adjustedPaintOffset, damageRect);
1015 void RenderLayerScrollableArea::paintScrollCorner(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
1017 IntRect absRect = scrollCornerRect();
1018 absRect.moveBy(paintOffset);
1019 if (!absRect.intersects(damageRect))
1022 if (context->updatingControlTints()) {
1023 updateScrollCornerStyle();
1027 if (m_scrollCorner) {
1028 m_scrollCorner->paintIntoRect(context, paintOffset, absRect);
1032 // We don't want to paint white if we have overlay scrollbars, since we need
1033 // to see what is behind it.
1034 if (!hasOverlayScrollbars())
1035 context->fillRect(absRect, Color::white);
1038 bool RenderLayerScrollableArea::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint)
1040 if (!hasScrollbar() && !m_box->canResize())
1043 IntRect resizeControlRect;
1044 if (m_box->style()->resize() != RESIZE_NONE) {
1045 resizeControlRect = resizerCornerRect(m_box->pixelSnappedBorderBoxRect(), ResizerForPointer);
1046 if (resizeControlRect.contains(localPoint))
1050 int resizeControlSize = max(resizeControlRect.height(), 0);
1051 if (m_vBar && m_vBar->shouldParticipateInHitTesting()) {
1052 LayoutRect vBarRect(verticalScrollbarStart(0, m_box->width()),
1055 m_box->height() - (m_box->borderTop() + m_box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize));
1056 if (vBarRect.contains(localPoint)) {
1057 result.setScrollbar(m_vBar.get());
1062 resizeControlSize = max(resizeControlRect.width(), 0);
1063 if (m_hBar && m_hBar->shouldParticipateInHitTesting()) {
1064 LayoutRect hBarRect(horizontalScrollbarStart(0),
1065 m_box->height() - m_box->borderBottom() - m_hBar->height(),
1066 m_box->width() - (m_box->borderLeft() + m_box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize),
1068 if (hBarRect.contains(localPoint)) {
1069 result.setScrollbar(m_hBar.get());
1074 // FIXME: We should hit test the m_scrollCorner and pass it back through the result.
1079 IntRect RenderLayerScrollableArea::resizerCornerRect(const IntRect& bounds, ResizerHitTestType resizerHitTestType) const
1081 if (m_box->style()->resize() == RESIZE_NONE)
1083 IntRect corner = cornerRect(m_box->style(), horizontalScrollbar(), verticalScrollbar(), bounds);
1085 if (resizerHitTestType == ResizerForTouch) {
1086 // We make the resizer virtually larger for touch hit testing. With the
1087 // expanding ratio k = ResizerControlExpandRatioForTouch, we first move
1088 // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)),
1089 // then expand the rect by new_w/h = w/h * k.
1090 int expandRatio = ResizerControlExpandRatioForTouch - 1;
1091 corner.move(-corner.width() * expandRatio, -corner.height() * expandRatio);
1092 corner.expand(corner.width() * expandRatio, corner.height() * expandRatio);
1098 IntRect RenderLayerScrollableArea::scrollCornerAndResizerRect() const
1100 IntRect scrollCornerAndResizer = scrollCornerRect();
1101 if (scrollCornerAndResizer.isEmpty())
1102 scrollCornerAndResizer = resizerCornerRect(m_box->pixelSnappedBorderBoxRect(), ResizerForPointer);
1103 return scrollCornerAndResizer;
1106 bool RenderLayerScrollableArea::overflowControlsIntersectRect(const IntRect& localRect) const
1108 const IntRect borderBox = m_box->pixelSnappedBorderBoxRect();
1110 if (rectForHorizontalScrollbar(borderBox).intersects(localRect))
1113 if (rectForVerticalScrollbar(borderBox).intersects(localRect))
1116 if (scrollCornerRect().intersects(localRect))
1119 if (resizerCornerRect(borderBox, ResizerForPointer).intersects(localRect))
1125 void RenderLayerScrollableArea::paintResizer(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
1127 if (m_box->style()->resize() == RESIZE_NONE)
1130 IntRect absRect = resizerCornerRect(m_box->pixelSnappedBorderBoxRect(), ResizerForPointer);
1131 absRect.moveBy(paintOffset);
1132 if (!absRect.intersects(damageRect))
1135 if (context->updatingControlTints()) {
1136 updateResizerStyle();
1141 m_resizer->paintIntoRect(context, paintOffset, absRect);
1145 drawPlatformResizerImage(context, absRect);
1147 // Draw a frame around the resizer (1px grey line) if there are any scrollbars present.
1148 // Clipping will exclude the right and bottom edges of this frame.
1149 if (!hasOverlayScrollbars() && hasScrollbar()) {
1150 GraphicsContextStateSaver stateSaver(*context);
1151 context->clip(absRect);
1152 IntRect largerCorner = absRect;
1153 largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1));
1154 context->setStrokeColor(Color(217, 217, 217));
1155 context->setStrokeThickness(1.0f);
1156 context->setFillColor(Color::transparent);
1157 context->drawRect(largerCorner);
1161 bool RenderLayerScrollableArea::isPointInResizeControl(const IntPoint& absolutePoint, ResizerHitTestType resizerHitTestType) const
1163 if (!m_box->canResize())
1166 IntPoint localPoint = roundedIntPoint(m_box->absoluteToLocal(absolutePoint, UseTransforms));
1167 IntRect localBounds(0, 0, m_box->pixelSnappedWidth(), m_box->pixelSnappedHeight());
1168 return resizerCornerRect(localBounds, resizerHitTestType).contains(localPoint);
1171 bool RenderLayerScrollableArea::hitTestResizerInFragments(const LayerFragments& layerFragments, const HitTestLocation& hitTestLocation) const
1173 if (!m_box->canResize())
1176 if (layerFragments.isEmpty())
1179 for (int i = layerFragments.size() - 1; i >= 0; --i) {
1180 const LayerFragment& fragment = layerFragments.at(i);
1181 if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(pixelSnappedIntRect(fragment.layerBounds), ResizerForPointer).contains(hitTestLocation.roundedPoint()))
1188 void RenderLayerScrollableArea::updateResizerAreaSet()
1190 Frame* frame = m_box->frame();
1193 FrameView* frameView = frame->view();
1196 if (m_box->canResize())
1197 frameView->addResizerArea(m_box);
1199 frameView->removeResizerArea(m_box);
1202 void RenderLayerScrollableArea::updateResizerStyle()
1204 RenderObject* actualRenderer = rendererForScrollbar(m_box);
1205 RefPtr<RenderStyle> resizer = m_box->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), actualRenderer->style()) : PassRefPtr<RenderStyle>(0);
1208 m_resizer = RenderScrollbarPart::createAnonymous(&m_box->document());
1209 m_resizer->setParent(m_box);
1211 m_resizer->setStyle(resizer.release());
1212 } else if (m_resizer) {
1213 m_resizer->destroy();
1218 void RenderLayerScrollableArea::drawPlatformResizerImage(GraphicsContext* context, IntRect resizerCornerRect)
1220 float deviceScaleFactor = WebCore::deviceScaleFactor(m_box->frame());
1222 RefPtr<Image> resizeCornerImage;
1223 IntSize cornerResizerSize;
1224 if (deviceScaleFactor >= 2) {
1225 DEFINE_STATIC_REF(Image, resizeCornerImageHiRes, (Image::loadPlatformResource("textAreaResizeCorner@2x")));
1226 resizeCornerImage = resizeCornerImageHiRes;
1227 cornerResizerSize = resizeCornerImage->size();
1228 cornerResizerSize.scale(0.5f);
1230 DEFINE_STATIC_REF(Image, resizeCornerImageLoRes, (Image::loadPlatformResource("textAreaResizeCorner")));
1231 resizeCornerImage = resizeCornerImageLoRes;
1232 cornerResizerSize = resizeCornerImage->size();
1235 if (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1237 context->translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height());
1238 context->scale(FloatSize(-1.0, 1.0));
1239 context->drawImage(resizeCornerImage.get(), IntRect(IntPoint(), cornerResizerSize));
1243 IntRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize);
1244 context->drawImage(resizeCornerImage.get(), imageRect);
1247 IntSize RenderLayerScrollableArea::offsetFromResizeCorner(const IntPoint& absolutePoint) const
1249 // Currently the resize corner is either the bottom right corner or the bottom left corner.
1250 // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be the case?
1251 IntSize elementSize = layer()->size();
1252 if (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1253 elementSize.setWidth(0);
1254 IntPoint resizerPoint = IntPoint(elementSize);
1255 IntPoint localPoint = roundedIntPoint(m_box->absoluteToLocal(absolutePoint, UseTransforms));
1256 return localPoint - resizerPoint;
1259 void RenderLayerScrollableArea::resize(const PlatformEvent& evt, const LayoutSize& oldOffset)
1261 // FIXME: This should be possible on generated content but is not right now.
1262 if (!inResizeMode() || !m_box->canResize() || !m_box->node())
1265 ASSERT(m_box->node()->isElementNode());
1266 Element* element = toElement(m_box->node());
1268 Document& document = element->document();
1271 const PlatformGestureEvent* gevt = 0;
1273 switch (evt.type()) {
1274 case PlatformEvent::MouseMoved:
1275 if (!document.frame()->eventHandler().mousePressed())
1277 pos = static_cast<const PlatformMouseEvent*>(&evt)->position();
1279 case PlatformEvent::GestureScrollUpdate:
1280 case PlatformEvent::GestureScrollUpdateWithoutPropagation:
1281 pos = static_cast<const PlatformGestureEvent*>(&evt)->position();
1282 gevt = static_cast<const PlatformGestureEvent*>(&evt);
1283 pos = gevt->position();
1284 pos.move(gevt->deltaX(), gevt->deltaY());
1287 ASSERT_NOT_REACHED();
1290 float zoomFactor = m_box->style()->effectiveZoom();
1292 LayoutSize newOffset = offsetFromResizeCorner(document.view()->windowToContents(pos));
1293 newOffset.setWidth(newOffset.width() / zoomFactor);
1294 newOffset.setHeight(newOffset.height() / zoomFactor);
1296 LayoutSize currentSize = LayoutSize(m_box->width() / zoomFactor, m_box->height() / zoomFactor);
1297 LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize);
1298 element->setMinimumSizeForResizing(minimumSize);
1300 LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor);
1301 if (m_box->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
1302 newOffset.setWidth(-newOffset.width());
1303 adjustedOldOffset.setWidth(-adjustedOldOffset.width());
1306 LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize;
1308 bool isBoxSizingBorder = m_box->style()->boxSizing() == BORDER_BOX;
1310 EResize resize = m_box->style()->resize();
1311 if (resize != RESIZE_VERTICAL && difference.width()) {
1312 if (element->isFormControlElement()) {
1313 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1314 element->setInlineStyleProperty(CSSPropertyMarginLeft, m_box->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1315 element->setInlineStyleProperty(CSSPropertyMarginRight, m_box->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1317 LayoutUnit baseWidth = m_box->width() - (isBoxSizingBorder ? LayoutUnit() : m_box->borderAndPaddingWidth());
1318 baseWidth = baseWidth / zoomFactor;
1319 element->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX);
1322 if (resize != RESIZE_HORIZONTAL && difference.height()) {
1323 if (element->isFormControlElement()) {
1324 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>).
1325 element->setInlineStyleProperty(CSSPropertyMarginTop, m_box->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1326 element->setInlineStyleProperty(CSSPropertyMarginBottom, m_box->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX);
1328 LayoutUnit baseHeight = m_box->height() - (isBoxSizingBorder ? LayoutUnit() : m_box->borderAndPaddingHeight());
1329 baseHeight = baseHeight / zoomFactor;
1330 element->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX);
1333 document.updateLayout();
1335 // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view.
1338 LayoutRect RenderLayerScrollableArea::exposeRect(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
1340 LayoutRect localExposeRect(m_box->absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox());
1341 LayoutRect layerBounds(0, 0, m_box->clientWidth(), m_box->clientHeight());
1342 LayoutRect r = ScrollAlignment::getRectToExpose(layerBounds, localExposeRect, alignX, alignY);
1344 IntSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location()));
1345 if (clampedScrollOffset == adjustedScrollOffset())
1348 IntSize oldScrollOffset = adjustedScrollOffset();
1349 scrollToOffset(clampedScrollOffset);
1350 IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset;
1351 localExposeRect.move(-scrollOffsetDifference);
1352 return LayoutRect(m_box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox());
1355 void RenderLayerScrollableArea::updateScrollableAreaSet(bool hasOverflow)
1357 Frame* frame = m_box->frame();
1361 FrameView* frameView = frame->view();
1365 bool isVisibleToHitTest = m_box->visibleToHitTesting();
1366 if (HTMLFrameOwnerElement* owner = frame->ownerElement())
1367 isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToHitTesting();
1369 bool requiresScrollableArea = hasOverflow && isVisibleToHitTest;
1370 bool updatedScrollableAreaSet = false;
1371 if (requiresScrollableArea) {
1372 if (frameView->addScrollableArea(this))
1373 updatedScrollableAreaSet = true;
1375 if (frameView->removeScrollableArea(this))
1376 updatedScrollableAreaSet = true;
1379 if (updatedScrollableAreaSet) {
1380 // Count the total number of RenderLayers that are scrollable areas for
1381 // any period. We only want to record this at most once per RenderLayer.
1382 if (requiresScrollableArea && !m_isScrollableAreaHasBeenRecorded) {
1383 blink::Platform::current()->histogramEnumeration("Renderer.CompositedScrolling", IsScrollableAreaBucket, CompositedScrollingHistogramMax);
1384 m_isScrollableAreaHasBeenRecorded = true;
1387 // We always want composited scrolling if compositor driven accelerated
1388 // scrolling is enabled. Since we will not update needs composited scrolling
1389 // in this case, we must force our state to update.
1390 if (m_box->compositorDrivenAcceleratedScrollingEnabled())
1391 layer()->didUpdateNeedsCompositedScrolling();
1392 else if (requiresScrollableArea)
1393 m_box->view()->compositor()->setNeedsUpdateCompositingRequirementsState();
1395 setNeedsCompositedScrolling(false);
1399 void RenderLayerScrollableArea::updateNeedsCompositedScrolling()
1401 TRACE_EVENT0("comp-scroll", "RenderLayer::updateNeedsCompositedScrolling");
1403 layer()->stackingNode()->updateDescendantsAreContiguousInStackingOrder();
1404 layer()->updateDescendantDependentFlags();
1406 ASSERT(scrollsOverflow());
1407 const bool needsToBeStackingContainer = m_box->acceleratedCompositingForOverflowScrollEnabled()
1408 && layer()->stackingNode()->descendantsAreContiguousInStackingOrder()
1409 && !layer()->hasUnclippedDescendant();
1411 const bool needsToBeStackingContainerDidChange = layer()->stackingNode()->setNeedsToBeStackingContainer(needsToBeStackingContainer);
1413 const bool needsCompositedScrolling = needsToBeStackingContainer
1414 || m_box->compositorDrivenAcceleratedScrollingEnabled();
1416 // We gather a boolean value for use with Google UMA histograms to
1417 // quantify the actual effects of a set of patches attempting to
1418 // relax composited scrolling requirements, thereby increasing the
1419 // number of composited overflow divs.
1420 if (m_box->acceleratedCompositingForOverflowScrollEnabled())
1421 blink::Platform::current()->histogramEnumeration("Renderer.NeedsCompositedScrolling", needsCompositedScrolling, 2);
1423 const bool needsCompositedScrollingDidChange = setNeedsCompositedScrolling(needsCompositedScrolling);
1425 if (needsToBeStackingContainerDidChange || needsCompositedScrollingDidChange) {
1426 // Note, the z-order lists may need to be rebuilt, but our code guarantees
1427 // that we have not affected stacking, so we will not dirty
1428 // m_descendantsAreContiguousInStackingOrder for either us or our stacking
1429 // context or container.
1430 layer()->didUpdateNeedsCompositedScrolling();
1434 bool RenderLayerScrollableArea::setNeedsCompositedScrolling(bool needsCompositedScrolling)
1436 if (this->needsCompositedScrolling() == needsCompositedScrolling)
1439 // Count the total number of RenderLayers which need composited scrolling at
1440 // some point. This should be recorded at most once per RenderLayer, so we
1441 // check m_willUseCompositedScrollingHasBeenRecorded.
1442 if (m_box->acceleratedCompositingForOverflowScrollEnabled() && !m_willUseCompositedScrollingHasBeenRecorded) {
1443 blink::Platform::current()->histogramEnumeration("Renderer.CompositedScrolling", WillUseCompositedScrollingBucket, CompositedScrollingHistogramMax);
1444 m_willUseCompositedScrollingHasBeenRecorded = true;
1447 m_needsCompositedScrolling = needsCompositedScrolling;
1452 void RenderLayerScrollableArea::updateHasVisibleNonLayerContent()
1454 layer()->updateHasVisibleNonLayerContent();
1457 void RenderLayerScrollableArea::updateCompositingLayersAfterScroll()
1459 RenderLayerCompositor* compositor = m_box->view()->compositor();
1460 if (compositor->inCompositingMode()) {
1461 // FIXME: Our stacking container is guaranteed to contain all of our descendants that may need
1462 // repositioning, so we should be able to enqueue a partial update compositing layers from there.
1463 // this feature was overridden for now by deferred compositing updates.
1464 if (usesCompositedScrolling())
1465 compositor->setNeedsCompositingUpdate(CompositingUpdateOnCompositedScroll);
1467 compositor->setNeedsCompositingUpdate(CompositingUpdateOnScroll);
1471 bool RenderLayerScrollableArea::usesCompositedScrolling() const
1473 // Scroll form controls on the main thread so they exhibit correct touch scroll event bubbling
1474 if (m_box && (m_box->isIntristicallyScrollable(VerticalScrollbar) || m_box->isIntristicallyScrollable(HorizontalScrollbar)))
1477 return m_box->hasCompositedLayerMapping() && m_box->compositedLayerMapping()->scrollingLayer();
1480 bool RenderLayerScrollableArea::adjustForForceCompositedScrollingMode(bool value) const
1482 switch (m_forceNeedsCompositedScrolling) {
1483 case DoNotForceCompositedScrolling:
1485 case CompositedScrollingAlwaysOn:
1487 case CompositedScrollingAlwaysOff:
1491 ASSERT_NOT_REACHED();
1495 bool RenderLayerScrollableArea::needsCompositedScrolling() const
1497 return adjustForForceCompositedScrollingMode(m_needsCompositedScrolling);
1500 void RenderLayerScrollableArea::setForceNeedsCompositedScrolling(ForceNeedsCompositedScrollingMode mode)
1502 if (m_forceNeedsCompositedScrolling == mode)
1505 m_forceNeedsCompositedScrolling = mode;
1506 layer()->didUpdateNeedsCompositedScrolling();
1509 } // Namespace WebCore