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