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 "config.h" | |
45 #include "core/rendering/RenderLayer.h" | |
46 | |
47 #include "core/css/PseudoStyleRequest.h" | |
48 #include "core/dom/AXObjectCache.h" | |
49 #include "core/dom/Node.h" | |
50 #include "core/dom/shadow/ShadowRoot.h" | |
51 #include "core/editing/FrameSelection.h" | |
52 #include "core/frame/FrameView.h" | |
53 #include "core/frame/LocalFrame.h" | |
54 #include "core/frame/Settings.h" | |
55 #include "core/html/HTMLFrameOwnerElement.h" | |
56 #include "core/inspector/InspectorInstrumentation.h" | |
57 #include "core/layout/LayoutTheme.h" | |
58 #include "core/layout/compositing/CompositedLayerMapping.h" | |
59 #include "core/layout/compositing/RenderLayerCompositor.h" | |
60 #include "core/page/Chrome.h" | |
61 #include "core/page/EventHandler.h" | |
62 #include "core/page/FocusController.h" | |
63 #include "core/page/Page.h" | |
64 #include "core/page/scrolling/ScrollingCoordinator.h" | |
65 #include "core/rendering/RenderGeometryMap.h" | |
66 #include "core/rendering/RenderScrollbar.h" | |
67 #include "core/rendering/RenderScrollbarPart.h" | |
68 #include "core/rendering/RenderView.h" | |
69 #include "platform/PlatformGestureEvent.h" | |
70 #include "platform/PlatformMouseEvent.h" | |
71 #include "platform/graphics/GraphicsContextStateSaver.h" | |
72 #include "platform/graphics/GraphicsLayer.h" | |
73 #include "platform/graphics/paint/DrawingRecorder.h" | |
74 #include "platform/scroll/ScrollAnimator.h" | |
75 #include "platform/scroll/ScrollbarTheme.h" | |
76 #include "public/platform/Platform.h" | |
77 | |
78 namespace blink { | |
79 | |
80 const int ResizerControlExpandRatioForTouch = 2; | |
81 | |
82 RenderLayerScrollableArea::RenderLayerScrollableArea(RenderLayer& layer) | |
83 : m_layer(layer) | |
84 , m_inResizeMode(false) | |
85 , m_scrollsOverflow(false) | |
86 , m_scrollDimensionsDirty(true) | |
87 , m_inOverflowRelayout(false) | |
88 , m_nextTopmostScrollChild(0) | |
89 , m_topmostScrollChild(0) | |
90 , m_needsCompositedScrolling(false) | |
91 , m_scrollCorner(nullptr) | |
92 , m_resizer(nullptr) | |
93 { | |
94 ScrollableArea::setConstrainsScrollingToContentEdge(false); | |
95 | |
96 Node* node = box().node(); | |
97 if (node && node->isElementNode()) { | |
98 // We save and restore only the scrollOffset as the other scroll values
are recalculated. | |
99 Element* element = toElement(node); | |
100 m_scrollOffset = element->savedLayerScrollOffset(); | |
101 if (!m_scrollOffset.isZero()) | |
102 scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset.width
(), m_scrollOffset.height())); | |
103 element->setSavedLayerScrollOffset(IntSize()); | |
104 } | |
105 | |
106 updateResizerAreaSet(); | |
107 } | |
108 | |
109 RenderLayerScrollableArea::~RenderLayerScrollableArea() | |
110 { | |
111 if (inResizeMode() && !box().documentBeingDestroyed()) { | |
112 if (LocalFrame* frame = box().frame()) | |
113 frame->eventHandler().resizeScrollableAreaDestroyed(); | |
114 } | |
115 | |
116 if (LocalFrame* frame = box().frame()) { | |
117 if (FrameView* frameView = frame->view()) { | |
118 frameView->removeScrollableArea(this); | |
119 frameView->removeAnimatingScrollableArea(this); | |
120 } | |
121 } | |
122 | |
123 if (box().frame() && box().frame()->page()) { | |
124 if (ScrollingCoordinator* scrollingCoordinator = box().frame()->page()->
scrollingCoordinator()) | |
125 scrollingCoordinator->willDestroyScrollableArea(this); | |
126 } | |
127 | |
128 if (!box().documentBeingDestroyed()) { | |
129 Node* node = box().node(); | |
130 // FIXME: Make setSavedLayerScrollOffset take DoubleSize. crbug.com/4142
83. | |
131 if (node && node->isElementNode()) | |
132 toElement(node)->setSavedLayerScrollOffset(flooredIntSize(m_scrollOf
fset)); | |
133 } | |
134 | |
135 if (LocalFrame* frame = box().frame()) { | |
136 if (FrameView* frameView = frame->view()) | |
137 frameView->removeResizerArea(box()); | |
138 } | |
139 | |
140 destroyScrollbar(HorizontalScrollbar); | |
141 destroyScrollbar(VerticalScrollbar); | |
142 | |
143 if (m_scrollCorner) | |
144 m_scrollCorner->destroy(); | |
145 if (m_resizer) | |
146 m_resizer->destroy(); | |
147 } | |
148 | |
149 HostWindow* RenderLayerScrollableArea::hostWindow() const | |
150 { | |
151 if (Page* page = box().frame()->page()) | |
152 return &page->chrome(); | |
153 return nullptr; | |
154 } | |
155 | |
156 GraphicsLayer* RenderLayerScrollableArea::layerForScrolling() const | |
157 { | |
158 return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMappin
g()->scrollingContentsLayer() : 0; | |
159 } | |
160 | |
161 GraphicsLayer* RenderLayerScrollableArea::layerForHorizontalScrollbar() const | |
162 { | |
163 // See crbug.com/343132. | |
164 DisableCompositingQueryAsserts disabler; | |
165 | |
166 return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMappin
g()->layerForHorizontalScrollbar() : 0; | |
167 } | |
168 | |
169 GraphicsLayer* RenderLayerScrollableArea::layerForVerticalScrollbar() const | |
170 { | |
171 // See crbug.com/343132. | |
172 DisableCompositingQueryAsserts disabler; | |
173 | |
174 return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMappin
g()->layerForVerticalScrollbar() : 0; | |
175 } | |
176 | |
177 GraphicsLayer* RenderLayerScrollableArea::layerForScrollCorner() const | |
178 { | |
179 // See crbug.com/343132. | |
180 DisableCompositingQueryAsserts disabler; | |
181 | |
182 return layer()->hasCompositedLayerMapping() ? layer()->compositedLayerMappin
g()->layerForScrollCorner() : 0; | |
183 } | |
184 | |
185 void RenderLayerScrollableArea::invalidateScrollbarRect(Scrollbar* scrollbar, co
nst IntRect& rect) | |
186 { | |
187 // See crbug.com/343132. | |
188 DisableCompositingQueryAsserts disabler; | |
189 | |
190 if (scrollbar == m_vBar.get()) { | |
191 if (GraphicsLayer* layer = layerForVerticalScrollbar()) { | |
192 layer->setNeedsDisplayInRect(rect, PaintInvalidationScroll); | |
193 return; | |
194 } | |
195 } else { | |
196 if (GraphicsLayer* layer = layerForHorizontalScrollbar()) { | |
197 layer->setNeedsDisplayInRect(rect, PaintInvalidationScroll); | |
198 return; | |
199 } | |
200 } | |
201 | |
202 IntRect scrollRect = rect; | |
203 // If we are not yet inserted into the tree, there is no need to issue paint
invaldiations. | |
204 if (!box().isRenderView() && !box().parent()) | |
205 return; | |
206 | |
207 if (scrollbar == m_vBar.get()) | |
208 scrollRect.move(verticalScrollbarStart(0, box().size().width()), box().b
orderTop()); | |
209 else | |
210 scrollRect.move(horizontalScrollbarStart(0), box().size().height() - box
().borderBottom() - scrollbar->height()); | |
211 | |
212 if (scrollRect.isEmpty()) | |
213 return; | |
214 | |
215 LayoutRect paintInvalidationRect = scrollRect; | |
216 box().flipForWritingMode(paintInvalidationRect); | |
217 | |
218 IntRect intRect = pixelSnappedIntRect(paintInvalidationRect); | |
219 | |
220 if (box().frameView()->isInPerformLayout()) | |
221 addScrollbarDamage(scrollbar, intRect); | |
222 else | |
223 box().invalidatePaintRectangle(intRect); | |
224 } | |
225 | |
226 void RenderLayerScrollableArea::invalidateScrollCornerRect(const IntRect& rect) | |
227 { | |
228 if (GraphicsLayer* layer = layerForScrollCorner()) { | |
229 layer->setNeedsDisplayInRect(rect, PaintInvalidationScroll); | |
230 return; | |
231 } | |
232 | |
233 if (m_scrollCorner) | |
234 m_scrollCorner->invalidatePaintRectangle(rect); | |
235 if (m_resizer) | |
236 m_resizer->invalidatePaintRectangle(rect); | |
237 } | |
238 | |
239 bool RenderLayerScrollableArea::shouldUseIntegerScrollOffset() const | |
240 { | |
241 Frame* frame = box().frame(); | |
242 if (frame->settings() && !frame->settings()->preferCompositingToLCDTextEnabl
ed()) | |
243 return true; | |
244 return false; | |
245 } | |
246 | |
247 bool RenderLayerScrollableArea::isActive() const | |
248 { | |
249 Page* page = box().frame()->page(); | |
250 return page && page->focusController().isActive(); | |
251 } | |
252 | |
253 bool RenderLayerScrollableArea::isScrollCornerVisible() const | |
254 { | |
255 return !scrollCornerRect().isEmpty(); | |
256 } | |
257 | |
258 static int cornerStart(const RenderStyle* style, int minX, int maxX, int thickne
ss) | |
259 { | |
260 if (style->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
261 return minX + style->borderLeftWidth(); | |
262 return maxX - thickness - style->borderRightWidth(); | |
263 } | |
264 | |
265 static IntRect cornerRect(const RenderStyle* style, const Scrollbar* horizontalS
crollbar, const Scrollbar* verticalScrollbar, const IntRect& bounds) | |
266 { | |
267 int horizontalThickness; | |
268 int verticalThickness; | |
269 if (!verticalScrollbar && !horizontalScrollbar) { | |
270 // FIXME: This isn't right. We need to know the thickness of custom scro
llbars | |
271 // even when they don't exist in order to set the resizer square size pr
operly. | |
272 horizontalThickness = ScrollbarTheme::theme()->scrollbarThickness(); | |
273 verticalThickness = horizontalThickness; | |
274 } else if (verticalScrollbar && !horizontalScrollbar) { | |
275 horizontalThickness = verticalScrollbar->width(); | |
276 verticalThickness = horizontalThickness; | |
277 } else if (horizontalScrollbar && !verticalScrollbar) { | |
278 verticalThickness = horizontalScrollbar->height(); | |
279 horizontalThickness = verticalThickness; | |
280 } else { | |
281 horizontalThickness = verticalScrollbar->width(); | |
282 verticalThickness = horizontalScrollbar->height(); | |
283 } | |
284 return IntRect(cornerStart(style, bounds.x(), bounds.maxX(), horizontalThick
ness), | |
285 bounds.maxY() - verticalThickness - style->borderBottomWidth(), | |
286 horizontalThickness, verticalThickness); | |
287 } | |
288 | |
289 | |
290 IntRect RenderLayerScrollableArea::scrollCornerRect() const | |
291 { | |
292 // We have a scrollbar corner when a scrollbar is visible and not filling th
e entire length of the box. | |
293 // This happens when: | |
294 // (a) A resizer is present and at least one scrollbar is present | |
295 // (b) Both scrollbars are present. | |
296 bool hasHorizontalBar = horizontalScrollbar(); | |
297 bool hasVerticalBar = verticalScrollbar(); | |
298 bool hasResizer = box().style()->resize() != RESIZE_NONE; | |
299 if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar
|| hasVerticalBar))) | |
300 return cornerRect(box().style(), horizontalScrollbar(), verticalScrollba
r(), box().pixelSnappedBorderBoxRect()); | |
301 return IntRect(); | |
302 } | |
303 | |
304 IntRect RenderLayerScrollableArea::convertFromScrollbarToContainingView(const Sc
rollbar* scrollbar, const IntRect& scrollbarRect) const | |
305 { | |
306 RenderView* view = box().view(); | |
307 if (!view) | |
308 return scrollbarRect; | |
309 | |
310 IntRect rect = scrollbarRect; | |
311 rect.move(scrollbarOffset(scrollbar)); | |
312 | |
313 return view->frameView()->convertFromRenderer(box(), rect); | |
314 } | |
315 | |
316 IntRect RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const Sc
rollbar* scrollbar, const IntRect& parentRect) const | |
317 { | |
318 RenderView* view = box().view(); | |
319 if (!view) | |
320 return parentRect; | |
321 | |
322 IntRect rect = view->frameView()->convertToRenderer(box(), parentRect); | |
323 rect.move(-scrollbarOffset(scrollbar)); | |
324 return rect; | |
325 } | |
326 | |
327 IntPoint RenderLayerScrollableArea::convertFromScrollbarToContainingView(const S
crollbar* scrollbar, const IntPoint& scrollbarPoint) const | |
328 { | |
329 RenderView* view = box().view(); | |
330 if (!view) | |
331 return scrollbarPoint; | |
332 | |
333 IntPoint point = scrollbarPoint; | |
334 point.move(scrollbarOffset(scrollbar)); | |
335 return view->frameView()->convertFromRenderer(box(), point); | |
336 } | |
337 | |
338 IntPoint RenderLayerScrollableArea::convertFromContainingViewToScrollbar(const S
crollbar* scrollbar, const IntPoint& parentPoint) const | |
339 { | |
340 RenderView* view = box().view(); | |
341 if (!view) | |
342 return parentPoint; | |
343 | |
344 IntPoint point = view->frameView()->convertToRenderer(box(), parentPoint); | |
345 | |
346 point.move(-scrollbarOffset(scrollbar)); | |
347 return point; | |
348 } | |
349 | |
350 int RenderLayerScrollableArea::scrollSize(ScrollbarOrientation orientation) cons
t | |
351 { | |
352 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition()
; | |
353 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scr
ollDimensions.height(); | |
354 } | |
355 | |
356 void RenderLayerScrollableArea::setScrollOffset(const IntPoint& newScrollOffset) | |
357 { | |
358 setScrollOffset(DoublePoint(newScrollOffset)); | |
359 } | |
360 | |
361 void RenderLayerScrollableArea::setScrollOffset(const DoublePoint& newScrollOffs
et) | |
362 { | |
363 // Ensure that the dimensions will be computed if they need to be (for overf
low:hidden blocks). | |
364 if (m_scrollDimensionsDirty) | |
365 computeScrollDimensions(); | |
366 | |
367 if (scrollOffset() == toDoubleSize(newScrollOffset)) | |
368 return; | |
369 | |
370 m_scrollOffset = toDoubleSize(newScrollOffset); | |
371 | |
372 LocalFrame* frame = box().frame(); | |
373 ASSERT(frame); | |
374 | |
375 RefPtrWillBeRawPtr<FrameView> frameView = box().frameView(); | |
376 | |
377 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ScrollLayer",
"data", InspectorScrollLayerEvent::data(&box())); | |
378 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeli
ne migrates to tracing. | |
379 InspectorInstrumentation::willScrollLayer(&box()); | |
380 | |
381 // Update the positions of our child layers (if needed as only fixed layers
should be impacted by a scroll). | |
382 // We don't update compositing layers, because we need to do a deep update f
rom the compositing ancestor. | |
383 if (!frameView->isInPerformLayout()) { | |
384 // If we're in the middle of layout, we'll just update layers once layou
t has finished. | |
385 layer()->updateLayerPositionsAfterOverflowScroll(); | |
386 // Update regions, scrolling may change the clip of a particular region. | |
387 frameView->updateAnnotatedRegions(); | |
388 frameView->setNeedsUpdateWidgetPositions(); | |
389 updateCompositingLayersAfterScroll(); | |
390 } | |
391 | |
392 const RenderLayerModelObject* paintInvalidationContainer = box().containerFo
rPaintInvalidation(); | |
393 // The caret rect needs to be invalidated after scrolling | |
394 frame->selection().setCaretRectNeedsUpdate(); | |
395 | |
396 FloatQuad quadForFakeMouseMoveEvent = FloatQuad(layer()->renderer()->previou
sPaintInvalidationRect()); | |
397 | |
398 quadForFakeMouseMoveEvent = paintInvalidationContainer->localToAbsoluteQuad(
quadForFakeMouseMoveEvent); | |
399 frame->eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseM
oveEvent); | |
400 | |
401 bool requiresPaintInvalidation = true; | |
402 | |
403 { | |
404 // FIXME(420741): Since scrolling depends on compositing state, the scro
ll should be | |
405 // deferred until after the compositing update. | |
406 DisableCompositingQueryAsserts disabler; | |
407 if (box().view()->compositor()->inCompositingMode()) { | |
408 bool onlyScrolledCompositedLayers = scrollsOverflow() | |
409 && !layer()->hasVisibleNonLayerContent() | |
410 && !layer()->hasNonCompositedChild() | |
411 && !layer()->hasBlockSelectionGapBounds() | |
412 && box().style()->backgroundLayers().attachment() != LocalBackgr
oundAttachment; | |
413 | |
414 if (usesCompositedScrolling() || onlyScrolledCompositedLayers) | |
415 requiresPaintInvalidation = false; | |
416 } | |
417 } | |
418 | |
419 // Just schedule a full paint invalidation of our object. | |
420 if (requiresPaintInvalidation) | |
421 box().setShouldDoFullPaintInvalidation(); | |
422 | |
423 // Schedule the scroll DOM event. | |
424 if (box().node()) | |
425 box().node()->document().enqueueScrollEventForNode(box().node()); | |
426 | |
427 if (AXObjectCache* cache = box().document().existingAXObjectCache()) | |
428 cache->handleScrollPositionChanged(&box()); | |
429 | |
430 InspectorInstrumentation::didScrollLayer(&box()); | |
431 } | |
432 | |
433 IntPoint RenderLayerScrollableArea::scrollPosition() const | |
434 { | |
435 return IntPoint(flooredIntSize(m_scrollOffset)); | |
436 } | |
437 | |
438 DoublePoint RenderLayerScrollableArea::scrollPositionDouble() const | |
439 { | |
440 return DoublePoint(m_scrollOffset); | |
441 } | |
442 | |
443 IntPoint RenderLayerScrollableArea::minimumScrollPosition() const | |
444 { | |
445 return -scrollOrigin(); | |
446 } | |
447 | |
448 IntPoint RenderLayerScrollableArea::maximumScrollPosition() const | |
449 { | |
450 if (!box().hasOverflowClip()) | |
451 return -scrollOrigin(); | |
452 return -scrollOrigin() + IntPoint(pixelSnappedScrollWidth(), pixelSnappedScr
ollHeight()) - enclosingIntRect(box().clientBoxRect()).size(); | |
453 } | |
454 | |
455 IntRect RenderLayerScrollableArea::visibleContentRect(IncludeScrollbarsInRect sc
rollbarInclusion) const | |
456 { | |
457 int verticalScrollbarWidth = 0; | |
458 int horizontalScrollbarHeight = 0; | |
459 if (scrollbarInclusion == IncludeScrollbars) { | |
460 verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->i
sOverlayScrollbar()) ? verticalScrollbar()->width() : 0; | |
461 horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollb
ar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0; | |
462 } | |
463 | |
464 return IntRect(IntPoint(scrollXOffset(), scrollYOffset()), | |
465 IntSize(max(0, layer()->size().width() - verticalScrollbarWidth), max(0,
layer()->size().height() - horizontalScrollbarHeight))); | |
466 } | |
467 | |
468 int RenderLayerScrollableArea::visibleHeight() const | |
469 { | |
470 return layer()->size().height(); | |
471 } | |
472 | |
473 int RenderLayerScrollableArea::visibleWidth() const | |
474 { | |
475 return layer()->size().width(); | |
476 } | |
477 | |
478 IntSize RenderLayerScrollableArea::contentsSize() const | |
479 { | |
480 return IntSize(scrollWidth(), scrollHeight()); | |
481 } | |
482 | |
483 IntSize RenderLayerScrollableArea::overhangAmount() const | |
484 { | |
485 return IntSize(); | |
486 } | |
487 | |
488 IntPoint RenderLayerScrollableArea::lastKnownMousePosition() const | |
489 { | |
490 return box().frame() ? box().frame()->eventHandler().lastKnownMousePosition(
) : IntPoint(); | |
491 } | |
492 | |
493 bool RenderLayerScrollableArea::shouldSuspendScrollAnimations() const | |
494 { | |
495 RenderView* view = box().view(); | |
496 if (!view) | |
497 return true; | |
498 return view->frameView()->shouldSuspendScrollAnimations(); | |
499 } | |
500 | |
501 bool RenderLayerScrollableArea::scrollbarsCanBeActive() const | |
502 { | |
503 RenderView* view = box().view(); | |
504 if (!view) | |
505 return false; | |
506 return view->frameView()->scrollbarsCanBeActive(); | |
507 } | |
508 | |
509 IntRect RenderLayerScrollableArea::scrollableAreaBoundingBox() const | |
510 { | |
511 return box().absoluteBoundingBoxRect(); | |
512 } | |
513 | |
514 void RenderLayerScrollableArea::registerForAnimation() | |
515 { | |
516 if (LocalFrame* frame = box().frame()) { | |
517 if (FrameView* frameView = frame->view()) | |
518 frameView->addAnimatingScrollableArea(this); | |
519 } | |
520 } | |
521 | |
522 void RenderLayerScrollableArea::deregisterForAnimation() | |
523 { | |
524 if (LocalFrame* frame = box().frame()) { | |
525 if (FrameView* frameView = frame->view()) | |
526 frameView->removeAnimatingScrollableArea(this); | |
527 } | |
528 } | |
529 | |
530 bool RenderLayerScrollableArea::userInputScrollable(ScrollbarOrientation orienta
tion) const | |
531 { | |
532 if (box().isIntristicallyScrollable(orientation)) | |
533 return true; | |
534 | |
535 EOverflow overflowStyle = (orientation == HorizontalScrollbar) ? | |
536 box().style()->overflowX() : box().style()->overflowY(); | |
537 return (overflowStyle == OSCROLL || overflowStyle == OAUTO || overflowStyle
== OOVERLAY); | |
538 } | |
539 | |
540 bool RenderLayerScrollableArea::shouldPlaceVerticalScrollbarOnLeft() const | |
541 { | |
542 return box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft(); | |
543 } | |
544 | |
545 int RenderLayerScrollableArea::pageStep(ScrollbarOrientation orientation) const | |
546 { | |
547 int length = (orientation == HorizontalScrollbar) ? | |
548 box().pixelSnappedClientWidth() : box().pixelSnappedClientHeight(); | |
549 int minPageStep = static_cast<float>(length) * ScrollableArea::minFractionTo
StepWhenPaging(); | |
550 int pageStep = max(minPageStep, length - ScrollableArea::maxOverlapBetweenPa
ges()); | |
551 | |
552 return max(pageStep, 1); | |
553 } | |
554 | |
555 RenderBox& RenderLayerScrollableArea::box() const | |
556 { | |
557 return *m_layer.renderBox(); | |
558 } | |
559 | |
560 RenderLayer* RenderLayerScrollableArea::layer() const | |
561 { | |
562 return &m_layer; | |
563 } | |
564 | |
565 LayoutUnit RenderLayerScrollableArea::scrollWidth() const | |
566 { | |
567 if (m_scrollDimensionsDirty) | |
568 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions(); | |
569 return m_overflowRect.width(); | |
570 } | |
571 | |
572 LayoutUnit RenderLayerScrollableArea::scrollHeight() const | |
573 { | |
574 if (m_scrollDimensionsDirty) | |
575 const_cast<RenderLayerScrollableArea*>(this)->computeScrollDimensions(); | |
576 return m_overflowRect.height(); | |
577 } | |
578 | |
579 int RenderLayerScrollableArea::pixelSnappedScrollWidth() const | |
580 { | |
581 return snapSizeToPixel(scrollWidth(), box().clientLeft() + box().location().
x()); | |
582 } | |
583 | |
584 int RenderLayerScrollableArea::pixelSnappedScrollHeight() const | |
585 { | |
586 return snapSizeToPixel(scrollHeight(), box().clientTop() + box().location().
y()); | |
587 } | |
588 | |
589 void RenderLayerScrollableArea::computeScrollDimensions() | |
590 { | |
591 m_scrollDimensionsDirty = false; | |
592 | |
593 m_overflowRect = box().layoutOverflowRect(); | |
594 box().flipForWritingMode(m_overflowRect); | |
595 | |
596 int scrollableLeftOverflow = m_overflowRect.x() - box().borderLeft() - (box(
).style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? box().verticalScr
ollbarWidth() : 0); | |
597 int scrollableTopOverflow = m_overflowRect.y() - box().borderTop(); | |
598 setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow)); | |
599 } | |
600 | |
601 void RenderLayerScrollableArea::scrollToOffset(const DoubleSize& scrollOffset, S
crollOffsetClamping clamp, ScrollBehavior scrollBehavior) | |
602 { | |
603 cancelProgrammaticScrollAnimation(); | |
604 DoubleSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffse
t(scrollOffset) : scrollOffset; | |
605 if (newScrollOffset != adjustedScrollOffset()) { | |
606 if (scrollBehavior == ScrollBehaviorAuto) | |
607 scrollBehavior = box().style()->scrollBehavior(); | |
608 DoublePoint origin(scrollOrigin()); | |
609 if (scrollBehavior == ScrollBehaviorSmooth) { | |
610 // FIXME: Make programmaticallyScrollSmoothlyToOffset take DoublePoi
nt. crbug.com/243871. | |
611 programmaticallyScrollSmoothlyToOffset(toFloatPoint(-origin + newScr
ollOffset)); | |
612 } else { | |
613 // FIXME: Make scrollToOffsetWithoutAnimation take DoublePoint. crbu
g.com/414283. | |
614 scrollToOffsetWithoutAnimation(toFloatPoint(-origin + newScrollOffse
t)); | |
615 } | |
616 } | |
617 } | |
618 | |
619 void RenderLayerScrollableArea::updateAfterLayout() | |
620 { | |
621 m_scrollDimensionsDirty = true; | |
622 DoubleSize originalScrollOffset = adjustedScrollOffset(); | |
623 | |
624 computeScrollDimensions(); | |
625 | |
626 // Layout may cause us to be at an invalid scroll position. In this case we
need | |
627 // to pull our scroll offsets back to the max (or push them up to the min). | |
628 DoubleSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset()); | |
629 if (clampedScrollOffset != adjustedScrollOffset()) | |
630 scrollToOffset(clampedScrollOffset); | |
631 | |
632 if (originalScrollOffset != adjustedScrollOffset()) { | |
633 DoublePoint origin(scrollOrigin()); | |
634 scrollToOffsetWithoutAnimation(toFloatPoint(-origin + adjustedScrollOffs
et())); | |
635 } | |
636 | |
637 bool hasHorizontalOverflow = this->hasHorizontalOverflow(); | |
638 bool hasVerticalOverflow = this->hasVerticalOverflow(); | |
639 | |
640 { | |
641 // Hits in compositing/overflow/automatically-opt-into-composited-scroll
ing-after-style-change.html. | |
642 DisableCompositingQueryAsserts disabler; | |
643 | |
644 // overflow:scroll should just enable/disable. | |
645 if (box().style()->overflowX() == OSCROLL && horizontalScrollbar()) | |
646 horizontalScrollbar()->setEnabled(hasHorizontalOverflow); | |
647 if (box().style()->overflowY() == OSCROLL && verticalScrollbar()) | |
648 verticalScrollbar()->setEnabled(hasVerticalOverflow); | |
649 } | |
650 if (hasOverlayScrollbars()) { | |
651 if (!scrollSize(HorizontalScrollbar)) | |
652 setHasHorizontalScrollbar(false); | |
653 if (!scrollSize(VerticalScrollbar)) | |
654 setHasVerticalScrollbar(false); | |
655 } | |
656 // overflow:auto may need to lay out again if scrollbars got added/removed. | |
657 bool autoHorizontalScrollBarChanged = box().hasAutoHorizontalScrollbar() &&
(hasHorizontalScrollbar() != hasHorizontalOverflow); | |
658 bool autoVerticalScrollBarChanged = box().hasAutoVerticalScrollbar() && (has
VerticalScrollbar() != hasVerticalOverflow); | |
659 | |
660 if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) { | |
661 if (box().hasAutoHorizontalScrollbar()) | |
662 setHasHorizontalScrollbar(hasHorizontalOverflow); | |
663 if (box().hasAutoVerticalScrollbar()) | |
664 setHasVerticalScrollbar(hasVerticalOverflow); | |
665 | |
666 if (hasVerticalOverflow || hasHorizontalOverflow) | |
667 updateScrollCornerStyle(); | |
668 | |
669 layer()->updateSelfPaintingLayer(); | |
670 | |
671 // Force an update since we know the scrollbars have changed things. | |
672 if (box().document().hasAnnotatedRegions()) | |
673 box().document().setAnnotatedRegionsDirty(true); | |
674 | |
675 if (box().style()->overflowX() == OAUTO || box().style()->overflowY() ==
OAUTO) { | |
676 if (!m_inOverflowRelayout) { | |
677 // Our proprietary overflow: overlay value doesn't trigger a lay
out. | |
678 m_inOverflowRelayout = true; | |
679 SubtreeLayoutScope layoutScope(box()); | |
680 layoutScope.setNeedsLayout(&box()); | |
681 if (box().isRenderBlock()) { | |
682 RenderBlock& block = toRenderBlock(box()); | |
683 block.scrollbarsChanged(autoHorizontalScrollBarChanged, auto
VerticalScrollBarChanged); | |
684 block.layoutBlock(true); | |
685 } else { | |
686 box().layout(); | |
687 } | |
688 m_inOverflowRelayout = false; | |
689 } | |
690 } | |
691 } | |
692 | |
693 { | |
694 // Hits in compositing/overflow/automatically-opt-into-composited-scroll
ing-after-style-change.html. | |
695 DisableCompositingQueryAsserts disabler; | |
696 | |
697 // Set up the range (and page step/line step). | |
698 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
699 int clientWidth = box().pixelSnappedClientWidth(); | |
700 horizontalScrollbar->setProportion(clientWidth, overflowRect().width
()); | |
701 } | |
702 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
703 int clientHeight = box().pixelSnappedClientHeight(); | |
704 verticalScrollbar->setProportion(clientHeight, overflowRect().height
()); | |
705 } | |
706 } | |
707 | |
708 bool hasOverflow = hasScrollableHorizontalOverflow() || hasScrollableVertica
lOverflow(); | |
709 updateScrollableAreaSet(hasOverflow); | |
710 | |
711 if (hasOverflow) { | |
712 DisableCompositingQueryAsserts disabler; | |
713 positionOverflowControls(IntSize()); | |
714 } | |
715 } | |
716 | |
717 bool RenderLayerScrollableArea::hasHorizontalOverflow() const | |
718 { | |
719 ASSERT(!m_scrollDimensionsDirty); | |
720 | |
721 return pixelSnappedScrollWidth() > box().pixelSnappedClientWidth(); | |
722 } | |
723 | |
724 bool RenderLayerScrollableArea::hasVerticalOverflow() const | |
725 { | |
726 ASSERT(!m_scrollDimensionsDirty); | |
727 | |
728 return pixelSnappedScrollHeight() > box().pixelSnappedClientHeight(); | |
729 } | |
730 | |
731 bool RenderLayerScrollableArea::hasScrollableHorizontalOverflow() const | |
732 { | |
733 return hasHorizontalOverflow() && box().scrollsOverflowX(); | |
734 } | |
735 | |
736 bool RenderLayerScrollableArea::hasScrollableVerticalOverflow() const | |
737 { | |
738 return hasVerticalOverflow() && box().scrollsOverflowY(); | |
739 } | |
740 | |
741 static bool overflowRequiresScrollbar(EOverflow overflow) | |
742 { | |
743 return overflow == OSCROLL; | |
744 } | |
745 | |
746 static bool overflowDefinesAutomaticScrollbar(EOverflow overflow) | |
747 { | |
748 return overflow == OAUTO || overflow == OOVERLAY; | |
749 } | |
750 | |
751 // This function returns true if the given box requires overflow scrollbars (as | |
752 // opposed to the 'viewport' scrollbars managed by the RenderLayerCompositor). | |
753 // FIXME: we should use the same scrolling machinery for both the viewport and | |
754 // overflow. Currently, we need to avoid producing scrollbars here if they'll be | |
755 // handled externally in the RLC. | |
756 static bool canHaveOverflowScrollbars(const RenderBox& box) | |
757 { | |
758 bool rootLayerScrolls = box.document().settings() && box.document().settings
()->rootLayerScrolls(); | |
759 return (rootLayerScrolls || !box.isRenderView()) && box.document().viewportD
efiningElement() != box.node(); | |
760 } | |
761 | |
762 void RenderLayerScrollableArea::updateAfterStyleChange(const RenderStyle* oldSty
le) | |
763 { | |
764 if (!m_scrollDimensionsDirty) | |
765 updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollab
leVerticalOverflow()); | |
766 | |
767 if (!canHaveOverflowScrollbars(box())) | |
768 return; | |
769 | |
770 EOverflow overflowX = box().style()->overflowX(); | |
771 EOverflow overflowY = box().style()->overflowY(); | |
772 | |
773 // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep
any automatic scrollbar that was already present. | |
774 bool needsHorizontalScrollbar = (hasHorizontalScrollbar() && overflowDefines
AutomaticScrollbar(overflowX)) || overflowRequiresScrollbar(overflowX); | |
775 bool needsVerticalScrollbar = (hasVerticalScrollbar() && overflowDefinesAuto
maticScrollbar(overflowY)) || overflowRequiresScrollbar(overflowY); | |
776 setHasHorizontalScrollbar(needsHorizontalScrollbar); | |
777 setHasVerticalScrollbar(needsVerticalScrollbar); | |
778 | |
779 // With overflow: scroll, scrollbars are always visible but may be disabled. | |
780 // When switching to another value, we need to re-enable them (see bug 11985
). | |
781 if (needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL
&& overflowX != OSCROLL) { | |
782 ASSERT(hasHorizontalScrollbar()); | |
783 m_hBar->setEnabled(true); | |
784 } | |
785 | |
786 if (needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL &
& overflowY != OSCROLL) { | |
787 ASSERT(hasVerticalScrollbar()); | |
788 m_vBar->setEnabled(true); | |
789 } | |
790 | |
791 // FIXME: Need to detect a swap from custom to native scrollbars (and vice v
ersa). | |
792 if (m_hBar) | |
793 m_hBar->styleChanged(); | |
794 if (m_vBar) | |
795 m_vBar->styleChanged(); | |
796 | |
797 updateScrollCornerStyle(); | |
798 updateResizerAreaSet(); | |
799 updateResizerStyle(); | |
800 } | |
801 | |
802 bool RenderLayerScrollableArea::updateAfterCompositingChange() | |
803 { | |
804 layer()->updateScrollingStateAfterCompositingChange(); | |
805 const bool layersChanged = m_topmostScrollChild != m_nextTopmostScrollChild; | |
806 m_topmostScrollChild = m_nextTopmostScrollChild; | |
807 m_nextTopmostScrollChild = nullptr; | |
808 return layersChanged; | |
809 } | |
810 | |
811 void RenderLayerScrollableArea::updateAfterOverflowRecalc() | |
812 { | |
813 computeScrollDimensions(); | |
814 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
815 int clientWidth = box().pixelSnappedClientWidth(); | |
816 horizontalScrollbar->setProportion(clientWidth, overflowRect().width()); | |
817 } | |
818 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
819 int clientHeight = box().pixelSnappedClientHeight(); | |
820 verticalScrollbar->setProportion(clientHeight, overflowRect().height()); | |
821 } | |
822 | |
823 bool hasHorizontalOverflow = this->hasHorizontalOverflow(); | |
824 bool hasVerticalOverflow = this->hasVerticalOverflow(); | |
825 bool autoHorizontalScrollBarChanged = box().hasAutoHorizontalScrollbar() &&
(hasHorizontalScrollbar() != hasHorizontalOverflow); | |
826 bool autoVerticalScrollBarChanged = box().hasAutoVerticalScrollbar() && (has
VerticalScrollbar() != hasVerticalOverflow); | |
827 if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) | |
828 box().setNeedsLayoutAndFullPaintInvalidation(); | |
829 } | |
830 | |
831 DoubleSize RenderLayerScrollableArea::clampScrollOffset(const DoubleSize& scroll
Offset) const | |
832 { | |
833 int maxX = scrollWidth() - box().pixelSnappedClientWidth(); | |
834 int maxY = scrollHeight() - box().pixelSnappedClientHeight(); | |
835 | |
836 double x = std::max(std::min(scrollOffset.width(), static_cast<double>(maxX)
), 0.0); | |
837 double y = std::max(std::min(scrollOffset.height(), static_cast<double>(maxY
)), 0.0); | |
838 return DoubleSize(x, y); | |
839 } | |
840 | |
841 IntRect RenderLayerScrollableArea::rectForHorizontalScrollbar(const IntRect& bor
derBoxRect) const | |
842 { | |
843 if (!m_hBar) | |
844 return IntRect(); | |
845 | |
846 const IntRect& scrollCorner = scrollCornerRect(); | |
847 | |
848 return IntRect(horizontalScrollbarStart(borderBoxRect.x()), | |
849 borderBoxRect.maxY() - box().borderBottom() - m_hBar->height(), | |
850 borderBoxRect.width() - (box().borderLeft() + box().borderRight()) - scr
ollCorner.width(), | |
851 m_hBar->height()); | |
852 } | |
853 | |
854 IntRect RenderLayerScrollableArea::rectForVerticalScrollbar(const IntRect& borde
rBoxRect) const | |
855 { | |
856 if (!m_vBar) | |
857 return IntRect(); | |
858 | |
859 const IntRect& scrollCorner = scrollCornerRect(); | |
860 | |
861 return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX(
)), | |
862 borderBoxRect.y() + box().borderTop(), | |
863 m_vBar->width(), | |
864 borderBoxRect.height() - (box().borderTop() + box().borderBottom()) - sc
rollCorner.height()); | |
865 } | |
866 | |
867 LayoutUnit RenderLayerScrollableArea::verticalScrollbarStart(int minX, int maxX)
const | |
868 { | |
869 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
870 return minX + box().borderLeft(); | |
871 return maxX - box().borderRight() - m_vBar->width(); | |
872 } | |
873 | |
874 LayoutUnit RenderLayerScrollableArea::horizontalScrollbarStart(int minX) const | |
875 { | |
876 int x = minX + box().borderLeft(); | |
877 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
878 x += m_vBar ? m_vBar->width() : resizerCornerRect(box().pixelSnappedBord
erBoxRect(), ResizerForPointer).width(); | |
879 return x; | |
880 } | |
881 | |
882 IntSize RenderLayerScrollableArea::scrollbarOffset(const Scrollbar* scrollbar) c
onst | |
883 { | |
884 if (scrollbar == m_vBar.get()) | |
885 return IntSize(verticalScrollbarStart(0, box().size().width()), box().bo
rderTop()); | |
886 | |
887 if (scrollbar == m_hBar.get()) | |
888 return IntSize(horizontalScrollbarStart(0), box().size().height() - box(
).borderBottom() - scrollbar->height()); | |
889 | |
890 ASSERT_NOT_REACHED(); | |
891 return IntSize(); | |
892 } | |
893 | |
894 static inline RenderObject* rendererForScrollbar(RenderObject& renderer) | |
895 { | |
896 if (Node* node = renderer.node()) { | |
897 if (ShadowRoot* shadowRoot = node->containingShadowRoot()) { | |
898 if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot) | |
899 return shadowRoot->host()->renderer(); | |
900 } | |
901 } | |
902 | |
903 return &renderer; | |
904 } | |
905 | |
906 PassRefPtrWillBeRawPtr<Scrollbar> RenderLayerScrollableArea::createScrollbar(Scr
ollbarOrientation orientation) | |
907 { | |
908 RefPtrWillBeRawPtr<Scrollbar> widget = nullptr; | |
909 RenderObject* actualRenderer = rendererForScrollbar(box()); | |
910 bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->st
yle()->hasPseudoStyle(SCROLLBAR); | |
911 if (hasCustomScrollbarStyle) { | |
912 widget = RenderScrollbar::createCustomScrollbar(this, orientation, actua
lRenderer->node()); | |
913 } else { | |
914 ScrollbarControlSize scrollbarSize = RegularScrollbar; | |
915 if (actualRenderer->style()->hasAppearance()) | |
916 scrollbarSize = LayoutTheme::theme().scrollbarControlSizeForPart(act
ualRenderer->style()->appearance()); | |
917 widget = Scrollbar::create(this, orientation, scrollbarSize); | |
918 if (orientation == HorizontalScrollbar) | |
919 didAddScrollbar(widget.get(), HorizontalScrollbar); | |
920 else | |
921 didAddScrollbar(widget.get(), VerticalScrollbar); | |
922 } | |
923 box().document().view()->addChild(widget.get()); | |
924 return widget.release(); | |
925 } | |
926 | |
927 void RenderLayerScrollableArea::destroyScrollbar(ScrollbarOrientation orientatio
n) | |
928 { | |
929 RefPtrWillBePersistent<Scrollbar>& scrollbar = orientation == HorizontalScro
llbar ? m_hBar : m_vBar; | |
930 if (!scrollbar) | |
931 return; | |
932 | |
933 if (!scrollbar->isCustomScrollbar()) | |
934 willRemoveScrollbar(scrollbar.get(), orientation); | |
935 | |
936 toFrameView(scrollbar->parent())->removeChild(scrollbar.get()); | |
937 scrollbar->disconnectFromScrollableArea(); | |
938 scrollbar = nullptr; | |
939 } | |
940 | |
941 void RenderLayerScrollableArea::setHasHorizontalScrollbar(bool hasScrollbar) | |
942 { | |
943 if (hasScrollbar == hasHorizontalScrollbar()) | |
944 return; | |
945 | |
946 if (hasScrollbar) { | |
947 // This doesn't hit in any tests, but since the equivalent code in setHa
sVerticalScrollbar | |
948 // does, presumably this code does as well. | |
949 DisableCompositingQueryAsserts disabler; | |
950 m_hBar = createScrollbar(HorizontalScrollbar); | |
951 } else { | |
952 if (!layerForHorizontalScrollbar()) | |
953 m_hBar->invalidate(); | |
954 // Otherwise we will remove the layer and just need recompositing. | |
955 | |
956 destroyScrollbar(HorizontalScrollbar); | |
957 } | |
958 | |
959 // Destroying or creating one bar can cause our scrollbar corner to come and
go. We need to update the opposite scrollbar's style. | |
960 if (m_hBar) | |
961 m_hBar->styleChanged(); | |
962 if (m_vBar) | |
963 m_vBar->styleChanged(); | |
964 | |
965 // Force an update since we know the scrollbars have changed things. | |
966 if (box().document().hasAnnotatedRegions()) | |
967 box().document().setAnnotatedRegionsDirty(true); | |
968 } | |
969 | |
970 void RenderLayerScrollableArea::setHasVerticalScrollbar(bool hasScrollbar) | |
971 { | |
972 if (hasScrollbar == hasVerticalScrollbar()) | |
973 return; | |
974 | |
975 if (hasScrollbar) { | |
976 // Hits in compositing/overflow/automatically-opt-into-composited-scroll
ing-after-style-change.html | |
977 DisableCompositingQueryAsserts disabler; | |
978 m_vBar = createScrollbar(VerticalScrollbar); | |
979 } else { | |
980 if (!layerForVerticalScrollbar()) | |
981 m_vBar->invalidate(); | |
982 // Otherwise we will remove the layer and just need recompositing. | |
983 | |
984 destroyScrollbar(VerticalScrollbar); | |
985 } | |
986 | |
987 // Destroying or creating one bar can cause our scrollbar corner to come and
go. We need to update the opposite scrollbar's style. | |
988 if (m_hBar) | |
989 m_hBar->styleChanged(); | |
990 if (m_vBar) | |
991 m_vBar->styleChanged(); | |
992 | |
993 // Force an update since we know the scrollbars have changed things. | |
994 if (box().document().hasAnnotatedRegions()) | |
995 box().document().setAnnotatedRegionsDirty(true); | |
996 } | |
997 | |
998 int RenderLayerScrollableArea::verticalScrollbarWidth(OverlayScrollbarSizeReleva
ncy relevancy) const | |
999 { | |
1000 if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayS
crollbarSize || !m_vBar->shouldParticipateInHitTesting()))) | |
1001 return 0; | |
1002 return m_vBar->width(); | |
1003 } | |
1004 | |
1005 int RenderLayerScrollableArea::horizontalScrollbarHeight(OverlayScrollbarSizeRel
evancy relevancy) const | |
1006 { | |
1007 if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayS
crollbarSize || !m_hBar->shouldParticipateInHitTesting()))) | |
1008 return 0; | |
1009 return m_hBar->height(); | |
1010 } | |
1011 | |
1012 void RenderLayerScrollableArea::positionOverflowControls(const IntSize& offsetFr
omRoot) | |
1013 { | |
1014 if (!hasScrollbar() && !box().canResize()) | |
1015 return; | |
1016 | |
1017 const IntRect borderBox = box().pixelSnappedBorderBoxRect(); | |
1018 if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { | |
1019 IntRect vBarRect = rectForVerticalScrollbar(borderBox); | |
1020 vBarRect.move(offsetFromRoot); | |
1021 verticalScrollbar->setFrameRect(vBarRect); | |
1022 } | |
1023 | |
1024 if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { | |
1025 IntRect hBarRect = rectForHorizontalScrollbar(borderBox); | |
1026 hBarRect.move(offsetFromRoot); | |
1027 horizontalScrollbar->setFrameRect(hBarRect); | |
1028 } | |
1029 | |
1030 const IntRect& scrollCorner = scrollCornerRect(); | |
1031 if (m_scrollCorner) | |
1032 m_scrollCorner->setFrameRect(scrollCorner); | |
1033 | |
1034 if (m_resizer) | |
1035 m_resizer->setFrameRect(resizerCornerRect(borderBox, ResizerForPointer))
; | |
1036 | |
1037 // FIXME, this should eventually be removed, once we are certain that compos
ited | |
1038 // controls get correctly positioned on a compositor update. For now, conser
vatively | |
1039 // leaving this unchanged. | |
1040 if (layer()->hasCompositedLayerMapping()) | |
1041 layer()->compositedLayerMapping()->positionOverflowControlsLayers(offset
FromRoot); | |
1042 } | |
1043 | |
1044 void RenderLayerScrollableArea::updateScrollCornerStyle() | |
1045 { | |
1046 if (!m_scrollCorner && !hasScrollbar()) | |
1047 return; | |
1048 if (!m_scrollCorner && hasOverlayScrollbars()) | |
1049 return; | |
1050 | |
1051 RenderObject* actualRenderer = rendererForScrollbar(box()); | |
1052 RefPtr<RenderStyle> corner = box().hasOverflowClip() ? actualRenderer->getUn
cachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), actualRenderer->style())
: PassRefPtr<RenderStyle>(nullptr); | |
1053 if (corner) { | |
1054 if (!m_scrollCorner) { | |
1055 m_scrollCorner = RenderScrollbarPart::createAnonymous(&box().documen
t()); | |
1056 m_scrollCorner->setParent(&box()); | |
1057 } | |
1058 m_scrollCorner->setStyle(corner.release()); | |
1059 } else if (m_scrollCorner) { | |
1060 m_scrollCorner->destroy(); | |
1061 m_scrollCorner = nullptr; | |
1062 } | |
1063 } | |
1064 | |
1065 bool RenderLayerScrollableArea::hitTestOverflowControls(HitTestResult& result, c
onst IntPoint& localPoint) | |
1066 { | |
1067 if (!hasScrollbar() && !box().canResize()) | |
1068 return false; | |
1069 | |
1070 IntRect resizeControlRect; | |
1071 if (box().style()->resize() != RESIZE_NONE) { | |
1072 resizeControlRect = resizerCornerRect(box().pixelSnappedBorderBoxRect(),
ResizerForPointer); | |
1073 if (resizeControlRect.contains(localPoint)) | |
1074 return true; | |
1075 } | |
1076 | |
1077 int resizeControlSize = max(resizeControlRect.height(), 0); | |
1078 if (m_vBar && m_vBar->shouldParticipateInHitTesting()) { | |
1079 LayoutRect vBarRect(verticalScrollbarStart(0, box().size().width()), | |
1080 box().borderTop(), | |
1081 m_vBar->width(), | |
1082 box().size().height() - (box().borderTop() + box().borderBottom()) -
(m_hBar ? m_hBar->height() : resizeControlSize)); | |
1083 if (vBarRect.contains(localPoint)) { | |
1084 result.setScrollbar(m_vBar.get()); | |
1085 return true; | |
1086 } | |
1087 } | |
1088 | |
1089 resizeControlSize = max(resizeControlRect.width(), 0); | |
1090 if (m_hBar && m_hBar->shouldParticipateInHitTesting()) { | |
1091 LayoutRect hBarRect(horizontalScrollbarStart(0), | |
1092 box().size().height() - box().borderBottom() - m_hBar->height(), | |
1093 box().size().width() - (box().borderLeft() + box().borderRight()) -
(m_vBar ? m_vBar->width() : resizeControlSize), | |
1094 m_hBar->height()); | |
1095 if (hBarRect.contains(localPoint)) { | |
1096 result.setScrollbar(m_hBar.get()); | |
1097 return true; | |
1098 } | |
1099 } | |
1100 | |
1101 // FIXME: We should hit test the m_scrollCorner and pass it back through the
result. | |
1102 | |
1103 return false; | |
1104 } | |
1105 | |
1106 IntRect RenderLayerScrollableArea::resizerCornerRect(const IntRect& bounds, Resi
zerHitTestType resizerHitTestType) const | |
1107 { | |
1108 if (box().style()->resize() == RESIZE_NONE) | |
1109 return IntRect(); | |
1110 IntRect corner = cornerRect(box().style(), horizontalScrollbar(), verticalSc
rollbar(), bounds); | |
1111 | |
1112 if (resizerHitTestType == ResizerForTouch) { | |
1113 // We make the resizer virtually larger for touch hit testing. With the | |
1114 // expanding ratio k = ResizerControlExpandRatioForTouch, we first move | |
1115 // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1))
, | |
1116 // then expand the rect by new_w/h = w/h * k. | |
1117 int expandRatio = ResizerControlExpandRatioForTouch - 1; | |
1118 corner.move(-corner.width() * expandRatio, -corner.height() * expandRati
o); | |
1119 corner.expand(corner.width() * expandRatio, corner.height() * expandRati
o); | |
1120 } | |
1121 | |
1122 return corner; | |
1123 } | |
1124 | |
1125 IntRect RenderLayerScrollableArea::scrollCornerAndResizerRect() const | |
1126 { | |
1127 IntRect scrollCornerAndResizer = scrollCornerRect(); | |
1128 if (scrollCornerAndResizer.isEmpty()) | |
1129 scrollCornerAndResizer = resizerCornerRect(box().pixelSnappedBorderBoxRe
ct(), ResizerForPointer); | |
1130 return scrollCornerAndResizer; | |
1131 } | |
1132 | |
1133 bool RenderLayerScrollableArea::isPointInResizeControl(const IntPoint& absoluteP
oint, ResizerHitTestType resizerHitTestType) const | |
1134 { | |
1135 if (!box().canResize()) | |
1136 return false; | |
1137 | |
1138 IntPoint localPoint = roundedIntPoint(box().absoluteToLocal(absolutePoint, U
seTransforms)); | |
1139 IntRect localBounds(0, 0, box().pixelSnappedWidth(), box().pixelSnappedHeigh
t()); | |
1140 return resizerCornerRect(localBounds, resizerHitTestType).contains(localPoin
t); | |
1141 } | |
1142 | |
1143 bool RenderLayerScrollableArea::hitTestResizerInFragments(const LayerFragments&
layerFragments, const HitTestLocation& hitTestLocation) const | |
1144 { | |
1145 if (!box().canResize()) | |
1146 return false; | |
1147 | |
1148 if (layerFragments.isEmpty()) | |
1149 return false; | |
1150 | |
1151 for (int i = layerFragments.size() - 1; i >= 0; --i) { | |
1152 const LayerFragment& fragment = layerFragments.at(i); | |
1153 if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCorner
Rect(pixelSnappedIntRect(fragment.layerBounds), ResizerForPointer).contains(hitT
estLocation.roundedPoint())) | |
1154 return true; | |
1155 } | |
1156 | |
1157 return false; | |
1158 } | |
1159 | |
1160 void RenderLayerScrollableArea::updateResizerAreaSet() | |
1161 { | |
1162 LocalFrame* frame = box().frame(); | |
1163 if (!frame) | |
1164 return; | |
1165 FrameView* frameView = frame->view(); | |
1166 if (!frameView) | |
1167 return; | |
1168 if (box().canResize()) | |
1169 frameView->addResizerArea(box()); | |
1170 else | |
1171 frameView->removeResizerArea(box()); | |
1172 } | |
1173 | |
1174 void RenderLayerScrollableArea::updateResizerStyle() | |
1175 { | |
1176 if (!m_resizer && !box().canResize()) | |
1177 return; | |
1178 | |
1179 RenderObject* actualRenderer = rendererForScrollbar(box()); | |
1180 RefPtr<RenderStyle> resizer = box().hasOverflowClip() ? actualRenderer->getU
ncachedPseudoStyle(PseudoStyleRequest(RESIZER), actualRenderer->style()) : PassR
efPtr<RenderStyle>(nullptr); | |
1181 if (resizer) { | |
1182 if (!m_resizer) { | |
1183 m_resizer = RenderScrollbarPart::createAnonymous(&box().document()); | |
1184 m_resizer->setParent(&box()); | |
1185 } | |
1186 m_resizer->setStyle(resizer.release()); | |
1187 } else if (m_resizer) { | |
1188 m_resizer->destroy(); | |
1189 m_resizer = nullptr; | |
1190 } | |
1191 } | |
1192 | |
1193 IntSize RenderLayerScrollableArea::offsetFromResizeCorner(const IntPoint& absolu
tePoint) const | |
1194 { | |
1195 // Currently the resize corner is either the bottom right corner or the bott
om left corner. | |
1196 // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be
the case? | |
1197 IntSize elementSize = layer()->size(); | |
1198 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
1199 elementSize.setWidth(0); | |
1200 IntPoint resizerPoint = IntPoint(elementSize); | |
1201 IntPoint localPoint = roundedIntPoint(box().absoluteToLocal(absolutePoint, U
seTransforms)); | |
1202 return localPoint - resizerPoint; | |
1203 } | |
1204 | |
1205 void RenderLayerScrollableArea::resize(const PlatformEvent& evt, const LayoutSiz
e& oldOffset) | |
1206 { | |
1207 // FIXME: This should be possible on generated content but is not right now. | |
1208 if (!inResizeMode() || !box().canResize() || !box().node()) | |
1209 return; | |
1210 | |
1211 ASSERT(box().node()->isElementNode()); | |
1212 Element* element = toElement(box().node()); | |
1213 | |
1214 Document& document = element->document(); | |
1215 | |
1216 IntPoint pos; | |
1217 const PlatformGestureEvent* gevt = 0; | |
1218 | |
1219 switch (evt.type()) { | |
1220 case PlatformEvent::MouseMoved: | |
1221 if (!document.frame()->eventHandler().mousePressed()) | |
1222 return; | |
1223 pos = static_cast<const PlatformMouseEvent*>(&evt)->position(); | |
1224 break; | |
1225 case PlatformEvent::GestureScrollUpdate: | |
1226 pos = static_cast<const PlatformGestureEvent*>(&evt)->position(); | |
1227 gevt = static_cast<const PlatformGestureEvent*>(&evt); | |
1228 pos = gevt->position(); | |
1229 pos.move(gevt->deltaX(), gevt->deltaY()); | |
1230 break; | |
1231 default: | |
1232 ASSERT_NOT_REACHED(); | |
1233 } | |
1234 | |
1235 float zoomFactor = box().style()->effectiveZoom(); | |
1236 | |
1237 IntSize newOffset = offsetFromResizeCorner(document.view()->windowToContents
(pos)); | |
1238 newOffset.setWidth(newOffset.width() / zoomFactor); | |
1239 newOffset.setHeight(newOffset.height() / zoomFactor); | |
1240 | |
1241 LayoutSize currentSize = box().size(); | |
1242 currentSize.scale(1 / zoomFactor); | |
1243 LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentS
ize); | |
1244 element->setMinimumSizeForResizing(minimumSize); | |
1245 | |
1246 LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, ol
dOffset.height() / zoomFactor); | |
1247 if (box().style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { | |
1248 newOffset.setWidth(-newOffset.width()); | |
1249 adjustedOldOffset.setWidth(-adjustedOldOffset.width()); | |
1250 } | |
1251 | |
1252 LayoutSize difference((currentSize + newOffset - adjustedOldOffset).expanded
To(minimumSize) - currentSize); | |
1253 | |
1254 bool isBoxSizingBorder = box().style()->boxSizing() == BORDER_BOX; | |
1255 | |
1256 EResize resize = box().style()->resize(); | |
1257 if (resize != RESIZE_VERTICAL && difference.width()) { | |
1258 if (element->isFormControlElement()) { | |
1259 // Make implicit margins from the theme explicit (see <http://bugs.w
ebkit.org/show_bug.cgi?id=9547>). | |
1260 element->setInlineStyleProperty(CSSPropertyMarginLeft, box().marginL
eft() / zoomFactor, CSSPrimitiveValue::CSS_PX); | |
1261 element->setInlineStyleProperty(CSSPropertyMarginRight, box().margin
Right() / zoomFactor, CSSPrimitiveValue::CSS_PX); | |
1262 } | |
1263 LayoutUnit baseWidth = box().size().width() - (isBoxSizingBorder ? Layou
tUnit() : box().borderAndPaddingWidth()); | |
1264 baseWidth = baseWidth / zoomFactor; | |
1265 element->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth +
difference.width()), CSSPrimitiveValue::CSS_PX); | |
1266 } | |
1267 | |
1268 if (resize != RESIZE_HORIZONTAL && difference.height()) { | |
1269 if (element->isFormControlElement()) { | |
1270 // Make implicit margins from the theme explicit (see <http://bugs.w
ebkit.org/show_bug.cgi?id=9547>). | |
1271 element->setInlineStyleProperty(CSSPropertyMarginTop, box().marginTo
p() / zoomFactor, CSSPrimitiveValue::CSS_PX); | |
1272 element->setInlineStyleProperty(CSSPropertyMarginBottom, box().margi
nBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX); | |
1273 } | |
1274 LayoutUnit baseHeight = box().size().height() - (isBoxSizingBorder ? Lay
outUnit() : box().borderAndPaddingHeight()); | |
1275 baseHeight = baseHeight / zoomFactor; | |
1276 element->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight
+ difference.height()), CSSPrimitiveValue::CSS_PX); | |
1277 } | |
1278 | |
1279 document.updateLayout(); | |
1280 | |
1281 // FIXME (Radar 4118564): We should also autoscroll the window as necessary
to keep the point under the cursor in view. | |
1282 } | |
1283 | |
1284 LayoutRect RenderLayerScrollableArea::exposeRect(const LayoutRect& rect, const S
crollAlignment& alignX, const ScrollAlignment& alignY) | |
1285 { | |
1286 LayoutRect localExposeRect(box().absoluteToLocalQuad(FloatQuad(FloatRect(rec
t)), UseTransforms).boundingBox()); | |
1287 localExposeRect.move(-box().borderLeft(), -box().borderTop()); | |
1288 LayoutRect layerBounds(0, 0, box().clientWidth(), box().clientHeight()); | |
1289 LayoutRect r = ScrollAlignment::getRectToExpose(layerBounds, localExposeRect
, alignX, alignY); | |
1290 | |
1291 DoubleSize clampedScrollOffset = clampScrollOffset(adjustedScrollOffset() +
roundedIntSize(r.location())); | |
1292 if (clampedScrollOffset == adjustedScrollOffset()) | |
1293 return rect; | |
1294 | |
1295 DoubleSize oldScrollOffset = adjustedScrollOffset(); | |
1296 scrollToOffset(clampedScrollOffset); | |
1297 DoubleSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset
; | |
1298 localExposeRect.move(-LayoutSize(scrollOffsetDifference)); | |
1299 return LayoutRect(box().localToAbsoluteQuad(FloatQuad(FloatRect(localExposeR
ect)), UseTransforms).boundingBox()); | |
1300 } | |
1301 | |
1302 void RenderLayerScrollableArea::updateScrollableAreaSet(bool hasOverflow) | |
1303 { | |
1304 LocalFrame* frame = box().frame(); | |
1305 if (!frame) | |
1306 return; | |
1307 | |
1308 FrameView* frameView = frame->view(); | |
1309 if (!frameView) | |
1310 return; | |
1311 | |
1312 // FIXME: Does this need to be fixed later for OOPI? | |
1313 bool isVisibleToHitTest = box().visibleToHitTesting(); | |
1314 if (HTMLFrameOwnerElement* owner = frame->deprecatedLocalOwner()) | |
1315 isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToH
itTesting(); | |
1316 | |
1317 bool didScrollOverflow = m_scrollsOverflow; | |
1318 | |
1319 m_scrollsOverflow = hasOverflow && isVisibleToHitTest; | |
1320 if (didScrollOverflow == scrollsOverflow()) | |
1321 return; | |
1322 | |
1323 if (m_scrollsOverflow) { | |
1324 ASSERT(canHaveOverflowScrollbars(box())); | |
1325 frameView->addScrollableArea(this); | |
1326 } else | |
1327 frameView->removeScrollableArea(this); | |
1328 } | |
1329 | |
1330 void RenderLayerScrollableArea::updateCompositingLayersAfterScroll() | |
1331 { | |
1332 DisableCompositingQueryAsserts disabler; | |
1333 RenderLayerCompositor* compositor = box().view()->compositor(); | |
1334 if (compositor->inCompositingMode()) { | |
1335 if (usesCompositedScrolling()) { | |
1336 ASSERT(layer()->hasCompositedLayerMapping()); | |
1337 layer()->compositedLayerMapping()->setNeedsGraphicsLayerUpdate(Graph
icsLayerUpdateSubtree); | |
1338 compositor->setNeedsCompositingUpdate(CompositingUpdateAfterGeometry
Change); | |
1339 } else { | |
1340 layer()->setNeedsCompositingInputsUpdate(); | |
1341 } | |
1342 } | |
1343 } | |
1344 | |
1345 bool RenderLayerScrollableArea::usesCompositedScrolling() const | |
1346 { | |
1347 // Scroll form controls on the main thread so they exhibit correct touch scr
oll event bubbling | |
1348 if (box().isIntristicallyScrollable(VerticalScrollbar) || box().isIntristica
llyScrollable(HorizontalScrollbar)) | |
1349 return false; | |
1350 | |
1351 // See https://codereview.chromium.org/176633003/ for the tests that fail wi
thout this disabler. | |
1352 DisableCompositingQueryAsserts disabler; | |
1353 return layer()->hasCompositedLayerMapping() && layer()->compositedLayerMappi
ng()->scrollingLayer(); | |
1354 } | |
1355 | |
1356 static bool layerNeedsCompositedScrolling(RenderLayerScrollableArea::LCDTextMode
mode, const RenderLayer* layer) | |
1357 { | |
1358 if (mode == RenderLayerScrollableArea::ConsiderLCDText && !layer->compositor
()->preferCompositingToLCDTextEnabled()) | |
1359 return false; | |
1360 | |
1361 return layer->scrollsOverflow() | |
1362 && !layer->hasDescendantWithClipPath() | |
1363 && !layer->hasAncestorWithClipPath() | |
1364 && !layer->renderer()->style()->hasBorderRadius(); | |
1365 } | |
1366 | |
1367 void RenderLayerScrollableArea::updateNeedsCompositedScrolling(LCDTextMode mode) | |
1368 { | |
1369 const bool needsCompositedScrolling = layerNeedsCompositedScrolling(mode, la
yer()); | |
1370 if (static_cast<bool>(m_needsCompositedScrolling) != needsCompositedScrollin
g) { | |
1371 m_needsCompositedScrolling = needsCompositedScrolling; | |
1372 layer()->didUpdateNeedsCompositedScrolling(); | |
1373 } | |
1374 } | |
1375 | |
1376 void RenderLayerScrollableArea::setTopmostScrollChild(RenderLayer* scrollChild) | |
1377 { | |
1378 // We only want to track the topmost scroll child for scrollable areas with | |
1379 // overlay scrollbars. | |
1380 if (!hasOverlayScrollbars()) | |
1381 return; | |
1382 m_nextTopmostScrollChild = scrollChild; | |
1383 } | |
1384 | |
1385 } // namespace blink | |
OLD | NEW |