Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2008, 2010 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 INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "WebDynamicScrollBarsViewInternal.h"
     27 
     28 #import "WebDocument.h"
     29 #import "WebFrameInternal.h"
     30 #import "WebFrameView.h"
     31 #import "WebHTMLViewInternal.h"
     32 #import <WebCore/Frame.h>
     33 #import <WebCore/FrameView.h>
     34 #import <WebKitSystemInterface.h>
     35 
     36 using namespace WebCore;
     37 
     38 // FIXME: <rdar://problem/5898985> Mail expects a constant of this name to exist.
     39 const int WebCoreScrollbarAlwaysOn = ScrollbarAlwaysOn;
     40 
     41 #ifndef __OBJC2__
     42 // In <rdar://problem/7814899> we saw crashes because WebDynamicScrollBarsView increased in size, breaking ABI compatiblity.
     43 COMPILE_ASSERT(sizeof(WebDynamicScrollBarsView) == 0x8c, WebDynamicScrollBarsView_is_expected_size);
     44 #endif
     45 
     46 struct WebDynamicScrollBarsViewPrivate {
     47     unsigned inUpdateScrollersLayoutPass;
     48 
     49     WebCore::ScrollbarMode hScroll;
     50     WebCore::ScrollbarMode vScroll;
     51 
     52     bool hScrollModeLocked;
     53     bool vScrollModeLocked;
     54     bool suppressLayout;
     55     bool suppressScrollers;
     56     bool inUpdateScrollers;
     57     bool verticallyPinnedByPreviousWheelEvent;
     58     bool horizontallyPinnedByPreviousWheelEvent;
     59 
     60     bool allowsScrollersToOverlapContent;
     61     bool alwaysHideHorizontalScroller;
     62     bool alwaysHideVerticalScroller;
     63     bool horizontalScrollingAllowedButScrollerHidden;
     64     bool verticalScrollingAllowedButScrollerHidden;
     65 
     66     // scrollOrigin is set for various combinations of writing mode and direction.
     67     // See the comment next to the corresponding member in ScrollView.h.
     68     NSPoint scrollOrigin;
     69 
     70     // Flag to indicate that the scrollbar thumb's initial position needs to
     71     // be manually set.
     72     bool scrollOriginChanged;
     73     NSPoint scrollPositionExcludingOrigin;
     74 
     75     bool inProgrammaticScroll;
     76 };
     77 
     78 @implementation WebDynamicScrollBarsView
     79 
     80 - (id)initWithFrame:(NSRect)frame
     81 {
     82     if (!(self = [super initWithFrame:frame]))
     83         return nil;
     84 
     85     _private = new WebDynamicScrollBarsViewPrivate;
     86     memset(_private, 0, sizeof(WebDynamicScrollBarsViewPrivate));
     87     return self;
     88 }
     89 
     90 - (id)initWithCoder:(NSCoder *)aDecoder
     91 {
     92     if (!(self = [super initWithCoder:aDecoder]))
     93         return nil;
     94 
     95     _private = new WebDynamicScrollBarsViewPrivate;
     96     memset(_private, 0, sizeof(WebDynamicScrollBarsViewPrivate));
     97     return self;
     98 }
     99 
    100 - (void)dealloc
    101 {
    102     delete _private;
    103     [super dealloc];
    104 }
    105 
    106 - (void)finalize
    107 {
    108     delete _private;
    109     [super finalize];
    110 }
    111 
    112 - (void)setAllowsHorizontalScrolling:(BOOL)flag
    113 {
    114     if (_private->hScrollModeLocked)
    115         return;
    116     if (flag && _private->hScroll == ScrollbarAlwaysOff)
    117         _private->hScroll = ScrollbarAuto;
    118     else if (!flag && _private->hScroll != ScrollbarAlwaysOff)
    119         _private->hScroll = ScrollbarAlwaysOff;
    120     [self updateScrollers];
    121 }
    122 
    123 - (void)setAllowsScrollersToOverlapContent:(BOOL)flag
    124 {
    125     if (_private->allowsScrollersToOverlapContent == flag)
    126         return;
    127 
    128     _private->allowsScrollersToOverlapContent = flag;
    129 
    130     [[self contentView] setFrame:[self contentViewFrame]];
    131     [[self documentView] setNeedsLayout:YES];
    132     [[self documentView] layout];
    133 }
    134 
    135 - (void)setAlwaysHideHorizontalScroller:(BOOL)shouldBeHidden
    136 {
    137     if (_private->alwaysHideHorizontalScroller == shouldBeHidden)
    138         return;
    139 
    140     _private->alwaysHideHorizontalScroller = shouldBeHidden;
    141     [self updateScrollers];
    142 }
    143 
    144 - (void)setAlwaysHideVerticalScroller:(BOOL)shouldBeHidden
    145 {
    146     if (_private->alwaysHideVerticalScroller == shouldBeHidden)
    147         return;
    148 
    149     _private->alwaysHideVerticalScroller = shouldBeHidden;
    150     [self updateScrollers];
    151 }
    152 
    153 - (BOOL)horizontalScrollingAllowed
    154 {
    155     return _private->horizontalScrollingAllowedButScrollerHidden || [self hasHorizontalScroller];
    156 }
    157 
    158 - (BOOL)verticalScrollingAllowed
    159 {
    160     return _private->verticalScrollingAllowedButScrollerHidden || [self hasVerticalScroller];
    161 }
    162 
    163 @end
    164 
    165 @implementation WebDynamicScrollBarsView (WebInternal)
    166 
    167 - (NSRect)contentViewFrame
    168 {
    169     NSRect frame = [[self contentView] frame];
    170 
    171     if ([self hasHorizontalScroller])
    172         frame.size.height = (_private->allowsScrollersToOverlapContent ? NSMaxY([[self horizontalScroller] frame]) : NSMinY([[self horizontalScroller] frame]));
    173     if ([self hasVerticalScroller])
    174         frame.size.width = (_private->allowsScrollersToOverlapContent ? NSMaxX([[self verticalScroller] frame]) : NSMinX([[self verticalScroller] frame]));
    175     return frame;
    176 }
    177 
    178 - (void)tile
    179 {
    180     [super tile];
    181 
    182     // [super tile] sets the contentView size so that it does not overlap with the scrollers,
    183     // we want to re-set the contentView to overlap scrollers before displaying.
    184     if (_private->allowsScrollersToOverlapContent)
    185         [[self contentView] setFrame:[self contentViewFrame]];
    186 }
    187 
    188 - (void)setSuppressLayout:(BOOL)flag
    189 {
    190     _private->suppressLayout = flag;
    191 }
    192 
    193 - (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint
    194 {
    195     _private->suppressScrollers = suppressed;
    196 
    197     // This code was originally changes for a Leopard performance imporvement. We decided to
    198     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
    199 #ifndef BUILDING_ON_TIGER
    200     if (suppressed) {
    201         [[self verticalScroller] setNeedsDisplay:NO];
    202         [[self horizontalScroller] setNeedsDisplay:NO];
    203     }
    204 
    205     if (!suppressed && repaint)
    206         [super reflectScrolledClipView:[self contentView]];
    207 #else
    208     if (suppressed || repaint) {
    209         [[self verticalScroller] setNeedsDisplay:!suppressed];
    210         [[self horizontalScroller] setNeedsDisplay:!suppressed];
    211     }
    212 #endif
    213 }
    214 
    215 - (void)adjustForScrollOriginChange
    216 {
    217     if (!_private->scrollOriginChanged)
    218         return;
    219 
    220     _private->scrollOriginChanged = false;
    221 
    222     NSView *documentView = [self documentView];
    223     NSRect documentRect = [documentView bounds];
    224 
    225     // The call to [NSView scrollPoint:] fires off notification the handler for which needs to know that
    226     // we're setting the initial scroll position so it doesn't interpret this as a user action and
    227     // fire off a JS event.
    228     _private->inProgrammaticScroll = true;
    229     [documentView scrollPoint:NSMakePoint(_private->scrollPositionExcludingOrigin.x + documentRect.origin.x, _private->scrollPositionExcludingOrigin.y + documentRect.origin.y)];
    230     _private->inProgrammaticScroll = false;
    231 }
    232 
    233 static const unsigned cMaxUpdateScrollbarsPass = 2;
    234 
    235 - (void)updateScrollers
    236 {
    237     NSView *documentView = [self documentView];
    238 
    239     // If we came in here with the view already needing a layout, then go ahead and do that
    240     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
    241     // This layout will not re-enter updateScrollers and does not count towards our max layout pass total.
    242     if (!_private->suppressLayout && !_private->suppressScrollers && [documentView isKindOfClass:[WebHTMLView class]]) {
    243         WebHTMLView* htmlView = (WebHTMLView*)documentView;
    244         if ([htmlView _needsLayout]) {
    245             _private->inUpdateScrollers = YES;
    246             [(id <WebDocumentView>)documentView layout];
    247             _private->inUpdateScrollers = NO;
    248         }
    249     }
    250 
    251     BOOL hasHorizontalScroller = [self hasHorizontalScroller];
    252     BOOL hasVerticalScroller = [self hasVerticalScroller];
    253 
    254     BOOL newHasHorizontalScroller = hasHorizontalScroller;
    255     BOOL newHasVerticalScroller = hasVerticalScroller;
    256 
    257     if (!documentView) {
    258         newHasHorizontalScroller = NO;
    259         newHasVerticalScroller = NO;
    260     }
    261 
    262     if (_private->hScroll != ScrollbarAuto)
    263         newHasHorizontalScroller = (_private->hScroll == ScrollbarAlwaysOn);
    264     if (_private->vScroll != ScrollbarAuto)
    265         newHasVerticalScroller = (_private->vScroll == ScrollbarAlwaysOn);
    266 
    267     if (!documentView || _private->suppressLayout || _private->suppressScrollers || (_private->hScroll != ScrollbarAuto && _private->vScroll != ScrollbarAuto)) {
    268         _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller;
    269         if (_private->horizontalScrollingAllowedButScrollerHidden)
    270             newHasHorizontalScroller = NO;
    271 
    272         _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller;
    273         if (_private->verticalScrollingAllowedButScrollerHidden)
    274             newHasVerticalScroller = NO;
    275 
    276         _private->inUpdateScrollers = YES;
    277         if (hasHorizontalScroller != newHasHorizontalScroller)
    278             [self setHasHorizontalScroller:newHasHorizontalScroller];
    279         if (hasVerticalScroller != newHasVerticalScroller)
    280             [self setHasVerticalScroller:newHasVerticalScroller];
    281         if (_private->suppressScrollers) {
    282             [[self verticalScroller] setNeedsDisplay:NO];
    283             [[self horizontalScroller] setNeedsDisplay:NO];
    284         }
    285         _private->inUpdateScrollers = NO;
    286         return;
    287     }
    288 
    289     BOOL needsLayout = NO;
    290 
    291     NSSize documentSize = [documentView frame].size;
    292     NSSize visibleSize = [self documentVisibleRect].size;
    293     NSSize frameSize = [self frame].size;
    294 
    295     // When in HiDPI with a scale factor > 1, the visibleSize and frameSize may be non-integral values,
    296     // while the documentSize (set by WebCore) will be integral.  Round up the non-integral sizes so that
    297     // the mismatch won't cause unwanted scrollbars to appear.  This can result in slightly cut off content,
    298     // but it will always be less than one pixel, which should not be noticeable.
    299     visibleSize.width = ceilf(visibleSize.width);
    300     visibleSize.height = ceilf(visibleSize.height);
    301     frameSize.width = ceilf(frameSize.width);
    302     frameSize.height = ceilf(frameSize.height);
    303 
    304     if (_private->hScroll == ScrollbarAuto) {
    305         newHasHorizontalScroller = documentSize.width > visibleSize.width;
    306         if (newHasHorizontalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
    307             newHasHorizontalScroller = NO;
    308     }
    309 
    310     if (_private->vScroll == ScrollbarAuto) {
    311         newHasVerticalScroller = documentSize.height > visibleSize.height;
    312         if (newHasVerticalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
    313             newHasVerticalScroller = NO;
    314     }
    315 
    316     // Unless in ScrollbarsAlwaysOn mode, if we ever turn one scrollbar off, always turn the other one off too.
    317     // Never ever try to both gain/lose a scrollbar in the same pass.
    318     if (!newHasHorizontalScroller && hasHorizontalScroller && _private->vScroll != ScrollbarAlwaysOn)
    319         newHasVerticalScroller = NO;
    320     if (!newHasVerticalScroller && hasVerticalScroller && _private->hScroll != ScrollbarAlwaysOn)
    321         newHasHorizontalScroller = NO;
    322 
    323     _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller;
    324     if (_private->horizontalScrollingAllowedButScrollerHidden)
    325         newHasHorizontalScroller = NO;
    326 
    327     _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller;
    328     if (_private->verticalScrollingAllowedButScrollerHidden)
    329         newHasVerticalScroller = NO;
    330 
    331     if (hasHorizontalScroller != newHasHorizontalScroller) {
    332         _private->inUpdateScrollers = YES;
    333         [self setHasHorizontalScroller:newHasHorizontalScroller];
    334         _private->inUpdateScrollers = NO;
    335         needsLayout = YES;
    336         NSView *documentView = [self documentView];
    337         NSRect documentRect = [documentView bounds];
    338         if (documentRect.origin.y < 0 && !newHasHorizontalScroller)
    339             [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x, documentRect.origin.y + 15)];
    340     }
    341 
    342     if (hasVerticalScroller != newHasVerticalScroller) {
    343         _private->inUpdateScrollers = YES;
    344         [self setHasVerticalScroller:newHasVerticalScroller];
    345         _private->inUpdateScrollers = NO;
    346         needsLayout = YES;
    347         NSView *documentView = [self documentView];
    348         NSRect documentRect = [documentView bounds];
    349         if (documentRect.origin.x < 0 && !newHasVerticalScroller)
    350             [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x + 15, documentRect.origin.y)];
    351     }
    352 
    353     if (needsLayout && _private->inUpdateScrollersLayoutPass < cMaxUpdateScrollbarsPass &&
    354         [documentView conformsToProtocol:@protocol(WebDocumentView)]) {
    355         _private->inUpdateScrollersLayoutPass++;
    356         [(id <WebDocumentView>)documentView setNeedsLayout:YES];
    357         [(id <WebDocumentView>)documentView layout];
    358         NSSize newDocumentSize = [documentView frame].size;
    359         if (NSEqualSizes(documentSize, newDocumentSize)) {
    360             // The layout with the new scroll state had no impact on
    361             // the document's overall size, so updateScrollers didn't get called.
    362             // Recur manually.
    363             [self updateScrollers];
    364         }
    365         _private->inUpdateScrollersLayoutPass--;
    366     }
    367 }
    368 
    369 // Make the horizontal and vertical scroll bars come and go as needed.
    370 - (void)reflectScrolledClipView:(NSClipView *)clipView
    371 {
    372     if (clipView == [self contentView]) {
    373         // Prevent appearance of trails because of overlapping views
    374         if (_private->allowsScrollersToOverlapContent)
    375             [self setDrawsBackground:NO];
    376 
    377         // FIXME: This hack here prevents infinite recursion that takes place when we
    378         // gyrate between having a vertical scroller and not having one. A reproducible
    379         // case is clicking on the "the Policy Routing text" link at
    380         // http://www.linuxpowered.com/archive/howto/Net-HOWTO-8.html.
    381         // The underlying cause is some problem in the NSText machinery, but I was not
    382         // able to pin it down.
    383         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
    384         if (!_private->inUpdateScrollers && (!currentContext || [currentContext isDrawingToScreen]))
    385             [self updateScrollers];
    386     }
    387 
    388     // This code was originally changed for a Leopard performance imporvement. We decided to
    389     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
    390 #ifndef BUILDING_ON_TIGER
    391     // Update the scrollers if they're not being suppressed.
    392     if (!_private->suppressScrollers)
    393         [super reflectScrolledClipView:clipView];
    394 #else
    395     [super reflectScrolledClipView:clipView];
    396 
    397     // Validate the scrollers if they're being suppressed.
    398     if (_private->suppressScrollers) {
    399         [[self verticalScroller] setNeedsDisplay:NO];
    400         [[self horizontalScroller] setNeedsDisplay:NO];
    401     }
    402 #endif
    403 
    404     // The call to [NSView reflectScrolledClipView] sets the scrollbar thumb
    405     // position to 0 (the left) when the view is initially displayed.
    406     // This call updates the initial position correctly.
    407     [self adjustForScrollOriginChange];
    408 
    409 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
    410     NSView *documentView = [self documentView];
    411     if ([documentView isKindOfClass:[WebHTMLView class]]) {
    412         WebHTMLView *htmlView = (WebHTMLView *)documentView;
    413         if ([htmlView _isUsingAcceleratedCompositing])
    414             [htmlView _updateLayerHostingViewPosition];
    415     }
    416 #endif
    417 }
    418 
    419 - (BOOL)allowsHorizontalScrolling
    420 {
    421     return _private->hScroll != ScrollbarAlwaysOff;
    422 }
    423 
    424 - (BOOL)allowsVerticalScrolling
    425 {
    426     return _private->vScroll != ScrollbarAlwaysOff;
    427 }
    428 
    429 - (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode
    430 {
    431     *hMode = _private->hScroll;
    432     *vMode = _private->vScroll;
    433 }
    434 
    435 - (ScrollbarMode)horizontalScrollingMode
    436 {
    437     return _private->hScroll;
    438 }
    439 
    440 - (ScrollbarMode)verticalScrollingMode
    441 {
    442     return _private->vScroll;
    443 }
    444 
    445 - (void)setHorizontalScrollingMode:(ScrollbarMode)horizontalMode andLock:(BOOL)lock
    446 {
    447     [self setScrollingModes:horizontalMode vertical:[self verticalScrollingMode] andLock:lock];
    448 }
    449 
    450 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode andLock:(BOOL)lock
    451 {
    452     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:lock];
    453 }
    454 
    455 // Mail uses this method, so we cannot remove it.
    456 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode
    457 {
    458     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:NO];
    459 }
    460 
    461 - (void)setScrollingModes:(ScrollbarMode)horizontalMode vertical:(ScrollbarMode)verticalMode andLock:(BOOL)lock
    462 {
    463     BOOL update = NO;
    464     if (verticalMode != _private->vScroll && !_private->vScrollModeLocked) {
    465         _private->vScroll = verticalMode;
    466         update = YES;
    467     }
    468 
    469     if (horizontalMode != _private->hScroll && !_private->hScrollModeLocked) {
    470         _private->hScroll = horizontalMode;
    471         update = YES;
    472     }
    473 
    474     if (lock)
    475         [self setScrollingModesLocked:YES];
    476 
    477     if (update)
    478         [self updateScrollers];
    479 }
    480 
    481 - (void)setHorizontalScrollingModeLocked:(BOOL)locked
    482 {
    483     _private->hScrollModeLocked = locked;
    484 }
    485 
    486 - (void)setVerticalScrollingModeLocked:(BOOL)locked
    487 {
    488     _private->vScrollModeLocked = locked;
    489 }
    490 
    491 - (void)setScrollingModesLocked:(BOOL)locked
    492 {
    493     _private->hScrollModeLocked = _private->vScrollModeLocked = locked;
    494 }
    495 
    496 - (BOOL)horizontalScrollingModeLocked
    497 {
    498     return _private->hScrollModeLocked;
    499 }
    500 
    501 - (BOOL)verticalScrollingModeLocked
    502 {
    503     return _private->vScrollModeLocked;
    504 }
    505 
    506 - (BOOL)autoforwardsScrollWheelEvents
    507 {
    508     return YES;
    509 }
    510 
    511 - (void)scrollWheel:(NSEvent *)event
    512 {
    513     float deltaX;
    514     float deltaY;
    515     BOOL isContinuous;
    516     WKGetWheelEventDeltas(event, &deltaX, &deltaY, &isContinuous);
    517 
    518 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
    519     NSEventPhase momentumPhase = [event momentumPhase];
    520     BOOL isLatchingEvent = momentumPhase & NSEventPhaseBegan || momentumPhase & NSEventPhaseChanged;
    521 #else
    522     int momentumPhase = WKGetNSEventMomentumPhase(event);
    523     BOOL isLatchingEvent = momentumPhase == WKEventPhaseBegan || momentumPhase == WKEventPhaseChanged;
    524 #endif
    525 
    526     if (fabsf(deltaY) > fabsf(deltaX)) {
    527         if (![self allowsVerticalScrolling]) {
    528             [[self nextResponder] scrollWheel:event];
    529             return;
    530         }
    531 
    532         if (isLatchingEvent && !_private->verticallyPinnedByPreviousWheelEvent) {
    533             double verticalPosition = [[self verticalScroller] doubleValue];
    534             if ((deltaY >= 0.0 && verticalPosition == 0.0) || (deltaY <= 0.0 && verticalPosition == 1.0))
    535                 return;
    536         }
    537     } else {
    538         if (![self allowsHorizontalScrolling]) {
    539             [[self nextResponder] scrollWheel:event];
    540             return;
    541         }
    542 
    543         if (isLatchingEvent && !_private->horizontallyPinnedByPreviousWheelEvent) {
    544             double horizontalPosition = [[self horizontalScroller] doubleValue];
    545             if ((deltaX >= 0.0 && horizontalPosition == 0.0) || (deltaX <= 0.0 && horizontalPosition == 1.0))
    546                 return;
    547         }
    548     }
    549 
    550     // Calling super can release the last reference. <rdar://problem/7400263>
    551     // Hold a reference so the code following the super call will not crash.
    552     [self retain];
    553 
    554     [super scrollWheel:event];
    555 
    556     if (!isLatchingEvent) {
    557         double verticalPosition = [[self verticalScroller] doubleValue];
    558         double horizontalPosition = [[self horizontalScroller] doubleValue];
    559 
    560         _private->verticallyPinnedByPreviousWheelEvent = (verticalPosition == 0.0 || verticalPosition == 1.0);
    561         _private->horizontallyPinnedByPreviousWheelEvent = (horizontalPosition == 0.0 || horizontalPosition == 1.0);
    562     }
    563 
    564     [self release];
    565 }
    566 
    567 // This object will be the parent of the web area in WK1, so it should not be ignored.
    568 - (BOOL)accessibilityIsIgnored
    569 {
    570     return NO;
    571 }
    572 
    573 - (void)setScrollOrigin:(NSPoint)scrollOrigin updatePositionAtAll:(BOOL)updatePositionAtAll immediately:(BOOL)updatePositionSynchronously
    574 {
    575     // The cross-platform ScrollView call already checked to see if the old/new scroll origins were the same or not
    576     // so we don't have to check for equivalence here.
    577     _private->scrollOrigin = scrollOrigin;
    578     id docView = [self documentView];
    579 
    580     NSRect visibleRect = [self documentVisibleRect];
    581 
    582     [docView setBoundsOrigin:NSMakePoint(-scrollOrigin.x, -scrollOrigin.y)];
    583 
    584     if (updatePositionAtAll)
    585         _private->scrollOriginChanged = true;
    586 
    587     // Maintain our original position in the presence of the new scroll origin.
    588     _private->scrollPositionExcludingOrigin = NSMakePoint(visibleRect.origin.x + scrollOrigin.x, visibleRect.origin.y + scrollOrigin.y);
    589 
    590     if (updatePositionAtAll && updatePositionSynchronously) // Otherwise we'll just let the snap happen when we update for the resize.
    591         [self adjustForScrollOriginChange];
    592 }
    593 
    594 - (NSPoint)scrollOrigin
    595 {
    596     return _private->scrollOrigin;
    597 }
    598 
    599 - (BOOL)inProgrammaticScroll
    600 {
    601     return _private->inProgrammaticScroll;
    602 }
    603 
    604 @end
    605