https://bugs.webkit.org/show_bug.cgi?id=46422, make printing and pagination work
[WebKit-https.git] / Source / WebKit / mac / WebView / WebDynamicScrollBarsView.mm
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 - (BOOL)inProgramaticScroll
164 {
165     return _private->inProgrammaticScroll;
166 }
167
168 @end
169
170 @implementation WebDynamicScrollBarsView (WebInternal)
171
172 - (NSRect)contentViewFrame
173 {
174     NSRect frame = [[self contentView] frame];
175
176     if ([self hasHorizontalScroller])
177         frame.size.height = (_private->allowsScrollersToOverlapContent ? NSMaxY([[self horizontalScroller] frame]) : NSMinY([[self horizontalScroller] frame]));
178     if ([self hasVerticalScroller])
179         frame.size.width = (_private->allowsScrollersToOverlapContent ? NSMaxX([[self verticalScroller] frame]) : NSMinX([[self verticalScroller] frame]));
180     return frame;
181 }
182
183 - (void)tile
184 {
185     [super tile];
186
187     // [super tile] sets the contentView size so that it does not overlap with the scrollers,
188     // we want to re-set the contentView to overlap scrollers before displaying.
189     if (_private->allowsScrollersToOverlapContent)
190         [[self contentView] setFrame:[self contentViewFrame]];
191 }
192
193 - (void)setSuppressLayout:(BOOL)flag
194 {
195     _private->suppressLayout = flag;
196 }
197
198 - (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint
199 {
200     _private->suppressScrollers = suppressed;
201
202     // This code was originally changes for a Leopard performance imporvement. We decided to 
203     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
204 #ifndef BUILDING_ON_TIGER
205     if (suppressed) {
206         [[self verticalScroller] setNeedsDisplay:NO];
207         [[self horizontalScroller] setNeedsDisplay:NO];
208     }
209
210     if (!suppressed && repaint)
211         [super reflectScrolledClipView:[self contentView]];
212 #else
213     if (suppressed || repaint) {
214         [[self verticalScroller] setNeedsDisplay:!suppressed];
215         [[self horizontalScroller] setNeedsDisplay:!suppressed];
216     }
217 #endif
218 }
219
220 - (void)adjustForScrollOriginChange
221 {
222     if (!_private->scrollOriginChanged)
223         return;
224
225     _private->scrollOriginChanged = false;
226
227     NSView *documentView = [self documentView];
228     NSRect documentRect = [documentView bounds];
229
230     // The call to [NSView scrollPoint:] fires off notification the handler for which needs to know that
231     // we're setting the initial scroll position so it doesn't interpret this as a user action and
232     // fire off a JS event.
233     _private->inProgrammaticScroll = true;
234     [documentView scrollPoint:NSMakePoint(_private->scrollPositionExcludingOrigin.x + documentRect.origin.x, _private->scrollPositionExcludingOrigin.y + documentRect.origin.y)];
235     _private->inProgrammaticScroll = false;
236 }
237
238 static const unsigned cMaxUpdateScrollbarsPass = 2;
239
240 - (void)updateScrollers
241 {
242     NSView *documentView = [self documentView];
243
244     // If we came in here with the view already needing a layout, then go ahead and do that
245     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
246     // This layout will not re-enter updateScrollers and does not count towards our max layout pass total.
247     if (!_private->suppressLayout && !_private->suppressScrollers && [documentView isKindOfClass:[WebHTMLView class]]) {
248         WebHTMLView* htmlView = (WebHTMLView*)documentView;
249         if ([htmlView _needsLayout]) {
250             _private->inUpdateScrollers = YES;
251             [(id <WebDocumentView>)documentView layout];
252             _private->inUpdateScrollers = NO;
253         }
254     }
255
256     BOOL hasHorizontalScroller = [self hasHorizontalScroller];
257     BOOL hasVerticalScroller = [self hasVerticalScroller];
258
259     BOOL newHasHorizontalScroller = hasHorizontalScroller;
260     BOOL newHasVerticalScroller = hasVerticalScroller;
261
262     if (!documentView) {
263         newHasHorizontalScroller = NO;
264         newHasVerticalScroller = NO;
265     }
266
267     if (_private->hScroll != ScrollbarAuto)
268         newHasHorizontalScroller = (_private->hScroll == ScrollbarAlwaysOn);
269     if (_private->vScroll != ScrollbarAuto)
270         newHasVerticalScroller = (_private->vScroll == ScrollbarAlwaysOn);
271
272     if (!documentView || _private->suppressLayout || _private->suppressScrollers || (_private->hScroll != ScrollbarAuto && _private->vScroll != ScrollbarAuto)) {
273         _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller;
274         if (_private->horizontalScrollingAllowedButScrollerHidden)
275             newHasHorizontalScroller = NO;
276
277         _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller;
278         if (_private->verticalScrollingAllowedButScrollerHidden)
279             newHasVerticalScroller = NO;
280
281         _private->inUpdateScrollers = YES;
282         if (hasHorizontalScroller != newHasHorizontalScroller)
283             [self setHasHorizontalScroller:newHasHorizontalScroller];
284         if (hasVerticalScroller != newHasVerticalScroller)
285             [self setHasVerticalScroller:newHasVerticalScroller];
286         if (_private->suppressScrollers) {
287             [[self verticalScroller] setNeedsDisplay:NO];
288             [[self horizontalScroller] setNeedsDisplay:NO];
289         }
290         _private->inUpdateScrollers = NO;
291         return;
292     }
293
294     BOOL needsLayout = NO;
295
296     NSSize documentSize = [documentView frame].size;
297     NSSize visibleSize = [self documentVisibleRect].size;
298     NSSize frameSize = [self frame].size;
299     
300     // When in HiDPI with a scale factor > 1, the visibleSize and frameSize may be non-integral values,
301     // while the documentSize (set by WebCore) will be integral.  Round up the non-integral sizes so that
302     // the mismatch won't cause unwanted scrollbars to appear.  This can result in slightly cut off content,
303     // but it will always be less than one pixel, which should not be noticeable.
304     visibleSize.width = ceilf(visibleSize.width);
305     visibleSize.height = ceilf(visibleSize.height);
306     frameSize.width = ceilf(frameSize.width);
307     frameSize.height = ceilf(frameSize.height);
308
309     if (_private->hScroll == ScrollbarAuto) {
310         newHasHorizontalScroller = documentSize.width > visibleSize.width;
311         if (newHasHorizontalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
312             newHasHorizontalScroller = NO;
313     }
314
315     if (_private->vScroll == ScrollbarAuto) {
316         newHasVerticalScroller = documentSize.height > visibleSize.height;
317         if (newHasVerticalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width)
318             newHasVerticalScroller = NO;
319     }
320
321     // Unless in ScrollbarsAlwaysOn mode, if we ever turn one scrollbar off, always turn the other one off too.
322     // Never ever try to both gain/lose a scrollbar in the same pass.
323     if (!newHasHorizontalScroller && hasHorizontalScroller && _private->vScroll != ScrollbarAlwaysOn)
324         newHasVerticalScroller = NO;
325     if (!newHasVerticalScroller && hasVerticalScroller && _private->hScroll != ScrollbarAlwaysOn)
326         newHasHorizontalScroller = NO;
327
328     _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller;
329     if (_private->horizontalScrollingAllowedButScrollerHidden)
330         newHasHorizontalScroller = NO;
331
332     _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller;
333     if (_private->verticalScrollingAllowedButScrollerHidden)
334         newHasVerticalScroller = NO;
335
336     if (hasHorizontalScroller != newHasHorizontalScroller) {
337         _private->inUpdateScrollers = YES;
338         [self setHasHorizontalScroller:newHasHorizontalScroller];
339         _private->inUpdateScrollers = NO;
340         needsLayout = YES;
341         NSView *documentView = [self documentView];
342         NSRect documentRect = [documentView bounds];
343         if (documentRect.origin.y < 0 && !newHasHorizontalScroller)
344             [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x, documentRect.origin.y + 15)];
345     }
346
347     if (hasVerticalScroller != newHasVerticalScroller) {
348         _private->inUpdateScrollers = YES;
349         [self setHasVerticalScroller:newHasVerticalScroller];
350         _private->inUpdateScrollers = NO;
351         needsLayout = YES;
352         NSView *documentView = [self documentView];
353         NSRect documentRect = [documentView bounds];
354         if (documentRect.origin.x < 0 && !newHasVerticalScroller)
355             [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x + 15, documentRect.origin.y)];
356     }
357
358     if (needsLayout && _private->inUpdateScrollersLayoutPass < cMaxUpdateScrollbarsPass &&
359         [documentView conformsToProtocol:@protocol(WebDocumentView)]) {
360         _private->inUpdateScrollersLayoutPass++;
361         [(id <WebDocumentView>)documentView setNeedsLayout:YES];
362         [(id <WebDocumentView>)documentView layout];
363         NSSize newDocumentSize = [documentView frame].size;
364         if (NSEqualSizes(documentSize, newDocumentSize)) {
365             // The layout with the new scroll state had no impact on
366             // the document's overall size, so updateScrollers didn't get called.
367             // Recur manually.
368             [self updateScrollers];
369         }
370         _private->inUpdateScrollersLayoutPass--;
371     }
372 }
373
374 // Make the horizontal and vertical scroll bars come and go as needed.
375 - (void)reflectScrolledClipView:(NSClipView *)clipView
376 {
377     if (clipView == [self contentView]) {
378         // Prevent appearance of trails because of overlapping views
379         if (_private->allowsScrollersToOverlapContent)
380             [self setDrawsBackground:NO];
381
382         // FIXME: This hack here prevents infinite recursion that takes place when we
383         // gyrate between having a vertical scroller and not having one. A reproducible
384         // case is clicking on the "the Policy Routing text" link at
385         // http://www.linuxpowered.com/archive/howto/Net-HOWTO-8.html.
386         // The underlying cause is some problem in the NSText machinery, but I was not
387         // able to pin it down.
388         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
389         if (!_private->inUpdateScrollers && (!currentContext || [currentContext isDrawingToScreen]))
390             [self updateScrollers];
391     }
392
393     // This code was originally changed for a Leopard performance imporvement. We decided to 
394     // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
395 #ifndef BUILDING_ON_TIGER
396     // Update the scrollers if they're not being suppressed.
397     if (!_private->suppressScrollers)
398         [super reflectScrolledClipView:clipView];
399 #else
400     [super reflectScrolledClipView:clipView];
401
402     // Validate the scrollers if they're being suppressed.
403     if (_private->suppressScrollers) {
404         [[self verticalScroller] setNeedsDisplay:NO];
405         [[self horizontalScroller] setNeedsDisplay:NO];
406     }
407 #endif
408
409     // The call to [NSView reflectScrolledClipView] sets the scrollbar thumb
410     // position to 0 (the left) when the view is initially displayed.
411     // This call updates the initial position correctly.
412     [self adjustForScrollOriginChange];
413
414 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
415     NSView *documentView = [self documentView];
416     if ([documentView isKindOfClass:[WebHTMLView class]]) {
417         WebHTMLView *htmlView = (WebHTMLView *)documentView;
418         if ([htmlView _isUsingAcceleratedCompositing])
419             [htmlView _updateLayerHostingViewPosition];
420     }
421 #endif
422 }
423
424 - (BOOL)allowsHorizontalScrolling
425 {
426     return _private->hScroll != ScrollbarAlwaysOff;
427 }
428
429 - (BOOL)allowsVerticalScrolling
430 {
431     return _private->vScroll != ScrollbarAlwaysOff;
432 }
433
434 - (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode
435 {
436     *hMode = _private->hScroll;
437     *vMode = _private->vScroll;
438 }
439
440 - (ScrollbarMode)horizontalScrollingMode
441 {
442     return _private->hScroll;
443 }
444
445 - (ScrollbarMode)verticalScrollingMode
446 {
447     return _private->vScroll;
448 }
449
450 - (void)setHorizontalScrollingMode:(ScrollbarMode)horizontalMode andLock:(BOOL)lock
451 {
452     [self setScrollingModes:horizontalMode vertical:[self verticalScrollingMode] andLock:lock];
453 }
454
455 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode andLock:(BOOL)lock
456 {
457     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:lock];
458 }
459
460 // Mail uses this method, so we cannot remove it. 
461 - (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode 
462
463     [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:NO]; 
464
465
466 - (void)setScrollingModes:(ScrollbarMode)horizontalMode vertical:(ScrollbarMode)verticalMode andLock:(BOOL)lock
467 {
468     BOOL update = NO;
469     if (verticalMode != _private->vScroll && !_private->vScrollModeLocked) {
470         _private->vScroll = verticalMode;
471         update = YES;
472     }
473
474     if (horizontalMode != _private->hScroll && !_private->hScrollModeLocked) {
475         _private->hScroll = horizontalMode;
476         update = YES;
477     }
478
479     if (lock)
480         [self setScrollingModesLocked:YES];
481
482     if (update)
483         [self updateScrollers];
484 }
485
486 - (void)setHorizontalScrollingModeLocked:(BOOL)locked
487 {
488     _private->hScrollModeLocked = locked;
489 }
490
491 - (void)setVerticalScrollingModeLocked:(BOOL)locked
492 {
493     _private->vScrollModeLocked = locked;
494 }
495
496 - (void)setScrollingModesLocked:(BOOL)locked
497 {
498     _private->hScrollModeLocked = _private->vScrollModeLocked = locked;
499 }
500
501 - (BOOL)horizontalScrollingModeLocked
502 {
503     return _private->hScrollModeLocked;
504 }
505
506 - (BOOL)verticalScrollingModeLocked
507 {
508     return _private->vScrollModeLocked;
509 }
510
511 - (BOOL)autoforwardsScrollWheelEvents
512 {
513     return YES;
514 }
515
516 - (void)scrollWheel:(NSEvent *)event
517 {
518     float deltaX;
519     float deltaY;
520     BOOL isContinuous;
521     WKGetWheelEventDeltas(event, &deltaX, &deltaY, &isContinuous);
522
523     BOOL isLatchingEvent = WKIsLatchingWheelEvent(event);
524
525     if (fabsf(deltaY) > fabsf(deltaX)) {
526         if (![self allowsVerticalScrolling]) {
527             [[self nextResponder] scrollWheel:event];
528             return;
529         }
530
531         if (isLatchingEvent && !_private->verticallyPinnedByPreviousWheelEvent) {
532             double verticalPosition = [[self verticalScroller] doubleValue];
533             if ((deltaY >= 0.0 && verticalPosition == 0.0) || (deltaY <= 0.0 && verticalPosition == 1.0))
534                 return;
535         }
536     } else {
537         if (![self allowsHorizontalScrolling]) {
538             [[self nextResponder] scrollWheel:event];
539             return;
540         }
541
542         if (isLatchingEvent && !_private->horizontallyPinnedByPreviousWheelEvent) {
543             double horizontalPosition = [[self horizontalScroller] doubleValue];
544             if ((deltaX >= 0.0 && horizontalPosition == 0.0) || (deltaX <= 0.0 && horizontalPosition == 1.0))
545                 return;
546         }
547     }
548
549     // Calling super can release the last reference. <rdar://problem/7400263>
550     // Hold a reference so the code following the super call will not crash.
551     [self retain];
552
553     [super scrollWheel:event];
554
555     if (!isLatchingEvent) {
556         double verticalPosition = [[self verticalScroller] doubleValue];
557         double horizontalPosition = [[self horizontalScroller] doubleValue];
558
559         _private->verticallyPinnedByPreviousWheelEvent = (verticalPosition == 0.0 || verticalPosition == 1.0);
560         _private->horizontallyPinnedByPreviousWheelEvent = (horizontalPosition == 0.0 || horizontalPosition == 1.0);
561     }
562
563     [self release];
564 }
565
566 - (BOOL)accessibilityIsIgnored 
567 {
568     return YES;
569 }
570
571 - (void)setScrollOrigin:(NSPoint)scrollOrigin updatePositionAtAll:(BOOL)updatePositionAtAll immediately:(BOOL)updatePositionSynchronously
572 {
573     // The cross-platform ScrollView call already checked to see if the old/new scroll origins were the same or not
574     // so we don't have to check for equivalence here.
575     _private->scrollOrigin = scrollOrigin;
576     id docView = [self documentView];
577
578     NSRect visibleRect = [self documentVisibleRect];
579
580     [docView setBoundsOrigin:NSMakePoint(-scrollOrigin.x, -scrollOrigin.y)];
581
582     if (updatePositionAtAll)
583         _private->scrollOriginChanged = true;
584
585     // Maintain our original position in the presence of the new scroll origin.
586     _private->scrollPositionExcludingOrigin = NSMakePoint(visibleRect.origin.x + scrollOrigin.x, visibleRect.origin.y + scrollOrigin.y);
587
588     if (updatePositionAtAll && updatePositionSynchronously) // Otherwise we'll just let the snap happen when we update for the resize.
589         [self adjustForScrollOriginChange];
590 }
591
592 - (NSPoint)scrollOrigin
593 {
594     return _private->scrollOrigin;
595 }
596
597 @end