REGRESSION: Horizontal scrollbar thumbs leave artifacts over page content when scroll...
[WebKit.git] / Source / WebCore / platform / ScrollView.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 "AXObjectCache.h"
30 #include "GraphicsContext.h"
31 #include "HostWindow.h"
32 #include "PlatformMouseEvent.h"
33 #include "PlatformWheelEvent.h"
34 #include "ScrollAnimator.h"
35 #include "Scrollbar.h"
36 #include "ScrollbarTheme.h"
37 #include <wtf/StdLibExtras.h>
38
39 using namespace std;
40
41 namespace WebCore {
42
43 ScrollView::ScrollView()
44     : m_horizontalScrollbarMode(ScrollbarAuto)
45     , m_verticalScrollbarMode(ScrollbarAuto)
46     , m_horizontalScrollbarLock(false)
47     , m_verticalScrollbarLock(false)
48     , m_prohibitsScrolling(false)
49     , m_canBlitOnScroll(true)
50     , m_scrollbarsAvoidingResizer(0)
51     , m_scrollbarsSuppressed(false)
52     , m_inUpdateScrollbars(false)
53     , m_updateScrollbarsPass(0)
54     , m_drawPanScrollIcon(false)
55     , m_useFixedLayout(false)
56     , m_paintsEntireContents(false)
57     , m_clipsRepaints(true)
58     , m_delegatesScrolling(false)
59 {
60     platformInit();
61 }
62
63 ScrollView::~ScrollView()
64 {
65     platformDestroy();
66 }
67
68 void ScrollView::addChild(PassRefPtr<Widget> prpChild) 
69 {
70     Widget* child = prpChild.get();
71     ASSERT(child != this && !child->parent());
72     child->setParent(this);
73     m_children.add(prpChild);
74     if (child->platformWidget())
75         platformAddChild(child);
76 }
77
78 void ScrollView::removeChild(Widget* child)
79 {
80     ASSERT(child->parent() == this);
81     child->setParent(0);
82     m_children.remove(child);
83     if (child->platformWidget())
84         platformRemoveChild(child);
85 }
86
87 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
88 {
89     if (hasBar && avoidScrollbarCreation())
90         return;
91
92     if (hasBar && !m_horizontalScrollbar) {
93         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
94         addChild(m_horizontalScrollbar.get());
95         m_horizontalScrollbar->styleChanged();
96     } else if (!hasBar && m_horizontalScrollbar) {
97         removeChild(m_horizontalScrollbar.get());
98         m_horizontalScrollbar = 0;
99     }
100     
101     if (AXObjectCache::accessibilityEnabled() && axObjectCache())
102         axObjectCache()->handleScrollbarUpdate(this);
103 }
104
105 void ScrollView::setHasVerticalScrollbar(bool hasBar)
106 {
107     if (hasBar && avoidScrollbarCreation())
108         return;
109
110     if (hasBar && !m_verticalScrollbar) {
111         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
112         addChild(m_verticalScrollbar.get());
113         m_verticalScrollbar->styleChanged();
114     } else if (!hasBar && m_verticalScrollbar) {
115         removeChild(m_verticalScrollbar.get());
116         m_verticalScrollbar = 0;
117     }
118     
119     if (AXObjectCache::accessibilityEnabled() && axObjectCache())
120         axObjectCache()->handleScrollbarUpdate(this);
121 }
122
123 #if !PLATFORM(GTK)
124 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
125 {
126     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
127 }
128
129 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
130                                    bool horizontalLock, bool verticalLock)
131 {
132     bool needsUpdate = false;
133
134     if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
135         m_horizontalScrollbarMode = horizontalMode;
136         needsUpdate = true;
137     }
138
139     if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
140         m_verticalScrollbarMode = verticalMode;
141         needsUpdate = true;
142     }
143
144     if (horizontalLock)
145         setHorizontalScrollbarLock();
146
147     if (verticalLock)
148         setVerticalScrollbarLock();
149
150     if (!needsUpdate)
151         return;
152
153     if (platformWidget())
154         platformSetScrollbarModes();
155     else
156         updateScrollbars(scrollOffset());
157 }
158 #endif
159
160 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
161 {
162     if (platformWidget()) {
163         platformScrollbarModes(horizontalMode, verticalMode);
164         return;
165     }
166     horizontalMode = m_horizontalScrollbarMode;
167     verticalMode = m_verticalScrollbarMode;
168 }
169
170 void ScrollView::setCanHaveScrollbars(bool canScroll)
171 {
172     ScrollbarMode newHorizontalMode;
173     ScrollbarMode newVerticalMode;
174     
175     scrollbarModes(newHorizontalMode, newVerticalMode);
176     
177     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
178         newVerticalMode = ScrollbarAuto;
179     else if (!canScroll)
180         newVerticalMode = ScrollbarAlwaysOff;
181     
182     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
183         newHorizontalMode = ScrollbarAuto;
184     else if (!canScroll)
185         newHorizontalMode = ScrollbarAlwaysOff;
186     
187     setScrollbarModes(newHorizontalMode, newVerticalMode);
188 }
189
190 void ScrollView::setCanBlitOnScroll(bool b)
191 {
192     if (platformWidget()) {
193         platformSetCanBlitOnScroll(b);
194         return;
195     }
196
197     m_canBlitOnScroll = b;
198 }
199
200 bool ScrollView::canBlitOnScroll() const
201 {
202     if (platformWidget())
203         return platformCanBlitOnScroll();
204
205     return m_canBlitOnScroll;
206 }
207
208 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
209 {
210     m_paintsEntireContents = paintsEntireContents;
211 }
212
213 void ScrollView::setClipsRepaints(bool clipsRepaints)
214 {
215     m_clipsRepaints = clipsRepaints;
216 }
217
218 void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
219 {
220     m_delegatesScrolling = delegatesScrolling;
221 }
222
223 #if !PLATFORM(GTK)
224 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
225 {
226     if (platformWidget())
227         return platformVisibleContentRect(includeScrollbars);
228
229     if (paintsEntireContents())
230         return IntRect(IntPoint(0, 0), contentsSize());
231
232     bool hasOverlayScrollbars = ScrollbarTheme::nativeTheme()->usesOverlayScrollbars();
233     int verticalScrollbarWidth = verticalScrollbar() && !hasOverlayScrollbars && !includeScrollbars
234         ? verticalScrollbar()->width() : 0;
235     int horizontalScrollbarHeight = horizontalScrollbar() && !hasOverlayScrollbars && !includeScrollbars
236         ? horizontalScrollbar()->height() : 0;
237
238     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
239                    IntSize(max(0, width() - verticalScrollbarWidth), 
240                            max(0, height() - horizontalScrollbarHeight)));
241 }
242 #endif
243
244 int ScrollView::layoutWidth() const
245 {
246     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
247 }
248
249 int ScrollView::layoutHeight() const
250 {
251     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
252 }
253
254 IntSize ScrollView::fixedLayoutSize() const
255 {
256     return m_fixedLayoutSize;
257 }
258
259 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
260 {
261     if (fixedLayoutSize() == newSize)
262         return;
263     m_fixedLayoutSize = newSize;
264     updateScrollbars(scrollOffset());
265 }
266
267 bool ScrollView::useFixedLayout() const
268 {
269     return m_useFixedLayout;
270 }
271
272 void ScrollView::setUseFixedLayout(bool enable)
273 {
274     if (useFixedLayout() == enable)
275         return;
276     m_useFixedLayout = enable;
277     updateScrollbars(scrollOffset());
278 }
279
280 IntSize ScrollView::contentsSize() const
281 {
282     if (platformWidget())
283         return platformContentsSize();
284     return m_contentsSize;
285 }
286
287 void ScrollView::setContentsSize(const IntSize& newSize)
288 {
289     if (contentsSize() == newSize)
290         return;
291     m_contentsSize = newSize;
292     if (platformWidget())
293         platformSetContentsSize();
294     else
295         updateScrollbars(scrollOffset());
296 }
297
298 IntPoint ScrollView::maximumScrollPosition() const
299 {
300     IntPoint maximumOffset(contentsWidth() - visibleWidth() - m_scrollOrigin.x(), contentsHeight() - visibleHeight() - m_scrollOrigin.y());
301     maximumOffset.clampNegativeToZero();
302     return maximumOffset;
303 }
304
305 IntPoint ScrollView::minimumScrollPosition() const
306 {
307     return IntPoint(-m_scrollOrigin.x(), -m_scrollOrigin.y());
308 }
309
310 IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
311 {
312     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
313     newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
314     return newScrollPosition;
315 }
316
317 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
318 {
319     Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
320     return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
321 }
322
323 void ScrollView::didCompleteRubberBand(const IntSize&) const
324 {
325 }
326
327 void ScrollView::setScrollOffset(const IntPoint& offset)
328 {
329     int horizontalOffset = offset.x();
330     int verticalOffset = offset.y();
331     if (constrainsScrollingToContentEdge()) {
332         horizontalOffset = max(min(horizontalOffset, contentsWidth() - visibleWidth()), 0);
333         verticalOffset = max(min(verticalOffset, contentsHeight() - visibleHeight()), 0);
334     }
335
336     IntSize newOffset = m_scrollOffset;
337     newOffset.setWidth(horizontalOffset - m_scrollOrigin.x());
338     newOffset.setHeight(verticalOffset - m_scrollOrigin.y());
339
340     scrollTo(newOffset);
341 }
342
343 void ScrollView::scrollTo(const IntSize& newOffset)
344 {
345     IntSize scrollDelta = newOffset - m_scrollOffset;
346     if (scrollDelta == IntSize())
347         return;
348     m_scrollOffset = newOffset;
349
350     if (scrollbarsSuppressed())
351         return;
352
353     repaintFixedElementsAfterScrolling();
354     scrollContents(scrollDelta);
355 }
356
357 int ScrollView::scrollPosition(Scrollbar* scrollbar) const
358 {
359     if (scrollbar->orientation() == HorizontalScrollbar)
360         return scrollPosition().x() + m_scrollOrigin.x();
361     if (scrollbar->orientation() == VerticalScrollbar)
362         return scrollPosition().y() + m_scrollOrigin.y();
363     return 0;
364 }
365
366 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
367 {
368     if (prohibitsScrolling())
369         return;
370
371     if (platformWidget()) {
372         platformSetScrollPosition(scrollPoint);
373         return;
374     }
375
376 #if ENABLE(TILED_BACKING_STORE)
377     if (delegatesScrolling()) {
378         hostWindow()->delegatedScrollRequested(IntSize(scrollPoint.x(), scrollPoint.y()));
379         if (!m_actualVisibleContentRect.isEmpty())
380             m_actualVisibleContentRect.setLocation(scrollPoint);
381         return;
382     }
383 #endif
384
385     IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
386
387     if (newScrollPosition == scrollPosition())
388         return;
389
390     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
391 }
392
393 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
394 {
395     if (platformWidget())
396         return platformScroll(direction, granularity);
397
398     return ScrollableArea::scroll(direction, granularity);
399 }
400
401 bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity)
402 {
403     return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity);
404 }
405
406 IntSize ScrollView::overhangAmount() const
407 {
408     IntSize stretch;
409     if (scrollY() < 0)
410         stretch.setHeight(scrollY());
411     else if (scrollY() > contentsHeight() - visibleContentRect().height())
412         stretch.setHeight(scrollY() - (contentsHeight() - visibleContentRect().height()));
413
414     if (scrollX() < 0)
415         stretch.setWidth(scrollX());
416     else if (scrollX() > contentsWidth() - visibleContentRect().width())
417         stretch.setWidth(scrollX() - (contentsWidth() - visibleContentRect().width()));
418
419     return stretch;
420 }
421
422 void ScrollView::windowResizerRectChanged()
423 {
424     if (platformWidget())
425         return;
426
427     updateScrollbars(scrollOffset());
428 }
429
430 static const unsigned cMaxUpdateScrollbarsPass = 2;
431
432 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
433 {
434     if (m_inUpdateScrollbars || prohibitsScrolling() || delegatesScrolling() || platformWidget())
435         return;
436
437     // If we came in here with the view already needing a layout, then go ahead and do that
438     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
439     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
440     if (!m_scrollbarsSuppressed) {
441         m_inUpdateScrollbars = true;
442         visibleContentsResized();
443         m_inUpdateScrollbars = false;
444     }
445
446     bool hasHorizontalScrollbar = m_horizontalScrollbar;
447     bool hasVerticalScrollbar = m_verticalScrollbar;
448     
449     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
450     bool newHasVerticalScrollbar = hasVerticalScrollbar;
451    
452     ScrollbarMode hScroll = m_horizontalScrollbarMode;
453     ScrollbarMode vScroll = m_verticalScrollbarMode;
454
455     if (hScroll != ScrollbarAuto)
456         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
457     if (vScroll != ScrollbarAuto)
458         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
459
460     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
461         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
462             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
463         if (hasVerticalScrollbar != newHasVerticalScrollbar)
464             setHasVerticalScrollbar(newHasVerticalScrollbar);
465     } else {
466         bool sendContentResizedNotification = false;
467         
468         IntSize docSize = contentsSize();
469         IntSize frameSize = frameRect().size();
470
471         if (hScroll == ScrollbarAuto) {
472             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
473             if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
474                 newHasHorizontalScrollbar = false;
475         }
476         if (vScroll == ScrollbarAuto) {
477             newHasVerticalScrollbar = docSize.height() > visibleHeight();
478             if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
479                 newHasVerticalScrollbar = false;
480         }
481
482         // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
483         // try to both gain/lose a scrollbar in the same pass.
484         if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
485             newHasVerticalScrollbar = false;
486         if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
487             newHasHorizontalScrollbar = false;
488
489         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
490             if (m_scrollOrigin.y() && !newHasHorizontalScrollbar)
491                 m_scrollOrigin.setY(m_scrollOrigin.y() - m_horizontalScrollbar->height());
492             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
493             sendContentResizedNotification = true;
494         }
495
496         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
497             if (m_scrollOrigin.x() && !newHasVerticalScrollbar)
498                 m_scrollOrigin.setX(m_scrollOrigin.x() - m_verticalScrollbar->width());
499             setHasVerticalScrollbar(newHasVerticalScrollbar);
500             sendContentResizedNotification = true;
501         }
502
503         if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
504             m_updateScrollbarsPass++;
505             contentsResized();
506             visibleContentsResized();
507             IntSize newDocSize = contentsSize();
508             if (newDocSize == docSize) {
509                 // The layout with the new scroll state had no impact on
510                 // the document's overall size, so updateScrollbars didn't get called.
511                 // Recur manually.
512                 updateScrollbars(desiredOffset);
513             }
514             m_updateScrollbarsPass--;
515         }
516     }
517     
518     // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
519     // doing it multiple times).
520     if (m_updateScrollbarsPass)
521         return;
522
523     m_inUpdateScrollbars = true;
524
525     IntPoint scrollPoint = adjustScrollPositionWithinRange(IntPoint(desiredOffset));
526     IntSize scroll(scrollPoint.x(), scrollPoint.y());
527
528     if (m_horizontalScrollbar) {
529         int clientWidth = visibleWidth();
530         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
531         int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
532         IntRect oldRect(m_horizontalScrollbar->frameRect());
533         IntRect hBarRect = IntRect(0,
534                                    height() - m_horizontalScrollbar->height(),
535                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
536                                    m_horizontalScrollbar->height());
537         m_horizontalScrollbar->setFrameRect(hBarRect);
538         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
539             m_horizontalScrollbar->invalidate();
540
541         if (m_scrollbarsSuppressed)
542             m_horizontalScrollbar->setSuppressInvalidation(true);
543         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
544         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
545         if (m_scrollbarsSuppressed)
546             m_horizontalScrollbar->setSuppressInvalidation(false); 
547     } 
548
549     if (m_verticalScrollbar) {
550         int clientHeight = visibleHeight();
551         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
552         int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
553         IntRect oldRect(m_verticalScrollbar->frameRect());
554         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 
555                                    0,
556                                    m_verticalScrollbar->width(),
557                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
558         m_verticalScrollbar->setFrameRect(vBarRect);
559         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
560             m_verticalScrollbar->invalidate();
561
562         if (m_scrollbarsSuppressed)
563             m_verticalScrollbar->setSuppressInvalidation(true);
564         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
565         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
566         if (m_scrollbarsSuppressed)
567             m_verticalScrollbar->setSuppressInvalidation(false);
568     }
569
570     if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
571         frameRectsChanged();
572         updateScrollCorner();
573     }
574
575     ScrollableArea::scrollToOffsetWithoutAnimation(FloatPoint(scroll.width() + m_scrollOrigin.x(), scroll.height() + m_scrollOrigin.y()));
576
577     // Make sure the scrollbar offsets are up to date.
578     if (m_horizontalScrollbar)
579         m_horizontalScrollbar->offsetDidChange();
580     if (m_verticalScrollbar)
581         m_verticalScrollbar->offsetDidChange();
582
583     m_inUpdateScrollbars = false;
584 }
585
586 const int panIconSizeLength = 16;
587
588 void ScrollView::scrollContents(const IntSize& scrollDelta)
589 {
590     if (!hostWindow())
591         return;
592
593     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
594     // with the clip rect every time to keep it smooth.
595     IntRect clipRect = windowClipRect();
596     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
597     if (ScrollbarTheme::nativeTheme()->usesOverlayScrollbars()) {
598         int verticalScrollbarWidth = verticalScrollbar() ? verticalScrollbar()->width() : 0;
599         int horizontalScrollbarHeight = horizontalScrollbar() ? horizontalScrollbar()->height() : 0;
600
601         scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
602         scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
603     }
604     
605     IntRect updateRect = clipRect;
606     updateRect.intersect(scrollViewRect);
607
608     // Invalidate the window (not the backing store).
609     hostWindow()->invalidateWindow(updateRect, false /*immediate*/);
610
611     if (m_drawPanScrollIcon) {
612         // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
613         // https://bugs.webkit.org/show_bug.cgi?id=47837
614         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
615         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
616         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
617         panScrollIconDirtyRect.intersect(clipRect);
618         hostWindow()->invalidateContentsAndWindow(panScrollIconDirtyRect, false /*immediate*/);
619     }
620
621     if (canBlitOnScroll()) { // The main frame can just blit the WebView window
622         // FIXME: Find a way to scroll subframes with this faster path
623         if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
624             scrollContentsSlowPath(updateRect);
625     } else { 
626        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
627        // windowed plugins.
628        scrollContentsSlowPath(updateRect);
629     }
630
631     // This call will move children with native widgets (plugins) and invalidate them as well.
632     frameRectsChanged();
633
634     // Now blit the backingstore into the window which should be very fast.
635     hostWindow()->invalidateWindow(IntRect(), true);
636 }
637
638 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
639 {
640     hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
641     return true;
642 }
643
644 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
645 {
646     hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
647 }
648
649 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
650 {
651     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
652     return viewPoint + scrollOffset();
653 }
654
655 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
656 {
657     IntPoint viewPoint = contentsPoint - scrollOffset();
658     return convertToContainingWindow(viewPoint);  
659 }
660
661 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
662 {
663     IntRect viewRect = convertFromContainingWindow(windowRect);
664     viewRect.move(scrollOffset());
665     return viewRect;
666 }
667
668 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
669 {
670     IntRect viewRect = contentsRect;
671     viewRect.move(-scrollOffset());
672     return convertToContainingWindow(viewRect);
673 }
674
675 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
676 {
677     if (platformWidget())
678         return platformContentsToScreen(rect);
679     if (!hostWindow())
680         return IntRect();
681     return hostWindow()->windowToScreen(contentsToWindow(rect));
682 }
683
684 IntPoint ScrollView::screenToContents(const IntPoint& point) const
685 {
686     if (platformWidget())
687         return platformScreenToContents(point);
688     if (!hostWindow())
689         return IntPoint();
690     return windowToContents(hostWindow()->screenToWindow(point));
691 }
692
693 bool ScrollView::containsScrollbarsAvoidingResizer() const
694 {
695     return !m_scrollbarsAvoidingResizer;
696 }
697
698 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
699 {
700     int oldCount = m_scrollbarsAvoidingResizer;
701     m_scrollbarsAvoidingResizer += overlapDelta;
702     if (parent())
703         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
704     else if (!scrollbarsSuppressed()) {
705         // If we went from n to 0 or from 0 to n and we're the outermost view,
706         // we need to invalidate the windowResizerRect(), since it will now need to paint
707         // differently.
708         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
709             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
710             invalidateRect(windowResizerRect());
711     }
712 }
713
714 void ScrollView::setParent(ScrollView* parentView)
715 {
716     if (parentView == parent())
717         return;
718
719     if (m_scrollbarsAvoidingResizer && parent())
720         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
721
722     Widget::setParent(parentView);
723
724     if (m_scrollbarsAvoidingResizer && parent())
725         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
726 }
727
728 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
729 {
730     if (suppressed == m_scrollbarsSuppressed)
731         return;
732
733     m_scrollbarsSuppressed = suppressed;
734
735     if (platformWidget())
736         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
737     else if (repaintOnUnsuppress && !suppressed) {
738         if (m_horizontalScrollbar)
739             m_horizontalScrollbar->invalidate();
740         if (m_verticalScrollbar)
741             m_verticalScrollbar->invalidate();
742
743         // Invalidate the scroll corner too on unsuppress.
744         invalidateRect(scrollCornerRect());
745     }
746 }
747
748 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
749 {
750     if (platformWidget())
751         return 0;
752
753     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
754     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
755         return m_horizontalScrollbar.get();
756     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
757         return m_verticalScrollbar.get();
758     return 0;
759 }
760
761 void ScrollView::wheelEvent(PlatformWheelEvent& e)
762 {
763     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
764 #if PLATFORM(WX)
765     if (!canHaveScrollbars()) {
766 #else
767     if (!canHaveScrollbars() || platformWidget()) {
768 #endif
769         return;
770     }
771
772     ScrollableArea::handleWheelEvent(e);
773 }
774
775 #if ENABLE(GESTURE_EVENTS)
776 void ScrollView::gestureEvent(const PlatformGestureEvent& gestureEvent)
777 {
778     if (platformWidget())
779         return;
780
781     ScrollableArea::handleGestureEvent(gestureEvent);
782 }
783 #endif
784
785 void ScrollView::setFrameRect(const IntRect& newRect)
786 {
787     IntRect oldRect = frameRect();
788     
789     if (newRect == oldRect)
790         return;
791
792     Widget::setFrameRect(newRect);
793
794     if (platformWidget())
795         return;
796     
797     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
798         updateScrollbars(m_scrollOffset);
799         if (!m_useFixedLayout)
800             contentsResized();
801     }
802
803     frameRectsChanged();
804 }
805
806 void ScrollView::frameRectsChanged()
807 {
808     if (platformWidget())
809         return;
810
811     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
812     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
813         (*current)->frameRectsChanged();
814 }
815
816 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
817 {
818     IntRect paintRect = rect;
819     if (clipsRepaints() && !paintsEntireContents())
820         paintRect.intersect(visibleContentRect());
821     if (paintRect.isEmpty())
822         return;
823
824     if (platformWidget()) {
825         platformRepaintContentRectangle(paintRect, now);
826         return;
827     }
828
829     if (hostWindow())
830         hostWindow()->invalidateContentsAndWindow(contentsToWindow(paintRect), now /*immediate*/);
831 }
832
833 IntRect ScrollView::scrollCornerRect() const
834 {
835     IntRect cornerRect;
836
837     if (ScrollbarTheme::nativeTheme()->usesOverlayScrollbars())
838         return cornerRect;
839
840     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
841         cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
842                                  height() - m_horizontalScrollbar->height(),
843                                  width() - m_horizontalScrollbar->width(),
844                                  m_horizontalScrollbar->height()));
845     }
846
847     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
848         cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
849                                  m_verticalScrollbar->height(),
850                                  m_verticalScrollbar->width(),
851                                  height() - m_verticalScrollbar->height()));
852     }
853     
854     return cornerRect;
855 }
856
857 void ScrollView::updateScrollCorner()
858 {
859 }
860
861 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
862 {
863     ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
864 }
865
866 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
867 {
868     if (m_horizontalScrollbar)
869         m_horizontalScrollbar->paint(context, rect);
870     if (m_verticalScrollbar)
871         m_verticalScrollbar->paint(context, rect);
872
873     paintScrollCorner(context, scrollCornerRect());
874 }
875
876 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
877 {
878     static Image* panScrollIcon = Image::loadPlatformResource("panIcon").releaseRef();
879     context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, m_panScrollIconPoint);
880 }
881
882 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
883 {
884     if (platformWidget()) {
885         Widget::paint(context, rect);
886         return;
887     }
888
889     if (context->paintingDisabled() && !context->updatingControlTints())
890         return;
891
892     scrollAnimator()->contentAreaWillPaint();
893     
894     IntRect documentDirtyRect = rect;
895     documentDirtyRect.intersect(frameRect());
896
897     context->save();
898
899     context->translate(x(), y());
900     documentDirtyRect.move(-x(), -y());
901
902     if (!paintsEntireContents()) {
903         context->translate(-scrollX(), -scrollY());
904         documentDirtyRect.move(scrollX(), scrollY());
905
906         context->clip(visibleContentRect());
907     }
908
909     paintContents(context, documentDirtyRect);
910
911     context->restore();
912
913     IntRect horizontalOverhangRect;
914     IntRect verticalOverhangRect;
915     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
916
917     if (rect.intersects(horizontalOverhangRect) || rect.intersects(verticalOverhangRect))
918         paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, rect);
919
920     // Now paint the scrollbars.
921     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
922         context->save();
923         IntRect scrollViewDirtyRect = rect;
924         scrollViewDirtyRect.intersect(frameRect());
925         context->translate(x(), y());
926         scrollViewDirtyRect.move(-x(), -y());
927
928         paintScrollbars(context, scrollViewDirtyRect);
929
930         context->restore();
931     }
932
933     // Paint the panScroll Icon
934     if (m_drawPanScrollIcon)
935         paintPanScrollIcon(context);
936 }
937
938 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
939 {
940     bool hasOverlayScrollbars = ScrollbarTheme::nativeTheme()->usesOverlayScrollbars();
941     int verticalScrollbarWidth = (verticalScrollbar() && !hasOverlayScrollbars) ? verticalScrollbar()->width() : 0;
942     int horizontalScrollbarHeight = (horizontalScrollbar() && !hasOverlayScrollbars) ? horizontalScrollbar()->height() : 0;
943
944     if (scrollY() < 0) {
945         horizontalOverhangRect = frameRect();
946         horizontalOverhangRect.setHeight(-scrollY());
947     } else if (scrollY() > contentsHeight() - visibleContentRect().height()) {
948         int height = scrollY() - (contentsHeight() - visibleContentRect().height());
949         horizontalOverhangRect = frameRect();
950         horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
951         horizontalOverhangRect.setHeight(height);
952     }
953
954     if (scrollX() < 0) {
955         verticalOverhangRect.setWidth(-scrollX());
956         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height());
957         verticalOverhangRect.setX(frameRect().x());
958         if (horizontalOverhangRect.y() == frameRect().y())
959             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
960         else
961             verticalOverhangRect.setY(frameRect().y());
962     } else if (scrollX() > contentsWidth() - visibleContentRect().width()) {
963         int width = scrollX() - (contentsWidth() - visibleContentRect().width());
964         verticalOverhangRect.setWidth(width);
965         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height());
966         verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
967         if (horizontalOverhangRect.y() == frameRect().y())
968             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
969         else
970             verticalOverhangRect.setY(frameRect().y());
971     }
972 }
973
974 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect&)
975 {
976     // FIXME: This should be checking the dirty rect.
977
978     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
979     if (!horizontalOverhangRect.isEmpty())
980         context->fillRect(horizontalOverhangRect);
981
982     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
983     if (!verticalOverhangRect.isEmpty())
984         context->fillRect(verticalOverhangRect);
985 }
986
987 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
988 {
989     if (!scrollbarCornerPresent())
990         return false;
991
992     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
993
994     if (m_horizontalScrollbar) {
995         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
996         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
997         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
998
999         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1000     }
1001
1002     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1003     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1004     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1005     
1006     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1007 }
1008
1009 bool ScrollView::scrollbarCornerPresent() const
1010 {
1011     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
1012            (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1013 }
1014
1015 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1016 {
1017     // Scrollbars won't be transformed within us
1018     IntRect newRect = localRect;
1019     newRect.move(scrollbar->x(), scrollbar->y());
1020     return newRect;
1021 }
1022
1023 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1024 {
1025     IntRect newRect = parentRect;
1026     // Scrollbars won't be transformed within us
1027     newRect.move(-scrollbar->x(), -scrollbar->y());
1028     return newRect;
1029 }
1030
1031 // FIXME: test these on windows
1032 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1033 {
1034     // Scrollbars won't be transformed within us
1035     IntPoint newPoint = localPoint;
1036     newPoint.move(scrollbar->x(), scrollbar->y());
1037     return newPoint;
1038 }
1039
1040 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1041 {
1042     IntPoint newPoint = parentPoint;
1043     // Scrollbars won't be transformed within us
1044     newPoint.move(-scrollbar->x(), -scrollbar->y());
1045     return newPoint;
1046 }
1047
1048 void ScrollView::setParentVisible(bool visible)
1049 {
1050     if (isParentVisible() == visible)
1051         return;
1052     
1053     Widget::setParentVisible(visible);
1054
1055     if (!isSelfVisible())
1056         return;
1057         
1058     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1059     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1060         (*it)->setParentVisible(visible);
1061 }
1062
1063 void ScrollView::show()
1064 {
1065     if (!isSelfVisible()) {
1066         setSelfVisible(true);
1067         if (isParentVisible()) {
1068             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1069             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1070                 (*it)->setParentVisible(true);
1071         }
1072     }
1073
1074     Widget::show();
1075 }
1076
1077 void ScrollView::hide()
1078 {
1079     if (isSelfVisible()) {
1080         if (isParentVisible()) {
1081             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1082             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1083                 (*it)->setParentVisible(false);
1084         }
1085         setSelfVisible(false);
1086     }
1087
1088     Widget::hide();
1089 }
1090
1091 bool ScrollView::isOffscreen() const
1092 {
1093     if (platformWidget())
1094         return platformIsOffscreen();
1095     
1096     if (!isVisible())
1097         return true;
1098     
1099     // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
1100     // currently, we can add the method when the other platforms decide to implement this concept.
1101     return false;
1102 }
1103
1104
1105 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1106 {
1107     if (!hostWindow())
1108         return;
1109     m_drawPanScrollIcon = true;    
1110     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1111     hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1112 }
1113
1114 void ScrollView::removePanScrollIcon()
1115 {
1116     if (!hostWindow())
1117         return;
1118     m_drawPanScrollIcon = false; 
1119     hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1120 }
1121
1122 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1123 {
1124     if (m_scrollOrigin == origin)
1125         return;
1126
1127     m_scrollOrigin = origin;
1128
1129     if (platformWidget()) {
1130         platformSetScrollOrigin(origin, updatePositionAtAll, updatePositionSynchronously);
1131         return;
1132     }
1133     
1134     // Update if the scroll origin changes, since our position will be different if the content size did not change.
1135     if (updatePositionAtAll && updatePositionSynchronously)
1136         updateScrollbars(scrollOffset());
1137 }
1138
1139 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(EFL)
1140
1141 void ScrollView::platformInit()
1142 {
1143 }
1144
1145 void ScrollView::platformDestroy()
1146 {
1147 }
1148
1149 #endif
1150
1151 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
1152
1153 void ScrollView::platformAddChild(Widget*)
1154 {
1155 }
1156
1157 void ScrollView::platformRemoveChild(Widget*)
1158 {
1159 }
1160
1161 #endif
1162
1163 #if !PLATFORM(MAC)
1164
1165 void ScrollView::platformSetScrollbarsSuppressed(bool)
1166 {
1167 }
1168
1169 void ScrollView::platformSetScrollOrigin(const IntPoint&, bool updatePositionAtAll, bool updatePositionSynchronously)
1170 {
1171 }
1172
1173 #endif
1174
1175 #if !PLATFORM(MAC) && !PLATFORM(WX)
1176
1177 void ScrollView::platformSetScrollbarModes()
1178 {
1179 }
1180
1181 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
1182 {
1183     horizontal = ScrollbarAuto;
1184     vertical = ScrollbarAuto;
1185 }
1186
1187 void ScrollView::platformSetCanBlitOnScroll(bool)
1188 {
1189 }
1190
1191 bool ScrollView::platformCanBlitOnScroll() const
1192 {
1193     return false;
1194 }
1195
1196 IntRect ScrollView::platformVisibleContentRect(bool) const
1197 {
1198     return IntRect();
1199 }
1200
1201 IntSize ScrollView::platformContentsSize() const
1202 {
1203     return IntSize();
1204 }
1205
1206 void ScrollView::platformSetContentsSize()
1207 {
1208 }
1209
1210 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1211 {
1212     return rect;
1213 }
1214
1215 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1216 {
1217     return point;
1218 }
1219
1220 void ScrollView::platformSetScrollPosition(const IntPoint&)
1221 {
1222 }
1223
1224 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1225 {
1226     return true;
1227 }
1228
1229 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1230 {
1231 }
1232
1233 bool ScrollView::platformIsOffscreen() const
1234 {
1235     return false;
1236 }
1237
1238 #endif
1239
1240 }