• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2005, 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 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#import "WebDynamicScrollBarsViewInternal.h"
30
31#import "WebDocument.h"
32#import "WebFrameInternal.h"
33#import "WebFrameView.h"
34#import "WebHTMLViewInternal.h"
35#import <WebCore/Frame.h>
36#import <WebCore/FrameView.h>
37#import <WebKitSystemInterface.h>
38
39using namespace WebCore;
40
41// FIXME: <rdar://problem/5898985> Mail expects a constant of this name to exist.
42const int WebCoreScrollbarAlwaysOn = ScrollbarAlwaysOn;
43
44@implementation WebDynamicScrollBarsView
45
46- (void)setAllowsHorizontalScrolling:(BOOL)flag
47{
48    if (hScrollModeLocked)
49        return;
50    if (flag && hScroll == ScrollbarAlwaysOff)
51        hScroll = ScrollbarAuto;
52    else if (!flag && hScroll != ScrollbarAlwaysOff)
53        hScroll = ScrollbarAlwaysOff;
54    [self updateScrollers];
55}
56
57@end
58
59@implementation WebDynamicScrollBarsView (WebInternal)
60
61- (void)setSuppressLayout:(BOOL)flag;
62{
63    suppressLayout = flag;
64}
65
66- (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint
67{
68    suppressScrollers = suppressed;
69
70    // This code was originally changes for a Leopard performance imporvement. We decided to
71    // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
72#ifndef BUILDING_ON_TIGER
73    if (suppressed) {
74        [[self verticalScroller] setNeedsDisplay:NO];
75        [[self horizontalScroller] setNeedsDisplay:NO];
76    }
77
78    if (!suppressed && repaint)
79        [super reflectScrolledClipView:[self contentView]];
80#else
81    if (suppressed || repaint) {
82        [[self verticalScroller] setNeedsDisplay: !suppressed];
83        [[self horizontalScroller] setNeedsDisplay: !suppressed];
84    }
85#endif
86}
87
88static const unsigned cMaxUpdateScrollbarsPass = 2;
89
90- (void)updateScrollers
91{
92    BOOL hasVerticalScroller = [self hasVerticalScroller];
93    BOOL hasHorizontalScroller = [self hasHorizontalScroller];
94
95    BOOL newHasHorizontalScroller = hasHorizontalScroller;
96    BOOL newHasVerticalScroller = hasVerticalScroller;
97
98    BOOL needsLayout = NO;
99
100    NSView *documentView = [self documentView];
101    if (!documentView) {
102        newHasHorizontalScroller = NO;
103        newHasVerticalScroller = NO;
104    }
105
106    if (hScroll != ScrollbarAuto)
107        newHasHorizontalScroller = (hScroll == ScrollbarAlwaysOn);
108    if (vScroll != ScrollbarAuto)
109        newHasVerticalScroller = (vScroll == ScrollbarAlwaysOn);
110
111    if (!documentView || suppressLayout || suppressScrollers || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
112        inUpdateScrollers = YES;
113        if (hasHorizontalScroller != newHasHorizontalScroller)
114            [self setHasHorizontalScroller:newHasHorizontalScroller];
115        if (hasVerticalScroller != newHasVerticalScroller)
116            [self setHasVerticalScroller:newHasVerticalScroller];
117        if (suppressScrollers) {
118            [[self verticalScroller] setNeedsDisplay:NO];
119            [[self horizontalScroller] setNeedsDisplay:NO];
120        }
121        inUpdateScrollers = NO;
122        return;
123    }
124
125    needsLayout = NO;
126
127    // If we came in here with the view already needing a layout, then go ahead and do that
128    // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
129    // This layout will not re-enter updateScrollers and does not count towards our max layout pass total.
130    if ([documentView isKindOfClass:[WebHTMLView class]]) {
131        WebHTMLView* htmlView = (WebHTMLView*)documentView;
132        if ([htmlView _needsLayout]) {
133            inUpdateScrollers = YES;
134            [(id <WebDocumentView>)documentView layout];
135            inUpdateScrollers = NO;
136        }
137    }
138
139    NSSize documentSize = [documentView frame].size;
140    NSSize visibleSize = [self documentVisibleRect].size;
141    NSSize frameSize = [self frame].size;
142
143    if (hScroll == ScrollbarAuto) {
144        newHasHorizontalScroller = documentSize.width > visibleSize.width;
145        if (newHasHorizontalScroller && !inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
146            newHasHorizontalScroller = NO;
147    }
148
149    if (vScroll == ScrollbarAuto) {
150        newHasVerticalScroller = documentSize.height > visibleSize.height;
151        if (newHasVerticalScroller && !inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
152            newHasVerticalScroller = NO;
153    }
154
155    // Unless in ScrollbarsAlwaysOn mode, if we ever turn one scrollbar off, always turn the other one off too.
156    // Never ever try to both gain/lose a scrollbar in the same pass.
157    if (!newHasHorizontalScroller && hasHorizontalScroller && vScroll != ScrollbarAlwaysOn)
158        newHasVerticalScroller = NO;
159    if (!newHasVerticalScroller && hasVerticalScroller && hScroll != ScrollbarAlwaysOn)
160        newHasHorizontalScroller = NO;
161
162    if (hasHorizontalScroller != newHasHorizontalScroller) {
163        inUpdateScrollers = YES;
164        [self setHasHorizontalScroller:newHasHorizontalScroller];
165        inUpdateScrollers = NO;
166        needsLayout = YES;
167    }
168
169    if (hasVerticalScroller != newHasVerticalScroller) {
170        inUpdateScrollers = YES;
171        [self setHasVerticalScroller:newHasVerticalScroller];
172        inUpdateScrollers = NO;
173        needsLayout = YES;
174    }
175
176    if (needsLayout && inUpdateScrollersLayoutPass < cMaxUpdateScrollbarsPass &&
177        [documentView conformsToProtocol:@protocol(WebDocumentView)]) {
178        inUpdateScrollersLayoutPass++;
179        [(id <WebDocumentView>)documentView setNeedsLayout:YES];
180        [(id <WebDocumentView>)documentView layout];
181        NSSize newDocumentSize = [documentView frame].size;
182        if (NSEqualSizes(documentSize, newDocumentSize)) {
183            // The layout with the new scroll state had no impact on
184            // the document's overall size, so updateScrollers didn't get called.
185            // Recur manually.
186            [self updateScrollers];
187        }
188        inUpdateScrollersLayoutPass--;
189    }
190}
191
192// Make the horizontal and vertical scroll bars come and go as needed.
193- (void)reflectScrolledClipView:(NSClipView *)clipView
194{
195    if (clipView == [self contentView]) {
196        // FIXME: This hack here prevents infinite recursion that takes place when we
197        // gyrate between having a vertical scroller and not having one. A reproducible
198        // case is clicking on the "the Policy Routing text" link at
199        // http://www.linuxpowered.com/archive/howto/Net-HOWTO-8.html.
200        // The underlying cause is some problem in the NSText machinery, but I was not
201        // able to pin it down.
202        if (!inUpdateScrollers && [[NSGraphicsContext currentContext] isDrawingToScreen])
203            [self updateScrollers];
204    }
205
206    // This code was originally changed for a Leopard performance imporvement. We decided to
207    // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
208#ifndef BUILDING_ON_TIGER
209    // Update the scrollers if they're not being suppressed.
210    if (!suppressScrollers)
211        [super reflectScrolledClipView:clipView];
212#else
213    [super reflectScrolledClipView:clipView];
214
215    // Validate the scrollers if they're being suppressed.
216    if (suppressScrollers) {
217        [[self verticalScroller] setNeedsDisplay: NO];
218        [[self horizontalScroller] setNeedsDisplay: NO];
219    }
220#endif
221
222#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
223    NSView *documentView = [self documentView];
224    if ([documentView isKindOfClass:[WebHTMLView class]]) {
225        WebHTMLView *htmlView = (WebHTMLView *)documentView;
226        if ([htmlView _isUsingAcceleratedCompositing])
227            [htmlView _updateLayerHostingViewPosition];
228    }
229#endif
230}
231
232- (BOOL)allowsHorizontalScrolling
233{
234    return hScroll != ScrollbarAlwaysOff;
235}
236
237- (BOOL)allowsVerticalScrolling
238{
239    return vScroll != ScrollbarAlwaysOff;
240}
241
242- (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode
243{
244    *hMode = static_cast<ScrollbarMode>(hScroll);
245    *vMode = static_cast<ScrollbarMode>(vScroll);
246}
247
248- (ScrollbarMode)horizontalScrollingMode
249{
250    return static_cast<ScrollbarMode>(hScroll);
251}
252
253- (ScrollbarMode)verticalScrollingMode
254{
255    return static_cast<ScrollbarMode>(vScroll);
256}
257
258- (void)setHorizontalScrollingMode:(ScrollbarMode)horizontalMode andLock:(BOOL)lock
259{
260    [self setScrollingModes:horizontalMode vertical:[self verticalScrollingMode] andLock:lock];
261}
262
263- (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode andLock:(BOOL)lock
264{
265    [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:lock];
266}
267
268// Mail uses this method, so we cannot remove it.
269- (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode
270{
271    [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:NO];
272}
273
274- (void)setScrollingModes:(ScrollbarMode)horizontalMode vertical:(ScrollbarMode)verticalMode andLock:(BOOL)lock
275{
276    BOOL update = NO;
277    if (verticalMode != vScroll && !vScrollModeLocked) {
278        vScroll = verticalMode;
279        update = YES;
280    }
281
282    if (horizontalMode != hScroll && !hScrollModeLocked) {
283        hScroll = horizontalMode;
284        update = YES;
285    }
286
287    if (lock)
288        [self setScrollingModesLocked:YES];
289
290    if (update)
291        [self updateScrollers];
292}
293
294- (void)setHorizontalScrollingModeLocked:(BOOL)locked
295{
296    hScrollModeLocked = locked;
297}
298
299- (void)setVerticalScrollingModeLocked:(BOOL)locked
300{
301    vScrollModeLocked = locked;
302}
303
304- (void)setScrollingModesLocked:(BOOL)locked
305{
306    hScrollModeLocked = vScrollModeLocked = locked;
307}
308
309- (BOOL)horizontalScrollingModeLocked
310{
311    return hScrollModeLocked;
312}
313
314- (BOOL)verticalScrollingModeLocked
315{
316    return vScrollModeLocked;
317}
318
319- (BOOL)autoforwardsScrollWheelEvents
320{
321    return YES;
322}
323
324- (void)scrollWheel:(NSEvent *)event
325{
326    float deltaX;
327    float deltaY;
328    BOOL isContinuous;
329    WKGetWheelEventDeltas(event, &deltaX, &deltaY, &isContinuous);
330
331    if (fabsf(deltaY) > fabsf(deltaX)) {
332        if (![self allowsVerticalScrolling]) {
333            [[self nextResponder] scrollWheel:event];
334            return;
335        }
336    } else if (![self allowsHorizontalScrolling]) {
337        [[self nextResponder] scrollWheel:event];
338        return;
339    }
340
341    [super scrollWheel:event];
342}
343
344- (BOOL)accessibilityIsIgnored
345{
346    id docView = [self documentView];
347    if ([docView isKindOfClass:[WebFrameView class]] && ![(WebFrameView *)docView allowsScrolling])
348        return YES;
349
350    return [super accessibilityIsIgnored];
351}
352
353@end
354