893696dab905525d75c4d9acb4f7578ec63f03f3
[WebKit-https.git] / WebCore / platform / win / ScrollViewWin.cpp
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 "Chrome.h"
30 #include "ChromeClient.h"
31 #include "FloatRect.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "IntRect.h"
36 #include "NotImplemented.h"
37 #include "Page.h"
38 #include "PlatformScrollBar.h"
39 #include "PlatformMouseEvent.h"
40 #include "PlatformWheelEvent.h"
41 #include "RenderTheme.h" 
42 #include "ScrollBar.h"
43 #include <algorithm>
44 #include <winsock2.h>
45 #include <windows.h>
46 #include <wtf/Assertions.h>
47 #include <wtf/HashSet.h>
48
49 using namespace std;
50
51 namespace WebCore {
52
53 class ScrollView::ScrollViewPrivate : public ScrollbarClient {
54 public:
55     ScrollViewPrivate(ScrollView* view)
56         : m_view(view)
57         , m_hasStaticBackground(false)
58         , m_scrollbarsSuppressed(false)
59         , m_inUpdateScrollbars(false)
60         , m_scrollbarsAvoidingResizer(0)
61         , m_vScrollbarMode(ScrollbarAuto)
62         , m_hScrollbarMode(ScrollbarAuto)
63         , m_visible(false)
64         , m_attachedToWindow(false)
65     {
66     }
67
68     ~ScrollViewPrivate()
69     {
70         setHasHorizontalScrollbar(false);
71         setHasVerticalScrollbar(false);
72     }
73
74     void setHasHorizontalScrollbar(bool hasBar);
75     void setHasVerticalScrollbar(bool hasBar);
76
77     virtual void valueChanged(Scrollbar*);
78     virtual IntRect windowClipRect() const;
79
80     void scrollBackingStore(const IntSize& scrollDelta);
81
82     void setAllowsScrolling(bool);
83     bool allowsScrolling() const;
84
85     ScrollView* m_view;
86     IntSize m_scrollOffset;
87     IntSize m_contentsSize;
88     bool m_hasStaticBackground;
89     bool m_scrollbarsSuppressed;
90     bool m_inUpdateScrollbars;
91     int m_scrollbarsAvoidingResizer;
92     ScrollbarMode m_vScrollbarMode;
93     ScrollbarMode m_hScrollbarMode;
94     RefPtr<PlatformScrollbar> m_vBar;
95     RefPtr<PlatformScrollbar> m_hBar;
96     HRGN m_dirtyRegion;
97     HashSet<Widget*> m_children;
98     bool m_visible;
99     bool m_attachedToWindow;
100 };
101
102 void ScrollView::ScrollViewPrivate::setHasHorizontalScrollbar(bool hasBar)
103 {
104     if (Scrollbar::hasPlatformScrollbars()) {
105         if (hasBar && !m_hBar) {
106             m_hBar = new PlatformScrollbar(this, HorizontalScrollbar, RegularScrollbar);
107             m_view->addChild(m_hBar.get());
108         } else if (!hasBar && m_hBar) {
109             m_view->removeChild(m_hBar.get());
110             m_hBar = 0;
111         }
112     }
113 }
114
115 void ScrollView::ScrollViewPrivate::setHasVerticalScrollbar(bool hasBar)
116 {
117     if (Scrollbar::hasPlatformScrollbars()) {
118         if (hasBar && !m_vBar) {
119             m_vBar = new PlatformScrollbar(this, VerticalScrollbar, RegularScrollbar);
120             m_view->addChild(m_vBar.get());
121         } else if (!hasBar && m_vBar) {
122             m_view->removeChild(m_vBar.get());
123             m_vBar = 0;
124         }
125     }
126 }
127
128 void ScrollView::ScrollViewPrivate::valueChanged(Scrollbar* bar)
129 {
130     // Figure out if we really moved.
131     IntSize newOffset = m_scrollOffset;
132     if (bar) {
133         if (bar == m_hBar)
134             newOffset.setWidth(bar->value());
135         else if (bar == m_vBar)
136             newOffset.setHeight(bar->value());
137     }
138     IntSize scrollDelta = newOffset - m_scrollOffset;
139     if (scrollDelta == IntSize())
140         return;
141     m_scrollOffset = newOffset;
142
143     if (m_scrollbarsSuppressed)
144         return;
145
146     scrollBackingStore(scrollDelta);
147     static_cast<FrameView*>(m_view)->frame()->sendScrollEvent();
148 }
149
150 void ScrollView::ScrollViewPrivate::scrollBackingStore(const IntSize& scrollDelta)
151 {
152     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
153     // with the clip rect every time to keep it smooth.
154     HWND containingWindowHandle = m_view->containingWindow();
155     IntRect clipRect = m_view->windowClipRect();
156     IntRect scrollViewRect = m_view->convertToContainingWindow(IntRect(0, 0, m_view->visibleWidth(), m_view->visibleHeight()));
157     IntRect updateRect = clipRect;
158     updateRect.intersect(scrollViewRect);
159     RECT r = updateRect;
160     ::InvalidateRect(containingWindowHandle, &r, false);
161
162     if (!m_hasStaticBackground) // The main frame can just blit the WebView window
163        // FIXME: Find a way to blit subframes without blitting overlapping content
164        m_view->scrollBackingStore(-scrollDelta.width(), -scrollDelta.height(), scrollViewRect, clipRect);
165     else  {
166        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
167        // plugins.
168        m_view->addToDirtyRegion(updateRect);
169        m_view->updateBackingStore();
170     }
171
172     // This call will move child HWNDs (plugins) and invalidate them as well.
173     m_view->geometryChanged();
174
175     // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should
176     // be very fast).
177     ::UpdateWindow(containingWindowHandle);
178 }
179
180 void ScrollView::ScrollViewPrivate::setAllowsScrolling(bool flag)
181 {
182     if (flag && m_vScrollbarMode == ScrollbarAlwaysOff)
183         m_vScrollbarMode = ScrollbarAuto;
184     else if (!flag)
185         m_vScrollbarMode = ScrollbarAlwaysOff;
186
187     if (flag && m_hScrollbarMode == ScrollbarAlwaysOff)
188         m_hScrollbarMode = ScrollbarAuto;
189     else if (!flag)
190         m_hScrollbarMode = ScrollbarAlwaysOff;
191
192     m_view->updateScrollbars(m_scrollOffset);
193 }
194
195 bool ScrollView::ScrollViewPrivate::allowsScrolling() const
196 {
197     // Return YES if either horizontal or vertical scrolling is allowed.
198     return m_hScrollbarMode != ScrollbarAlwaysOff || m_vScrollbarMode != ScrollbarAlwaysOff;
199 }
200
201 IntRect ScrollView::ScrollViewPrivate::windowClipRect() const
202 {
203     return static_cast<const FrameView*>(m_view)->windowClipRect(false);
204 }
205
206 ScrollView::ScrollView()
207     : m_data(new ScrollViewPrivate(this))
208 {
209 }
210
211 ScrollView::~ScrollView()
212 {
213     delete m_data;
214 }
215
216 void ScrollView::updateContents(const IntRect& rect, bool now)
217 {
218     if (rect.isEmpty())
219         return;
220
221     IntPoint windowPoint = contentsToWindow(rect.location());
222     IntRect containingWindowRect = rect;
223     containingWindowRect.setLocation(windowPoint);
224
225     RECT containingWindowRectWin = containingWindowRect;
226     HWND containingWindowHandle = containingWindow();
227
228     ::InvalidateRect(containingWindowHandle, &containingWindowRectWin, false);
229
230     // Cache the dirty spot.
231     addToDirtyRegion(containingWindowRect);
232
233     if (now)
234         ::UpdateWindow(containingWindowHandle);
235 }
236
237 void ScrollView::update()
238 {
239     ::UpdateWindow(containingWindow());
240 }
241
242 int ScrollView::visibleWidth() const
243 {
244     return max(0, width() - (m_data->m_vBar ? m_data->m_vBar->width() : 0));
245 }
246
247 int ScrollView::visibleHeight() const
248 {
249     return max(0, height() - (m_data->m_hBar ? m_data->m_hBar->height() : 0));
250 }
251
252 FloatRect ScrollView::visibleContentRect() const
253 {
254     return FloatRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
255 }
256
257 FloatRect ScrollView::visibleContentRectConsideringExternalScrollers() const
258 {
259     // external scrollers not supported for now
260     return visibleContentRect();
261 }
262
263 void ScrollView::setContentsPos(int newX, int newY)
264 {
265     int dx = newX - contentsX();
266     int dy = newY - contentsY();
267     scrollBy(dx, dy);
268 }
269
270 void ScrollView::resizeContents(int w, int h)
271 {
272     IntSize newContentsSize(w, h);
273     if (m_data->m_contentsSize != newContentsSize) {
274         m_data->m_contentsSize = newContentsSize;
275         updateScrollbars(m_data->m_scrollOffset);
276     }
277 }
278
279 void ScrollView::setFrameGeometry(const IntRect& newGeometry)
280 {
281     IntRect oldGeometry = frameGeometry();
282     Widget::setFrameGeometry(newGeometry);
283
284     if (newGeometry == oldGeometry)
285         return;
286
287     if (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height()) {
288         updateScrollbars(m_data->m_scrollOffset);
289         static_cast<FrameView*>(this)->setNeedsLayout();
290     }
291
292     geometryChanged();
293 }
294
295 int ScrollView::contentsX() const
296 {
297     return scrollOffset().width();
298 }
299
300 int ScrollView::contentsY() const
301 {
302     return scrollOffset().height();
303 }
304
305 int ScrollView::contentsWidth() const
306 {
307     return m_data->m_contentsSize.width();
308 }
309
310 int ScrollView::contentsHeight() const
311 {
312     return m_data->m_contentsSize.height();
313 }
314
315 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
316 {
317     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
318     return viewPoint + scrollOffset();
319 }
320
321 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
322 {
323     IntPoint viewPoint = contentsPoint - scrollOffset();
324     return convertToContainingWindow(viewPoint);  
325 }
326
327 IntPoint ScrollView::convertChildToSelf(const Widget* child, const IntPoint& point) const
328 {
329     IntPoint newPoint = point;
330     if (child != m_data->m_hBar && child != m_data->m_vBar)
331         newPoint = point - scrollOffset();
332     return Widget::convertChildToSelf(child, newPoint);
333 }
334
335 IntPoint ScrollView::convertSelfToChild(const Widget* child, const IntPoint& point) const
336 {
337     IntPoint newPoint = point;
338     if (child != m_data->m_hBar && child != m_data->m_vBar)
339         newPoint = point + scrollOffset();
340     return Widget::convertSelfToChild(child, newPoint);
341 }
342
343 IntSize ScrollView::scrollOffset() const
344 {
345     return m_data->m_scrollOffset;
346 }
347
348 IntSize ScrollView::maximumScroll() const
349 {
350     IntSize delta = (m_data->m_contentsSize - IntSize(visibleWidth(), visibleHeight())) - scrollOffset();
351     delta.clampNegativeToZero();
352     return delta;
353 }
354
355 void ScrollView::scrollBy(int dx, int dy)
356 {
357     IntSize scrollOffset = m_data->m_scrollOffset;
358     IntSize newScrollOffset = scrollOffset + IntSize(dx, dy).shrunkTo(maximumScroll());
359     newScrollOffset.clampNegativeToZero();
360
361     if (newScrollOffset == scrollOffset)
362         return;
363
364     updateScrollbars(newScrollOffset);
365 }
366
367 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
368 {
369     IntPoint p(max(0, r.x()), max(0, r.y()));
370     ScrollView* view = this;
371     while (view) {
372         view->setContentsPos(p.x(), p.y());
373         p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
374         view = static_cast<ScrollView*>(view->parent());
375     }
376 }
377
378 WebCore::ScrollbarMode ScrollView::hScrollbarMode() const
379 {
380     return m_data->m_hScrollbarMode;
381 }
382
383 WebCore::ScrollbarMode ScrollView::vScrollbarMode() const
384 {
385     return m_data->m_vScrollbarMode;
386 }
387
388 void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnSuppress)
389 {
390     m_data->m_scrollbarsSuppressed = suppressed;
391     if (repaintOnSuppress && !suppressed) {
392         if (m_data->m_hBar)
393             m_data->m_hBar->invalidate();
394         if (m_data->m_vBar)
395             m_data->m_vBar->invalidate();
396
397         // Invalidate the scroll corner too on unsuppress.
398         IntRect hCorner;
399         if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) {
400             hCorner = IntRect(m_data->m_hBar->width(),
401                               height() - m_data->m_hBar->height(),
402                               width() - m_data->m_hBar->width(),
403                               m_data->m_hBar->height());
404             invalidateRect(hCorner);
405         }
406
407         if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) {
408             IntRect vCorner(width() - m_data->m_vBar->width(),
409                             m_data->m_vBar->height(),
410                             m_data->m_vBar->width(),
411                             height() - m_data->m_vBar->height());
412             if (vCorner != hCorner)
413                 invalidateRect(vCorner);
414         }
415     }
416 }
417
418 void ScrollView::setHScrollbarMode(ScrollbarMode newMode)
419 {
420     if (m_data->m_hScrollbarMode != newMode) {
421         m_data->m_hScrollbarMode = newMode;
422         updateScrollbars(m_data->m_scrollOffset);
423     }
424 }
425
426 void ScrollView::setVScrollbarMode(ScrollbarMode newMode)
427 {
428     if (m_data->m_vScrollbarMode != newMode) {
429         m_data->m_vScrollbarMode = newMode;
430         updateScrollbars(m_data->m_scrollOffset);
431     }
432 }
433
434 void ScrollView::setScrollbarsMode(ScrollbarMode newMode)
435 {
436     if (m_data->m_hScrollbarMode != newMode ||
437         m_data->m_vScrollbarMode != newMode) {
438         m_data->m_hScrollbarMode = m_data->m_vScrollbarMode = newMode;
439         updateScrollbars(m_data->m_scrollOffset);
440     }
441 }
442
443 void ScrollView::setStaticBackground(bool flag)
444 {
445     m_data->m_hasStaticBackground = flag;
446 }
447
448 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
449 {
450     // Don't allow re-entrancy into this function.
451     if (m_data->m_inUpdateScrollbars)
452         return;
453
454     // FIXME: This code is here so we don't have to fork FrameView.h/.cpp.
455     // In the end, FrameView should just merge with ScrollView.
456     if (static_cast<const FrameView*>(this)->frame()->prohibitsScrolling())
457         return;
458
459     m_data->m_inUpdateScrollbars = true;
460
461     bool hasVerticalScrollbar = m_data->m_vBar;
462     bool hasHorizontalScrollbar = m_data->m_hBar;
463     bool oldHasVertical = hasVerticalScrollbar;
464     bool oldHasHorizontal = hasHorizontalScrollbar;
465     ScrollbarMode hScroll = m_data->m_hScrollbarMode;
466     ScrollbarMode vScroll = m_data->m_vScrollbarMode;
467     
468     const int cVerticalWidth = PlatformScrollbar::verticalScrollbarWidth();
469     const int cHorizontalHeight = PlatformScrollbar::horizontalScrollbarHeight();
470
471     for (int pass = 0; pass < 2; pass++) {
472         bool scrollsVertically;
473         bool scrollsHorizontally;
474
475         if (!m_data->m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) {
476             // Do a layout if pending before checking if scrollbars are needed.
477             if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar != oldHasHorizontal)
478                 static_cast<FrameView*>(this)->layout();
479
480             scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() > height());
481             if (scrollsVertically)
482                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() + cVerticalWidth > width());
483             else {
484                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() > width());
485                 if (scrollsHorizontally)
486                     scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() + cHorizontalHeight > height());
487             }
488         }
489         else {
490             scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScrollbar : (hScroll == ScrollbarAlwaysOn);
491             scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollbar : (vScroll == ScrollbarAlwaysOn);
492         }
493         
494         if (hasVerticalScrollbar != scrollsVertically) {
495             m_data->setHasVerticalScrollbar(scrollsVertically);
496             hasVerticalScrollbar = scrollsVertically;
497         }
498
499         if (hasHorizontalScrollbar != scrollsHorizontally) {
500             m_data->setHasHorizontalScrollbar(scrollsHorizontally);
501             hasHorizontalScrollbar = scrollsHorizontally;
502         }
503     }
504     
505     // Set up the range (and page step/line step).
506     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
507     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
508     scroll.clampNegativeToZero();
509  
510     if (m_data->m_hBar) {
511         int clientWidth = visibleWidth();
512         m_data->m_hBar->setEnabled(contentsWidth() > clientWidth);
513         int pageStep = (clientWidth - PAGE_KEEP);
514         if (pageStep < 0) pageStep = clientWidth;
515         IntRect oldRect(m_data->m_hBar->frameGeometry());
516         IntRect hBarRect = IntRect(0,
517                                    height() - m_data->m_hBar->height(),
518                                    width() - (m_data->m_vBar ? m_data->m_vBar->width() : 0),
519                                    m_data->m_hBar->height());
520         m_data->m_hBar->setRect(hBarRect);
521         if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_hBar->frameGeometry())
522             m_data->m_hBar->invalidate();
523
524         if (m_data->m_scrollbarsSuppressed)
525             m_data->m_hBar->setSuppressInvalidation(true);
526         m_data->m_hBar->setSteps(LINE_STEP, pageStep);
527         m_data->m_hBar->setProportion(clientWidth, contentsWidth());
528         m_data->m_hBar->setValue(scroll.width());
529         if (m_data->m_scrollbarsSuppressed)
530             m_data->m_hBar->setSuppressInvalidation(false); 
531     } 
532
533     if (m_data->m_vBar) {
534         int clientHeight = visibleHeight();
535         m_data->m_vBar->setEnabled(contentsHeight() > clientHeight);
536         int pageStep = (clientHeight - PAGE_KEEP);
537         if (pageStep < 0) pageStep = clientHeight;
538         IntRect oldRect(m_data->m_vBar->frameGeometry());
539         IntRect vBarRect = IntRect(width() - m_data->m_vBar->width(), 
540                                    0,
541                                    m_data->m_vBar->width(),
542                                    height() - (m_data->m_hBar ? m_data->m_hBar->height() : 0));
543         m_data->m_vBar->setRect(vBarRect);
544         if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_vBar->frameGeometry())
545             m_data->m_vBar->invalidate();
546
547         if (m_data->m_scrollbarsSuppressed)
548             m_data->m_vBar->setSuppressInvalidation(true);
549         m_data->m_vBar->setSteps(LINE_STEP, pageStep);
550         m_data->m_vBar->setProportion(clientHeight, contentsHeight());
551         m_data->m_vBar->setValue(scroll.height());
552         if (m_data->m_scrollbarsSuppressed)
553             m_data->m_vBar->setSuppressInvalidation(false);
554     }
555
556     if (oldHasVertical != (m_data->m_vBar != 0) || oldHasHorizontal != (m_data->m_hBar != 0))
557         geometryChanged();
558
559     // See if our offset has changed in a situation where we might not have scrollbars.
560     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
561     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
562     // does not).
563     IntSize scrollDelta = scroll - m_data->m_scrollOffset;
564     if (scrollDelta != IntSize()) {
565        m_data->m_scrollOffset = scroll;
566        m_data->scrollBackingStore(scrollDelta);
567     }
568
569     m_data->m_inUpdateScrollbars = false;
570 }
571
572 PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mouseEvent)
573 {
574     IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos());
575     if (m_data->m_hBar && m_data->m_hBar->frameGeometry().contains(viewPoint))
576         return m_data->m_hBar.get();
577     if (m_data->m_vBar && m_data->m_vBar->frameGeometry().contains(viewPoint))
578         return m_data->m_vBar.get();
579     return 0;
580 }
581
582 void ScrollView::addChild(Widget* child) 
583
584     child->setParent(this);
585     child->setContainingWindow(containingWindow());
586     m_data->m_children.add(child);
587 }
588
589 void ScrollView::removeChild(Widget* child)
590 {
591     child->setParent(0);
592     m_data->m_children.remove(child);
593 }
594
595 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
596 {
597     // FIXME: This code is here so we don't have to fork FrameView.h/.cpp.
598     // In the end, FrameView should just merge with ScrollView.
599     ASSERT(isFrameView());
600
601     if (context->paintingDisabled())
602         return;
603
604     IntRect documentDirtyRect = rect;
605     documentDirtyRect.intersect(frameGeometry());
606
607     context->save();
608
609     context->translate(x(), y());
610     documentDirtyRect.move(-x(), -y());
611
612     context->translate(-contentsX(), -contentsY());
613     documentDirtyRect.move(contentsX(), contentsY());
614
615     context->clip(enclosingIntRect(visibleContentRect()));
616
617     static_cast<const FrameView*>(this)->frame()->paint(context, documentDirtyRect);
618
619     context->restore();
620
621     // Now paint the scrollbars.
622     if (!m_data->m_scrollbarsSuppressed && (m_data->m_hBar || m_data->m_vBar)) {
623         context->save();
624         IntRect scrollViewDirtyRect = rect;
625         scrollViewDirtyRect.intersect(frameGeometry());
626         context->translate(x(), y());
627         scrollViewDirtyRect.move(-x(), -y());
628         if (m_data->m_hBar)
629             m_data->m_hBar->paint(context, scrollViewDirtyRect);
630         if (m_data->m_vBar)
631             m_data->m_vBar->paint(context, scrollViewDirtyRect);
632
633         // Fill the scroll corner with white.
634         IntRect hCorner;
635         if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) {
636             hCorner = IntRect(m_data->m_hBar->width(),
637                               height() - m_data->m_hBar->height(),
638                               width() - m_data->m_hBar->width(),
639                               m_data->m_hBar->height());
640             if (hCorner.intersects(scrollViewDirtyRect))
641                 context->fillRect(hCorner, Color::white);
642         }
643
644         if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) {
645             IntRect vCorner(width() - m_data->m_vBar->width(),
646                             m_data->m_vBar->height(),
647                             m_data->m_vBar->width(),
648                             height() - m_data->m_vBar->height());
649             if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect))
650                 context->fillRect(vCorner, Color::white);
651         }
652
653         context->restore();
654     }
655 }
656
657 void ScrollView::themeChanged()
658 {
659     PlatformScrollbar::themeChanged();
660     theme()->themeChanged();
661     invalidate();
662 }
663
664 void ScrollView::wheelEvent(PlatformWheelEvent& e)
665 {
666     if (!m_data->allowsScrolling())
667         return;
668
669     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
670     IntSize maxScrollDelta = maximumScroll();
671     if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
672         (e.deltaX() > 0 && scrollOffset().width() > 0) ||
673         (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
674         (e.deltaY() > 0 && scrollOffset().height() > 0)) {
675         e.accept();
676         scrollBy(-e.deltaX() * LINE_STEP, -e.deltaY() * LINE_STEP);
677     }
678 }
679
680 HashSet<Widget*>* ScrollView::children()
681 {
682     return &(m_data->m_children);
683 }
684
685 void ScrollView::geometryChanged() const
686 {
687     HashSet<Widget*>::const_iterator end = m_data->m_children.end();
688     for (HashSet<Widget*>::const_iterator current = m_data->m_children.begin(); current != end; ++current)
689         (*current)->geometryChanged();
690 }
691
692 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
693 {
694     if  ((direction == ScrollUp || direction == ScrollDown) && m_data->m_vBar)
695         return m_data->m_vBar->scroll(direction, granularity);
696     else if (m_data->m_hBar)
697         return m_data->m_hBar->scroll(direction, granularity);
698 }
699
700 IntRect ScrollView::windowResizerRect()
701 {
702     ASSERT(isFrameView());
703     const FrameView* frameView = static_cast<const FrameView*>(this);
704     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
705     if (!page)
706         return IntRect();
707     return page->chrome()->windowResizerRect();
708 }
709
710 bool ScrollView::resizerOverlapsContent() const
711 {
712     return !m_data->m_scrollbarsAvoidingResizer;
713 }
714
715 void ScrollView::adjustOverlappingScrollbarCount(int overlapDelta)
716 {
717     int oldCount = m_data->m_scrollbarsAvoidingResizer;
718     m_data->m_scrollbarsAvoidingResizer += overlapDelta;
719     if (parent() && parent()->isFrameView())
720         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(overlapDelta);
721     else if (!m_data->m_scrollbarsSuppressed) {
722         // If we went from n to 0 or from 0 to n and we're the outermost view,
723         // we need to invalidate the windowResizerRect(), since it will now need to paint
724         // differently.
725         if (oldCount > 0 && m_data->m_scrollbarsAvoidingResizer == 0 ||
726             oldCount == 0 && m_data->m_scrollbarsAvoidingResizer > 0)
727             invalidateRect(windowResizerRect());
728     }
729 }
730
731 void ScrollView::setParent(ScrollView* parentView)
732 {
733     if (!parentView && m_data->m_scrollbarsAvoidingResizer && parent() && parent()->isFrameView())
734         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(false);
735     Widget::setParent(parentView);
736 }
737
738 void ScrollView::attachToWindow()
739 {
740     if (m_data->m_attachedToWindow)
741         return;
742
743     m_data->m_attachedToWindow = true;
744
745     if (m_data->m_visible) {
746         HashSet<Widget*>::iterator end = m_data->m_children.end();
747         for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != end; ++it)
748             (*it)->attachToWindow();
749     }
750 }
751
752 void ScrollView::detachFromWindow()
753 {
754     if (!m_data->m_attachedToWindow)
755         return;
756
757     if (m_data->m_visible) {
758         HashSet<Widget*>::iterator end = m_data->m_children.end();
759         for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != end; ++it)
760             (*it)->detachFromWindow();
761     }
762
763     m_data->m_attachedToWindow = false;
764 }
765
766 void ScrollView::show()
767 {
768     if (!m_data->m_visible) {
769         m_data->m_visible = true;
770         if (isAttachedToWindow()) {
771             HashSet<Widget*>::iterator end = m_data->m_children.end();
772             for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != end; ++it)
773                 (*it)->attachToWindow();
774         }
775     }
776
777     Widget::show();
778 }
779
780 void ScrollView::hide()
781 {
782     if (m_data->m_visible) {
783         if (isAttachedToWindow()) {
784             HashSet<Widget*>::iterator end = m_data->m_children.end();
785             for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != end; ++it)
786                 (*it)->detachFromWindow();
787         }
788         m_data->m_visible = false;
789     }
790
791     Widget::hide();
792 }
793
794 bool ScrollView::isAttachedToWindow() const
795 {
796     return m_data->m_attachedToWindow;
797 }
798
799 void ScrollView::addToDirtyRegion(const IntRect& containingWindowRect)
800 {
801     ASSERT(isFrameView());
802     const FrameView* frameView = static_cast<const FrameView*>(this);
803     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
804     if (!page)
805         return;
806     page->chrome()->addToDirtyRegion(containingWindowRect);
807 }
808
809 void ScrollView::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect)
810 {
811     ASSERT(isFrameView());
812     const FrameView* frameView = static_cast<const FrameView*>(this);
813     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
814     if (!page)
815         return;
816     page->chrome()->scrollBackingStore(dx, dy, scrollViewRect, clipRect);
817 }
818
819 void ScrollView::updateBackingStore()
820 {
821     ASSERT(isFrameView());
822     const FrameView* frameView = static_cast<const FrameView*>(this);
823     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
824     if (!page)
825         return;
826     page->chrome()->updateBackingStore();
827 }
828
829 void ScrollView::setAllowsScrolling(bool flag)
830 {
831     m_data->setAllowsScrolling(flag);
832 }
833
834 bool ScrollView::allowsScrolling() const
835 {
836     return m_data->allowsScrolling();
837 }
838
839 bool ScrollView::inWindow() const
840 {
841     // Needed for back/forward cache. 
842     notImplemented();
843     return true;
844 }
845
846 } // namespace WebCore