• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "ScrollView.h"
28 
29 #include "GraphicsContext.h"
30 #include "HostWindow.h"
31 #include "PlatformMouseEvent.h"
32 #include "PlatformWheelEvent.h"
33 #include "Scrollbar.h"
34 #include "ScrollbarTheme.h"
35 #include <wtf/StdLibExtras.h>
36 
37 using std::max;
38 
39 namespace WebCore {
40 
ScrollView()41 ScrollView::ScrollView()
42     : m_horizontalScrollbarMode(ScrollbarAuto)
43     , m_verticalScrollbarMode(ScrollbarAuto)
44     , m_prohibitsScrolling(false)
45     , m_canBlitOnScroll(true)
46     , m_scrollbarsAvoidingResizer(0)
47     , m_scrollbarsSuppressed(false)
48     , m_inUpdateScrollbars(false)
49     , m_updateScrollbarsPass(0)
50     , m_drawPanScrollIcon(false)
51     , m_useFixedLayout(false)
52     , m_paintsEntireContents(false)
53 {
54     platformInit();
55 }
56 
~ScrollView()57 ScrollView::~ScrollView()
58 {
59     platformDestroy();
60 }
61 
addChild(PassRefPtr<Widget> prpChild)62 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
63 {
64     Widget* child = prpChild.get();
65     ASSERT(child != this && !child->parent());
66     child->setParent(this);
67     m_children.add(prpChild);
68     if (child->platformWidget())
69         platformAddChild(child);
70 }
71 
removeChild(Widget * child)72 void ScrollView::removeChild(Widget* child)
73 {
74     ASSERT(child->parent() == this);
75     child->setParent(0);
76     m_children.remove(child);
77     if (child->platformWidget())
78         platformRemoveChild(child);
79 }
80 
setHasHorizontalScrollbar(bool hasBar)81 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
82 {
83     if (hasBar && !m_horizontalScrollbar) {
84         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
85         addChild(m_horizontalScrollbar.get());
86         m_horizontalScrollbar->styleChanged();
87     } else if (!hasBar && m_horizontalScrollbar) {
88         removeChild(m_horizontalScrollbar.get());
89         m_horizontalScrollbar = 0;
90     }
91 }
92 
setHasVerticalScrollbar(bool hasBar)93 void ScrollView::setHasVerticalScrollbar(bool hasBar)
94 {
95     if (hasBar && !m_verticalScrollbar) {
96         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
97         addChild(m_verticalScrollbar.get());
98         m_verticalScrollbar->styleChanged();
99     } else if (!hasBar && m_verticalScrollbar) {
100         removeChild(m_verticalScrollbar.get());
101         m_verticalScrollbar = 0;
102     }
103 }
104 
105 #if !PLATFORM(GTK)
createScrollbar(ScrollbarOrientation orientation)106 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
107 {
108     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
109 }
110 
setScrollbarModes(ScrollbarMode horizontalMode,ScrollbarMode verticalMode)111 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
112 {
113     if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode())
114         return;
115     m_horizontalScrollbarMode = horizontalMode;
116     m_verticalScrollbarMode = verticalMode;
117     if (platformWidget())
118         platformSetScrollbarModes();
119     else
120         updateScrollbars(scrollOffset());
121 }
122 #endif
123 
scrollbarModes(ScrollbarMode & horizontalMode,ScrollbarMode & verticalMode) const124 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
125 {
126     if (platformWidget()) {
127         platformScrollbarModes(horizontalMode, verticalMode);
128         return;
129     }
130     horizontalMode = m_horizontalScrollbarMode;
131     verticalMode = m_verticalScrollbarMode;
132 }
133 
setCanHaveScrollbars(bool canScroll)134 void ScrollView::setCanHaveScrollbars(bool canScroll)
135 {
136     ScrollbarMode newHorizontalMode;
137     ScrollbarMode newVerticalMode;
138 
139     scrollbarModes(newHorizontalMode, newVerticalMode);
140 
141     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
142         newVerticalMode = ScrollbarAuto;
143     else if (!canScroll)
144         newVerticalMode = ScrollbarAlwaysOff;
145 
146     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
147         newHorizontalMode = ScrollbarAuto;
148     else if (!canScroll)
149         newHorizontalMode = ScrollbarAlwaysOff;
150 
151     setScrollbarModes(newHorizontalMode, newVerticalMode);
152 }
153 
setCanBlitOnScroll(bool b)154 void ScrollView::setCanBlitOnScroll(bool b)
155 {
156     if (platformWidget()) {
157         platformSetCanBlitOnScroll(b);
158         return;
159     }
160 
161     m_canBlitOnScroll = b;
162 }
163 
canBlitOnScroll() const164 bool ScrollView::canBlitOnScroll() const
165 {
166     if (platformWidget())
167         return platformCanBlitOnScroll();
168 
169     return m_canBlitOnScroll;
170 }
171 
setPaintsEntireContents(bool paintsEntireContents)172 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
173 {
174     m_paintsEntireContents = paintsEntireContents;
175 }
176 
177 #if !PLATFORM(GTK)
visibleContentRect(bool includeScrollbars) const178 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
179 {
180     if (platformWidget())
181         return platformVisibleContentRect(includeScrollbars);
182     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
183                    IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)),
184                            max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
185 }
186 #endif
187 
layoutWidth() const188 int ScrollView::layoutWidth() const
189 {
190     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
191 }
192 
layoutHeight() const193 int ScrollView::layoutHeight() const
194 {
195     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
196 }
197 
fixedLayoutSize() const198 IntSize ScrollView::fixedLayoutSize() const
199 {
200     return m_fixedLayoutSize;
201 }
202 
setFixedLayoutSize(const IntSize & newSize)203 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
204 {
205     if (fixedLayoutSize() == newSize)
206         return;
207     m_fixedLayoutSize = newSize;
208     updateScrollbars(scrollOffset());
209 }
210 
useFixedLayout() const211 bool ScrollView::useFixedLayout() const
212 {
213     return m_useFixedLayout;
214 }
215 
setUseFixedLayout(bool enable)216 void ScrollView::setUseFixedLayout(bool enable)
217 {
218     if (useFixedLayout() == enable)
219         return;
220     m_useFixedLayout = enable;
221     updateScrollbars(scrollOffset());
222 }
223 
contentsSize() const224 IntSize ScrollView::contentsSize() const
225 {
226     if (platformWidget())
227         return platformContentsSize();
228     return m_contentsSize;
229 }
230 
setContentsSize(const IntSize & newSize)231 void ScrollView::setContentsSize(const IntSize& newSize)
232 {
233     if (contentsSize() == newSize)
234         return;
235     m_contentsSize = newSize;
236     if (platformWidget())
237         platformSetContentsSize();
238     else
239         updateScrollbars(scrollOffset());
240 }
241 
maximumScrollPosition() const242 IntPoint ScrollView::maximumScrollPosition() const
243 {
244     IntSize maximumOffset = contentsSize() - visibleContentRect().size();
245     maximumOffset.clampNegativeToZero();
246     return IntPoint(maximumOffset.width(), maximumOffset.height());
247 }
248 
valueChanged(Scrollbar * scrollbar)249 void ScrollView::valueChanged(Scrollbar* scrollbar)
250 {
251     // Figure out if we really moved.
252     IntSize newOffset = m_scrollOffset;
253     if (scrollbar) {
254         if (scrollbar->orientation() == HorizontalScrollbar)
255             newOffset.setWidth(scrollbar->value());
256         else if (scrollbar->orientation() == VerticalScrollbar)
257             newOffset.setHeight(scrollbar->value());
258     }
259 
260     IntSize scrollDelta = newOffset - m_scrollOffset;
261     if (scrollDelta == IntSize())
262         return;
263     m_scrollOffset = newOffset;
264 
265     if (scrollbarsSuppressed())
266         return;
267 
268     scrollContents(scrollDelta);
269 }
270 
scrollRectIntoViewRecursively(const IntRect & r)271 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
272 {
273 #if PLATFORM(ANDROID)
274     if (platformProhibitsScrolling())
275         return;
276 #endif
277     // FIXME: This method is not transform-aware.  It should just be moved to FrameView so that an accurate
278     // position for the child view can be determined.
279     IntRect rect = r;
280     ScrollView* view = this;
281     while (view) {
282         if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling.
283             return;
284         view->setScrollPosition(rect.location());
285         rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
286         if (view->parent())
287             rect.intersect(view->frameRect());
288         view = view->parent();
289     }
290 
291     // We may be embedded inside some containing platform scroll view that we don't manage.  This is the case
292     // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView
293     // that contains both it and message headers.  Let the HostWindow know about this scroll so that it can pass the message
294     // on up the view chain.
295     // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to
296     // expose the headers.
297     if (hostWindow())
298         hostWindow()->scrollRectIntoView(rect, this);
299 }
300 
setScrollPosition(const IntPoint & scrollPoint)301 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
302 {
303     if (prohibitsScrolling())
304         return;
305 
306     if (platformWidget()) {
307         platformSetScrollPosition(scrollPoint);
308         return;
309     }
310 
311     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
312     newScrollPosition.clampNegativeToZero();
313 
314     if (newScrollPosition == scrollPosition())
315         return;
316 
317     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
318 }
319 
scroll(ScrollDirection direction,ScrollGranularity granularity)320 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
321 {
322     if (platformWidget())
323         return platformScroll(direction, granularity);
324 
325     if (direction == ScrollUp || direction == ScrollDown) {
326         if (m_verticalScrollbar)
327             return m_verticalScrollbar->scroll(direction, granularity);
328     } else {
329         if (m_horizontalScrollbar)
330             return m_horizontalScrollbar->scroll(direction, granularity);
331     }
332     return false;
333 }
334 
335 static const unsigned cMaxUpdateScrollbarsPass = 2;
336 
updateScrollbars(const IntSize & desiredOffset)337 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
338 {
339     if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
340         return;
341 
342     // If we came in here with the view already needing a layout, then go ahead and do that
343     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
344     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
345     if (!m_scrollbarsSuppressed) {
346         m_inUpdateScrollbars = true;
347         visibleContentsResized();
348         m_inUpdateScrollbars = false;
349     }
350 
351     bool hasHorizontalScrollbar = m_horizontalScrollbar;
352     bool hasVerticalScrollbar = m_verticalScrollbar;
353 
354     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
355     bool newHasVerticalScrollbar = hasVerticalScrollbar;
356 
357     ScrollbarMode hScroll = m_horizontalScrollbarMode;
358     ScrollbarMode vScroll = m_verticalScrollbarMode;
359 
360     if (hScroll != ScrollbarAuto)
361         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
362     if (vScroll != ScrollbarAuto)
363         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
364 
365     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
366         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
367             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
368         if (hasVerticalScrollbar != newHasVerticalScrollbar)
369             setHasVerticalScrollbar(newHasVerticalScrollbar);
370     } else {
371         bool sendContentResizedNotification = false;
372 
373         IntSize docSize = contentsSize();
374         IntSize frameSize = frameRect().size();
375 
376         if (hScroll == ScrollbarAuto) {
377             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
378             if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
379                 newHasHorizontalScrollbar = false;
380         }
381         if (vScroll == ScrollbarAuto) {
382             newHasVerticalScrollbar = docSize.height() > visibleHeight();
383             if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
384                 newHasVerticalScrollbar = false;
385         }
386 
387         // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
388         // try to both gain/lose a scrollbar in the same pass.
389         if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
390             newHasVerticalScrollbar = false;
391         if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
392             newHasHorizontalScrollbar = false;
393 
394         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
395             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
396             sendContentResizedNotification = true;
397         }
398 
399         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
400             setHasVerticalScrollbar(newHasVerticalScrollbar);
401             sendContentResizedNotification = true;
402         }
403 
404         if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
405             m_updateScrollbarsPass++;
406             contentsResized();
407             visibleContentsResized();
408             IntSize newDocSize = contentsSize();
409             if (newDocSize == docSize) {
410                 // The layout with the new scroll state had no impact on
411                 // the document's overall size, so updateScrollbars didn't get called.
412                 // Recur manually.
413                 updateScrollbars(desiredOffset);
414             }
415             m_updateScrollbarsPass--;
416         }
417     }
418 
419     // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
420     // doing it multiple times).
421     if (m_updateScrollbarsPass)
422         return;
423 
424     m_inUpdateScrollbars = true;
425     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
426     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
427     scroll.clampNegativeToZero();
428 
429     if (m_horizontalScrollbar) {
430         int clientWidth = visibleWidth();
431         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
432         int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
433         IntRect oldRect(m_horizontalScrollbar->frameRect());
434         IntRect hBarRect = IntRect(0,
435                                    height() - m_horizontalScrollbar->height(),
436                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
437                                    m_horizontalScrollbar->height());
438         m_horizontalScrollbar->setFrameRect(hBarRect);
439         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
440             m_horizontalScrollbar->invalidate();
441 
442         if (m_scrollbarsSuppressed)
443             m_horizontalScrollbar->setSuppressInvalidation(true);
444         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
445         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
446         m_horizontalScrollbar->setValue(scroll.width());
447         if (m_scrollbarsSuppressed)
448             m_horizontalScrollbar->setSuppressInvalidation(false);
449     }
450 
451     if (m_verticalScrollbar) {
452         int clientHeight = visibleHeight();
453         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
454         int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
455         if (pageStep < 0)
456             pageStep = clientHeight;
457         IntRect oldRect(m_verticalScrollbar->frameRect());
458         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(),
459                                    0,
460                                    m_verticalScrollbar->width(),
461                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
462         m_verticalScrollbar->setFrameRect(vBarRect);
463         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
464             m_verticalScrollbar->invalidate();
465 
466         if (m_scrollbarsSuppressed)
467             m_verticalScrollbar->setSuppressInvalidation(true);
468         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
469         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
470         m_verticalScrollbar->setValue(scroll.height());
471         if (m_scrollbarsSuppressed)
472             m_verticalScrollbar->setSuppressInvalidation(false);
473     }
474 
475     if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
476         frameRectsChanged();
477         updateScrollCorner();
478     }
479 
480     // See if our offset has changed in a situation where we might not have scrollbars.
481     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
482     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
483     // does not).
484     IntSize scrollDelta = scroll - m_scrollOffset;
485     if (scrollDelta != IntSize()) {
486        m_scrollOffset = scroll;
487        scrollContents(scrollDelta);
488     }
489 
490     m_inUpdateScrollbars = false;
491 }
492 
493 const int panIconSizeLength = 16;
494 
scrollContents(const IntSize & scrollDelta)495 void ScrollView::scrollContents(const IntSize& scrollDelta)
496 {
497     if (!hostWindow())
498         return;
499 
500     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
501     // with the clip rect every time to keep it smooth.
502     IntRect clipRect = windowClipRect();
503     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
504     IntRect updateRect = clipRect;
505     updateRect.intersect(scrollViewRect);
506 
507     // Invalidate the window (not the backing store).
508     hostWindow()->repaint(updateRect, false);
509 
510     if (m_drawPanScrollIcon) {
511         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
512         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
513         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
514         panScrollIconDirtyRect.intersect(clipRect);
515         hostWindow()->repaint(panScrollIconDirtyRect, true);
516     }
517 
518     if (canBlitOnScroll()) { // The main frame can just blit the WebView window
519        // FIXME: Find a way to blit subframes without blitting overlapping content
520        hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect);
521     } else {
522        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
523        // windowed plugins.
524        hostWindow()->repaint(updateRect, true, false, true); // Invalidate the backing store and repaint it synchronously
525     }
526 
527     // This call will move children with native widgets (plugins) and invalidate them as well.
528     frameRectsChanged();
529 
530     // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should
531     // be very fast).
532     hostWindow()->paint();
533 }
534 
windowToContents(const IntPoint & windowPoint) const535 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
536 {
537     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
538     return viewPoint + scrollOffset();
539 }
540 
contentsToWindow(const IntPoint & contentsPoint) const541 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
542 {
543     IntPoint viewPoint = contentsPoint - scrollOffset();
544     return convertToContainingWindow(viewPoint);
545 }
546 
windowToContents(const IntRect & windowRect) const547 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
548 {
549     IntRect viewRect = convertFromContainingWindow(windowRect);
550     viewRect.move(scrollOffset());
551     return viewRect;
552 }
553 
contentsToWindow(const IntRect & contentsRect) const554 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
555 {
556     IntRect viewRect = contentsRect;
557     viewRect.move(-scrollOffset());
558     return convertToContainingWindow(viewRect);
559 }
560 
contentsToScreen(const IntRect & rect) const561 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
562 {
563     if (platformWidget())
564         return platformContentsToScreen(rect);
565     if (!hostWindow())
566         return IntRect();
567     return hostWindow()->windowToScreen(contentsToWindow(rect));
568 }
569 
screenToContents(const IntPoint & point) const570 IntPoint ScrollView::screenToContents(const IntPoint& point) const
571 {
572     if (platformWidget())
573         return platformScreenToContents(point);
574     if (!hostWindow())
575         return IntPoint();
576     return windowToContents(hostWindow()->screenToWindow(point));
577 }
578 
containsScrollbarsAvoidingResizer() const579 bool ScrollView::containsScrollbarsAvoidingResizer() const
580 {
581     return !m_scrollbarsAvoidingResizer;
582 }
583 
adjustScrollbarsAvoidingResizerCount(int overlapDelta)584 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
585 {
586     int oldCount = m_scrollbarsAvoidingResizer;
587     m_scrollbarsAvoidingResizer += overlapDelta;
588     if (parent())
589         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
590     else if (!scrollbarsSuppressed()) {
591         // If we went from n to 0 or from 0 to n and we're the outermost view,
592         // we need to invalidate the windowResizerRect(), since it will now need to paint
593         // differently.
594         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
595             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
596             invalidateRect(windowResizerRect());
597     }
598 }
599 
setParent(ScrollView * parentView)600 void ScrollView::setParent(ScrollView* parentView)
601 {
602     if (parentView == parent())
603         return;
604 
605     if (m_scrollbarsAvoidingResizer && parent())
606         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
607 
608     Widget::setParent(parentView);
609 
610     if (m_scrollbarsAvoidingResizer && parent())
611         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
612 }
613 
setScrollbarsSuppressed(bool suppressed,bool repaintOnUnsuppress)614 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
615 {
616     if (suppressed == m_scrollbarsSuppressed)
617         return;
618 
619     m_scrollbarsSuppressed = suppressed;
620 
621     if (platformWidget())
622         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
623     else if (repaintOnUnsuppress && !suppressed) {
624         if (m_horizontalScrollbar)
625             m_horizontalScrollbar->invalidate();
626         if (m_verticalScrollbar)
627             m_verticalScrollbar->invalidate();
628 
629         // Invalidate the scroll corner too on unsuppress.
630         invalidateRect(scrollCornerRect());
631     }
632 }
633 
scrollbarAtPoint(const IntPoint & windowPoint)634 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
635 {
636     if (platformWidget())
637         return 0;
638 
639     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
640     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
641         return m_horizontalScrollbar.get();
642     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
643         return m_verticalScrollbar.get();
644     return 0;
645 }
646 
wheelEvent(PlatformWheelEvent & e)647 void ScrollView::wheelEvent(PlatformWheelEvent& e)
648 {
649     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
650 #if PLATFORM(WX)
651     if (!canHaveScrollbars()) {
652 #else
653     if (!canHaveScrollbars() || platformWidget()) {
654 #endif
655         return;
656     }
657 
658     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
659     IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
660     if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
661         (e.deltaX() > 0 && scrollOffset().width() > 0) ||
662         (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
663         (e.deltaY() > 0 && scrollOffset().height() > 0)) {
664         e.accept();
665         float deltaX = e.deltaX();
666         float deltaY = e.deltaY();
667         if (e.granularity() == ScrollByPageWheelEvent) {
668             ASSERT(deltaX == 0);
669             bool negative = deltaY < 0;
670             deltaY = max(max<int>(visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), visibleHeight() - Scrollbar::maxOverlapBetweenPages()), 1);
671             if (negative)
672                 deltaY = -deltaY;
673         }
674         scrollBy(IntSize(-deltaX, -deltaY));
675     }
676 }
677 
678 void ScrollView::setFrameRect(const IntRect& newRect)
679 {
680     IntRect oldRect = frameRect();
681 
682     if (newRect == oldRect)
683         return;
684 
685     Widget::setFrameRect(newRect);
686 
687     if (platformWidget())
688         return;
689 
690     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
691         updateScrollbars(m_scrollOffset);
692         contentsResized();
693     }
694 
695     frameRectsChanged();
696 }
697 
698 void ScrollView::frameRectsChanged()
699 {
700     if (platformWidget())
701         return;
702 
703     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
704     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
705         (*current)->frameRectsChanged();
706 }
707 
708 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
709 {
710     IntRect paintRect = rect;
711     if (!paintsEntireContents())
712         paintRect.intersect(visibleContentRect());
713 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
714     if (rect != paintRect)
715         platformOffscreenContentRectangle(visibleContentRect(), rect);
716 #endif
717     if (paintRect.isEmpty())
718         return;
719     if (platformWidget()) {
720         platformRepaintContentRectangle(paintRect, now);
721         return;
722     }
723 
724     if (hostWindow())
725         hostWindow()->repaint(contentsToWindow(paintRect), true, now);
726 }
727 
728 IntRect ScrollView::scrollCornerRect() const
729 {
730     IntRect cornerRect;
731 
732     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
733         cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
734                                  height() - m_horizontalScrollbar->height(),
735                                  width() - m_horizontalScrollbar->width(),
736                                  m_horizontalScrollbar->height()));
737     }
738 
739     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
740         cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
741                                  m_verticalScrollbar->height(),
742                                  m_verticalScrollbar->width(),
743                                  height() - m_verticalScrollbar->height()));
744     }
745 
746     return cornerRect;
747 }
748 
749 void ScrollView::updateScrollCorner()
750 {
751 }
752 
753 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
754 {
755     ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
756 }
757 
758 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
759 {
760     if (m_horizontalScrollbar)
761         m_horizontalScrollbar->paint(context, rect);
762     if (m_verticalScrollbar)
763         m_verticalScrollbar->paint(context, rect);
764 
765     paintScrollCorner(context, scrollCornerRect());
766 }
767 
768 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
769 {
770     DEFINE_STATIC_LOCAL(Image*, panScrollIcon, (Image::loadPlatformResource("panIcon").releaseRef()));
771     context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint);
772 }
773 
774 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
775 {
776     if (platformWidget()) {
777         Widget::paint(context, rect);
778         return;
779     }
780 
781     if (context->paintingDisabled() && !context->updatingControlTints())
782         return;
783 
784     IntRect documentDirtyRect = rect;
785     documentDirtyRect.intersect(frameRect());
786 
787     context->save();
788 
789     context->translate(x(), y());
790     documentDirtyRect.move(-x(), -y());
791 
792     context->translate(-scrollX(), -scrollY());
793     documentDirtyRect.move(scrollX(), scrollY());
794 
795     context->clip(visibleContentRect());
796 
797     paintContents(context, documentDirtyRect);
798 
799     context->restore();
800 
801     // Now paint the scrollbars.
802     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
803         context->save();
804         IntRect scrollViewDirtyRect = rect;
805         scrollViewDirtyRect.intersect(frameRect());
806         context->translate(x(), y());
807         scrollViewDirtyRect.move(-x(), -y());
808 
809         paintScrollbars(context, scrollViewDirtyRect);
810 
811         context->restore();
812     }
813 
814     // Paint the panScroll Icon
815     if (m_drawPanScrollIcon)
816         paintPanScrollIcon(context);
817 }
818 
819 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
820 {
821     if (!scrollbarCornerPresent())
822         return false;
823 
824     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
825 
826     if (m_horizontalScrollbar) {
827         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
828         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
829         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
830 
831         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
832     }
833 
834     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
835     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
836     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
837 
838     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
839 }
840 
841 bool ScrollView::scrollbarCornerPresent() const
842 {
843     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
844            (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
845 }
846 
847 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
848 {
849     // Scrollbars won't be transformed within us
850     IntRect newRect = localRect;
851     newRect.move(scrollbar->x(), scrollbar->y());
852     return newRect;
853 }
854 
855 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
856 {
857     IntRect newRect = parentRect;
858     // Scrollbars won't be transformed within us
859     newRect.move(-scrollbar->x(), -scrollbar->y());
860     return newRect;
861 }
862 
863 // FIXME: test these on windows
864 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
865 {
866     // Scrollbars won't be transformed within us
867     IntPoint newPoint = localPoint;
868     newPoint.move(scrollbar->x(), scrollbar->y());
869     return newPoint;
870 }
871 
872 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
873 {
874     IntPoint newPoint = parentPoint;
875     // Scrollbars won't be transformed within us
876     newPoint.move(-scrollbar->x(), -scrollbar->y());
877     return newPoint;
878 }
879 
880 void ScrollView::setParentVisible(bool visible)
881 {
882     if (isParentVisible() == visible)
883         return;
884 
885     Widget::setParentVisible(visible);
886 
887     if (!isSelfVisible())
888         return;
889 
890     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
891     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
892         (*it)->setParentVisible(visible);
893 }
894 
895 void ScrollView::show()
896 {
897     if (!isSelfVisible()) {
898         setSelfVisible(true);
899         if (isParentVisible()) {
900             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
901             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
902                 (*it)->setParentVisible(true);
903         }
904     }
905 
906     Widget::show();
907 }
908 
909 void ScrollView::hide()
910 {
911     if (isSelfVisible()) {
912         if (isParentVisible()) {
913             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
914             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
915                 (*it)->setParentVisible(false);
916         }
917         setSelfVisible(false);
918     }
919 
920     Widget::hide();
921 }
922 
923 bool ScrollView::isOffscreen() const
924 {
925     if (platformWidget())
926         return platformIsOffscreen();
927 
928     if (!isVisible())
929         return true;
930 
931     // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
932     // currently, we can add the method when the other platforms decide to implement this concept.
933     return false;
934 }
935 
936 
937 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
938 {
939     if (!hostWindow())
940         return;
941     m_drawPanScrollIcon = true;
942     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
943     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength,panIconSizeLength)), true, true);
944 }
945 
946 void ScrollView::removePanScrollIcon()
947 {
948     if (!hostWindow())
949         return;
950     m_drawPanScrollIcon = false;
951     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true, true);
952 }
953 
954 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT)
955 
956 void ScrollView::platformInit()
957 {
958 }
959 
960 void ScrollView::platformDestroy()
961 {
962 }
963 
964 #endif
965 
966 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
967 
968 void ScrollView::platformAddChild(Widget*)
969 {
970 }
971 
972 void ScrollView::platformRemoveChild(Widget*)
973 {
974 }
975 
976 #endif
977 
978 #if !PLATFORM(MAC)
979 
980 void ScrollView::platformSetScrollbarsSuppressed(bool)
981 {
982 }
983 
984 #endif
985 
986 #if !PLATFORM(MAC) && !PLATFORM(WX)
987 
988 #if !PLATFORM(ANDROID)
989 void ScrollView::platformSetScrollbarModes()
990 {
991 }
992 
993 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
994 {
995     horizontal = ScrollbarAuto;
996     vertical = ScrollbarAuto;
997 }
998 #endif
999 
1000 void ScrollView::platformSetCanBlitOnScroll(bool)
1001 {
1002 }
1003 
1004 bool ScrollView::platformCanBlitOnScroll() const
1005 {
1006     return false;
1007 }
1008 
1009 #if !PLATFORM(ANDROID)
1010 IntRect ScrollView::platformVisibleContentRect(bool) const
1011 {
1012     return IntRect();
1013 }
1014 #endif
1015 
1016 #if !PLATFORM(ANDROID)
1017 IntSize ScrollView::platformContentsSize() const
1018 {
1019     return IntSize();
1020 }
1021 #endif
1022 
1023 void ScrollView::platformSetContentsSize()
1024 {
1025 }
1026 
1027 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1028 {
1029     return rect;
1030 }
1031 
1032 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1033 {
1034     return point;
1035 }
1036 
1037 #if !PLATFORM(ANDROID)
1038 void ScrollView::platformSetScrollPosition(const IntPoint&)
1039 {
1040 }
1041 #endif
1042 
1043 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1044 {
1045     return true;
1046 }
1047 
1048 #if !PLATFORM(ANDROID)
1049 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1050 {
1051 }
1052 
1053 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
1054 void ScrollView::platformOffscreenContentRectangle(const IntRect& )
1055 {
1056 }
1057 #endif
1058 #endif
1059 
1060 bool ScrollView::platformIsOffscreen() const
1061 {
1062     return false;
1063 }
1064 
1065 #endif
1066 
1067 }
1068 
1069