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