56266cfdffb10daa859bead251c96ee300e6146a
[WebKit-https.git] / Source / WebKitLegacy / mac / WebView / WebFrameView.mm
1 /*
2  * Copyright (C) 2005, 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  *
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 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 "WebFrameView.h"
30
31 #import "BackForwardList.h"
32 #import "WebClipView.h"
33 #import "WebDataSourcePrivate.h"
34 #import "WebDocument.h"
35 #import "WebDynamicScrollBarsViewInternal.h"
36 #import "WebFrame.h"
37 #import "WebFrameInternal.h"
38 #import "WebFrameViewInternal.h"
39 #import "WebFrameViewPrivate.h"
40 #import "WebHistoryItemInternal.h"
41 #import "WebHTMLViewPrivate.h"
42 #import "WebKitErrorsPrivate.h"
43 #import "WebKitStatisticsPrivate.h"
44 #import "WebKitVersionChecks.h"
45 #import "WebNSDictionaryExtras.h"
46 #import "WebNSObjectExtras.h"
47 #import "WebNSViewExtras.h"
48 #import "WebPDFView.h"
49 #import "WebPreferenceKeysPrivate.h"
50 #import "WebResourceInternal.h"
51 #import "WebViewInternal.h"
52 #import "WebViewPrivate.h"
53 #import <Foundation/NSURLRequest.h>
54 #import <WebCore/BackForwardController.h>
55 #import <WebCore/DragController.h>
56 #import <WebCore/EventHandler.h>
57 #import <WebCore/Frame.h>
58 #import <WebCore/FrameView.h>
59 #import <WebCore/HistoryItem.h>
60 #import <WebCore/Page.h>
61 #import <WebCore/RenderView.h>
62 #import <WebCore/RenderWidget.h>
63 #import <WebCore/ThreadCheck.h>
64 #import <WebCore/WebCoreFrameView.h>
65 #import <WebCore/WebCoreView.h>
66 #import <wtf/Assertions.h>
67
68 #if PLATFORM(IOS_FAMILY)
69 #import "WebFrameInternal.h"
70 #import "WebPDFViewIOS.h"
71 #import "WebUIKitDelegate.h"
72 #import <Foundation/NSURLRequest.h>
73 #import <WebCore/GraphicsContext.h>
74 #import <WebCore/KeyEventCodesIOS.h>
75 #import <WebCore/WAKClipView.h>
76 #import <WebCore/WAKScrollView.h>
77 #import <WebCore/WAKWindow.h>
78 #import <WebCore/WKGraphics.h>
79 #import <WebCore/WebEvent.h>
80 #else
81 #import "WebNSPasteboardExtras.h"
82 #import "WebNSWindowExtras.h"
83 #endif
84
85 using namespace WebCore;
86
87 #if !PLATFORM(IOS_FAMILY)
88 @interface NSWindow (WindowPrivate)
89 - (BOOL)_needsToResetDragMargins;
90 - (void)_setNeedsToResetDragMargins:(BOOL)s;
91 @end
92 #endif
93
94 @interface NSClipView (AppKitSecretsIKnow)
95 - (BOOL)_scrollTo:(const NSPoint *)newOrigin animate:(BOOL)animate; // need the boolean result from this method
96 @end
97
98 #if PLATFORM(MAC)
99 @interface NSView ()
100 - (void)setBackgroundColor:(NSColor *)color;
101 @end
102 #endif
103
104 enum {
105     SpaceKey = 0x0020
106 };
107
108 @interface WebFrameView (WebFrameViewFileInternal) <WebCoreFrameView>
109 - (float)_verticalKeyboardScrollDistance;
110 @end
111
112 @interface WebFrameViewPrivate : NSObject {
113 @public
114     WebFrame *webFrame;
115     WebDynamicScrollBarsView *frameScrollView;
116     BOOL includedInWebKitStatistics;
117 }
118 @end
119
120 @implementation WebFrameViewPrivate
121
122 - (void)dealloc
123 {
124     [frameScrollView release];
125     [super dealloc];
126 }
127
128 @end
129
130 @implementation WebFrameView (WebFrameViewFileInternal)
131
132 - (float)_verticalKeyboardScrollDistance
133 {
134     // Arrow keys scroll the same distance that clicking the scroll arrow does.
135     return [[self _scrollView] verticalLineScroll];
136 }
137
138 - (Frame*)_web_frame
139 {
140     return core(_private->webFrame);
141 }
142
143 @end
144
145 @implementation WebFrameView (WebInternal)
146
147 // Note that the WebVew is not retained.
148 - (WebView *)_webView
149 {
150     return [_private->webFrame webView];
151 }
152
153 - (void)_setDocumentView:(NSView <WebDocumentView> *)view
154 {
155     WebDynamicScrollBarsView *sv = [self _scrollView];
156     
157 #if ENABLE(DRAG_SUPPORT)
158     core([self _webView])->dragController().setDidInitiateDrag(false);
159 #endif
160     
161 #if !PLATFORM(IOS_FAMILY)
162     [sv setSuppressLayout:YES];
163     
164     // If the old view is the first responder, transfer first responder status to the new view as 
165     // a convenience and so that we don't leave the window pointing to a view that's no longer in it.
166     NSWindow *window = [sv window];
167     NSResponder *firstResponder = [window firstResponder];
168     bool makeNewViewFirstResponder = [firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:[sv documentView]];
169
170     // Suppress the resetting of drag margins since we know we can't affect them.
171     BOOL resetDragMargins = [window _needsToResetDragMargins];
172     [window _setNeedsToResetDragMargins:NO];
173 #endif
174     [sv setDocumentView:view];
175 #if !PLATFORM(IOS_FAMILY)
176     [window _setNeedsToResetDragMargins:resetDragMargins];
177
178     if (makeNewViewFirstResponder)
179         [window makeFirstResponder:view];
180     [sv setSuppressLayout:NO];
181 #else
182     ASSERT(_private->webFrame);
183
184     Frame* frame = core(_private->webFrame);
185
186     ASSERT(frame);
187     ASSERT(frame->page());
188
189     if (frame == &frame->page()->mainFrame())
190         [[self window] makeFirstResponder:[self documentView]];
191 #endif
192 }
193
194 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
195 {
196     NSString* MIMEType = [dataSource _responseMIMEType];
197     if (!MIMEType)
198         MIMEType = @"text/html";
199     Class viewClass = [self _viewClassForMIMEType:MIMEType];
200     NSView <WebDocumentView> *documentView;
201     if (viewClass) {
202         // If the dataSource's representation has already been created, and it is also the
203         // same class as the desired documentView, then use it as the documentView instead
204         // of creating another one (Radar 4340787).
205         id <WebDocumentRepresentation> dataSourceRepresentation = [dataSource representation];
206         if (dataSourceRepresentation && [dataSourceRepresentation class] == viewClass)
207             documentView = (NSView <WebDocumentView> *)[dataSourceRepresentation retain];
208         else
209             documentView = [(NSView <WebDocumentView> *)[viewClass alloc] init];
210     } else
211         documentView = nil;
212     
213     [self _setDocumentView:documentView];
214     [documentView release];
215     
216     return documentView;
217 }
218
219 - (void)_setWebFrame:(WebFrame *)webFrame
220 {
221     if (!webFrame) {
222         NSView *docV = [self documentView];
223         if ([docV respondsToSelector:@selector(close)])
224             [docV performSelector:@selector(close)];
225     }
226
227     // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
228     _private->webFrame = webFrame;    
229
230     if (!_private->includedInWebKitStatistics && [webFrame _isIncludedInWebKitStatistics]) {
231         _private->includedInWebKitStatistics = YES;
232         ++WebFrameViewCount;
233     }
234 }
235
236 - (WebDynamicScrollBarsView *)_scrollView
237 {
238     // This can be called by [super dealloc] when cleaning up the key view loop,
239     // after _private has been nilled out.
240     if (_private == nil)
241         return nil;
242     return _private->frameScrollView;
243 }
244
245 - (float)_verticalPageScrollDistance
246 {
247     float height = [[self _contentView] bounds].size.height;
248     return std::max<float>(height * Scrollbar::minFractionToStepWhenPaging(), height - Scrollbar::maxOverlapBetweenPages());
249 }
250
251 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes)
252 {
253     NSEnumerator *enumerator = [supportTypes objectEnumerator];
254     ASSERT(enumerator != nil);
255     NSString *mime = nil;
256     while ((mime = [enumerator nextObject]) != nil) {
257         // Don't clobber previously-registered classes.
258         if ([allTypes objectForKey:mime] == nil)
259             [allTypes setObject:objCClass forKey:mime];
260     }
261 }
262
263 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
264 {
265     static NSMutableDictionary *viewTypes = nil;
266     static BOOL addedImageTypes = NO;
267     
268     if (!viewTypes) {
269         viewTypes = [[NSMutableDictionary alloc] init];
270         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedNonImageMIMETypes]);
271         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedMediaMIMETypes]);
272
273         // Since this is a "secret default" we don't bother registering it.
274         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
275         if (!omitPDFSupport)
276 #if PLATFORM(IOS_FAMILY)
277 #define WebPDFView ([WebView _getPDFViewClass])
278 #endif
279             addTypesFromClass(viewTypes, [WebPDFView class], [WebPDFView supportedMIMETypes]);
280 #if PLATFORM(IOS_FAMILY)
281 #undef WebPDFView
282 #endif
283     }
284     
285     if (!addedImageTypes && !allowImageTypeOmission) {
286         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedImageMIMETypes]);
287         addedImageTypes = YES;
288     }
289     
290     return viewTypes;
291 }
292
293 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
294 {
295     return [[[self _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType] isSubclassOfClass:[WebHTMLView class]];
296 }
297
298 + (Class)_viewClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins
299 {
300     Class viewClass;
301     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType allowingPlugins:allowPlugins] ? viewClass : nil;
302 }
303
304 - (Class)_viewClassForMIMEType:(NSString *)MIMEType
305 {
306     Class retVal = [[self class] _viewClassForMIMEType:MIMEType allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]];
307
308 #if PLATFORM(IOS_FAMILY)   
309     if ([retVal respondsToSelector:@selector(_representationClassForWebFrame:)])
310         retVal = [retVal performSelector:@selector(_representationClassForWebFrame:) withObject:[self webFrame]];
311 #endif
312         
313     return retVal;
314 }
315
316 - (void)_install
317 {
318     ASSERT(_private->webFrame);
319     ASSERT(_private->frameScrollView);
320
321     Frame* frame = core(_private->webFrame);
322
323     ASSERT(frame);
324     ASSERT(frame->page());
325
326     // If this isn't the main frame, it must have an owner element set, or it
327     // won't ever get installed in the view hierarchy.
328     ASSERT(frame->isMainFrame() || frame->ownerElement());
329
330     FrameView* view = frame->view();
331
332     view->setPlatformWidget(_private->frameScrollView);
333
334     // FIXME: Frame tries to do this too. Is this code needed?
335     if (RenderWidget* owner = frame->ownerRenderer()) {
336         owner->setWidget(view);
337         // Now the RenderWidget owns the view, so we don't any more.
338     }
339
340     view->updateCanHaveScrollbars();
341 }
342
343 - (void)_frameSizeChanged
344 {
345     // See WebFrameLoaderClient::provisionalLoadStarted.
346     if ([[[self webFrame] webView] drawsBackground])
347         [[self _scrollView] setDrawsBackground:YES];
348     if (Frame* coreFrame = [self _web_frame]) {
349         if (FrameView* coreFrameView = coreFrame->view())
350             coreFrameView->availableContentSizeChanged(ScrollableArea::AvailableSizeChangeReason::AreaSizeChanged);
351     }
352 }
353
354 @end
355
356 @implementation WebFrameView
357
358 - (id)initWithFrame:(NSRect)frame
359 {
360     self = [super initWithFrame:frame];
361     if (!self)
362         return nil;
363  
364     static bool didFirstTimeInitialization;
365     if (!didFirstTimeInitialization) {
366         didFirstTimeInitialization = true;
367         
368         // Need to tell WebCore what function to call for the "History Item has Changed" notification.
369         // Note: We also do this in WebHistoryItem's init method.
370         WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
371
372 #if !PLATFORM(IOS_FAMILY)
373         if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_MAIN_THREAD_EXCEPTIONS))
374             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundOne);
375
376         bool throwExceptionsForRoundTwo = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_ROUND_TWO_MAIN_THREAD_EXCEPTIONS);
377         if (!throwExceptionsForRoundTwo)
378             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundTwo);
379
380         bool throwExceptionsForRoundThree = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_ROUND_THREE_MAIN_THREAD_EXCEPTIONS);
381         if (!throwExceptionsForRoundThree)
382             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundThree);
383 #endif
384     }
385
386     _private = [[WebFrameViewPrivate alloc] init];
387
388     WebDynamicScrollBarsView *scrollView = [[WebDynamicScrollBarsView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, frame.size.width, frame.size.height)];
389     _private->frameScrollView = scrollView;
390 #if PLATFORM(IOS_FAMILY)
391     [scrollView setDelegate:self];
392 #else
393     [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
394 #endif
395     [scrollView setDrawsBackground:NO];
396     [scrollView setHasVerticalScroller:NO];
397     [scrollView setHasHorizontalScroller:NO];
398     [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
399     [scrollView setLineScroll:Scrollbar::pixelsPerLineStep()];
400     [self addSubview:scrollView];
401
402     // Don't call our overridden version of setNextKeyView here; we need to make the standard NSView
403     // link between us and our subview so that previousKeyView and previousValidKeyView work as expected.
404     // This works together with our becomeFirstResponder and setNextKeyView overrides.
405     [super setNextKeyView:scrollView];
406     
407     return self;
408 }
409
410 - (void)dealloc 
411 {
412     if (_private && _private->includedInWebKitStatistics)
413         --WebFrameViewCount;
414     
415     [_private release];
416     _private = nil;
417     
418     [super dealloc];
419 }
420
421 #if PLATFORM(IOS_FAMILY)
422 - (BOOL)scrollView:(WAKScrollView *)scrollView shouldScrollToPoint:(CGPoint)point
423 {
424     WebView *webView = [self _webView];
425     return [[webView _UIKitDelegateForwarder] webView:webView shouldScrollToPoint:point forFrame:_private->webFrame];
426 }
427 #endif
428
429 - (WebFrame *)webFrame
430 {
431     // This method can be called beneath -[NSView dealloc] after _private has been cleared.
432     return _private ? _private->webFrame : nil;
433 }
434
435 - (void)setAllowsScrolling:(BOOL)flag
436 {
437     WebCore::Frame *frame = core([self webFrame]);
438     if (WebCore::FrameView *view = frame? frame->view() : 0)
439         view->setCanHaveScrollbars(flag);
440 }
441
442 - (BOOL)allowsScrolling
443 {
444     WebCore::Frame *frame = core([self webFrame]);
445     if (WebCore::FrameView *view = frame? frame->view() : 0)
446         return view->canHaveScrollbars();
447     return YES;
448 }
449
450 - (NSView <WebDocumentView> *)documentView
451 {
452     return [[self _scrollView] documentView];
453 }
454
455 - (BOOL)acceptsFirstResponder
456 {
457     // We always accept first responder; this matches OS X 10.2 WebKit
458     // behavior (see 3469791).
459     return YES;
460 }
461
462 - (BOOL)becomeFirstResponder
463 {
464     // This works together with setNextKeyView to splice the WebFrameView into
465     // the key loop similar to the way NSScrollView does this. Note that
466     // WebView has similar code.
467     
468     NSWindow *window = [self window];
469     if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
470         NSView *previousValidKeyView = [self previousValidKeyView];
471         // If we couldn't find a previous valid key view, ask the WebView. This handles frameset
472         // cases (one is mentioned in Radar bug 3748628). Note that previousValidKeyView should
473         // never be self but can be due to AppKit oddness (mentioned in Radar bug 3748628).
474         if (previousValidKeyView == nil || previousValidKeyView == self)
475             previousValidKeyView = [[[self webFrame] webView] previousValidKeyView];
476         [window makeFirstResponder:previousValidKeyView];
477     } else {
478         // If the scroll view won't accept first-responderness now, then just become
479         // the first responder ourself like a normal view. This lets us be the first 
480         // responder in cases where no page has yet been loaded.
481         if ([[self _scrollView] acceptsFirstResponder])
482             [window makeFirstResponder:[self _scrollView]];
483     }
484     
485     return YES;
486 }
487
488 - (void)setNextKeyView:(NSView *)aView
489 {
490     // This works together with becomeFirstResponder to splice the WebFrameView into
491     // the key loop similar to the way NSScrollView does this. Note that
492     // WebView has very similar code.
493     if ([self _scrollView] != nil) {
494         [[self _scrollView] setNextKeyView:aView];
495     } else {
496         [super setNextKeyView:aView];
497     }
498 }
499
500 - (BOOL)isOpaque
501 {
502     return [[self _webView] drawsBackground];
503 }
504
505 - (void)drawRect:(NSRect)rect
506 {
507 #if !PLATFORM(IOS_FAMILY)
508     if (![self documentView])
509 #else
510     if (![self documentView] || [[self documentView] frame].size.height == 0 || [[self webFrame] _isCommitting])
511 #endif
512     {
513         // Need to paint ourselves if there's no documentView to do it instead.
514         if ([[self _webView] drawsBackground]) {
515 #if !PLATFORM(IOS_FAMILY)
516             [[[self _webView] backgroundColor] set];
517             NSRectFill(rect);
518 #else
519             CGContextRef cgContext = WKGetCurrentGraphicsContext();
520             CGContextSetFillColorWithColor(cgContext, cachedCGColor(Color::white));
521             WKRectFill(cgContext, rect);
522 #endif
523         }
524     } else {
525 #ifndef NDEBUG
526         if ([[self _scrollView] drawsBackground]) {
527 #if !PLATFORM(IOS_FAMILY)
528             [[NSColor cyanColor] set];
529             NSRectFill(rect);
530 #else
531             CGContextRef cgContext = WKGetCurrentGraphicsContext();
532             CGContextSetFillColorWithColor(cgContext, cachedCGColor(Color::cyan));
533             WKRectFill(cgContext, rect);
534 #endif
535         }
536 #endif
537     }
538 }
539
540 #if PLATFORM(MAC)
541 - (BOOL)wantsUpdateLayer
542 {
543     return YES;
544 }
545
546 - (void)updateLayer
547 {
548     // Do what -drawRect: does but by setting a backgroundColor on the view. This avoids
549     // backing store for this view when the WebView is layer-backed.
550     if (![self documentView]) {
551         if ([[self _webView] drawsBackground]) {
552             [self setBackgroundColor:[[self _webView] backgroundColor]];
553             return;
554         }
555     } else {
556 #ifndef NDEBUG
557         if ([[self _scrollView] drawsBackground]) {
558             [self setBackgroundColor:[NSColor cyanColor]];
559             return;
560         }
561 #endif
562     }
563
564     [self setBackgroundColor:[NSColor clearColor]];
565 }
566 #endif
567
568 - (NSRect)visibleRect
569 {
570     // This method can be called beneath -[NSView dealloc] after we have cleared _private.
571     if (!_private)
572         return [super visibleRect];
573
574     // FIXME: <rdar://problem/6213380> This method does not work correctly with transforms, for two reasons:
575     // 1) [super visibleRect] does not account for the transform, since it is not represented
576     //    in the NSView hierarchy.
577     // 2) -_getVisibleRect: does not correct for transforms.
578
579     NSRect rendererVisibleRect;
580     if (![[self webFrame] _getVisibleRect:&rendererVisibleRect])
581         return [super visibleRect];
582
583     if (NSIsEmptyRect(rendererVisibleRect))
584         return NSZeroRect;
585
586     NSRect viewVisibleRect = [super visibleRect];
587     if (NSIsEmptyRect(viewVisibleRect))
588         return NSZeroRect;
589
590     NSRect frame = [self frame];
591     // rendererVisibleRect is in the parent's coordinate space, and frame is in the superview's coordinate space.
592     // The return value from this method needs to be in this view's coordinate space. We get that right by subtracting
593     // the origins (and correcting for flipping), but when we support transforms, we will need to do better than this.
594     rendererVisibleRect.origin.x -= frame.origin.x;
595     rendererVisibleRect.origin.y = NSMaxY(frame) - NSMaxY(rendererVisibleRect);
596     return NSIntersectionRect(rendererVisibleRect, viewVisibleRect);
597 }
598
599 - (void)setFrameSize:(NSSize)size
600 {
601     if (!NSEqualSizes(size, [self frame].size))
602         [self _frameSizeChanged];
603
604     [super setFrameSize:size];
605 }
606
607 - (void)viewDidMoveToWindow
608 {
609     // See WebFrameLoaderClient::provisionalLoadStarted.
610     // Need to check _private for nil because this can be called inside -[WebView initWithCoder:].
611     if (_private && [[[self webFrame] webView] drawsBackground])
612         [[self _scrollView] setDrawsBackground:YES];
613     [super viewDidMoveToWindow];
614 }
615
616 - (BOOL)_scrollOverflowInDirection:(ScrollDirection)direction granularity:(ScrollGranularity)granularity
617 {
618     // scrolling overflows is only applicable if we're dealing with an WebHTMLView
619     if (![[self documentView] isKindOfClass:[WebHTMLView class]])
620         return NO;
621     Frame* frame = core([self webFrame]);
622     if (!frame)
623         return NO;
624     return frame->eventHandler().scrollOverflow(direction, granularity);
625 }
626
627
628 - (BOOL)_isVerticalDocument
629 {
630     Frame* coreFrame = [self _web_frame];
631     if (!coreFrame)
632         return YES;
633     Document* document = coreFrame->document();
634     if (!document)
635         return YES;
636     RenderView* renderView = document->renderView();
637     if (!renderView)
638         return YES;
639     return renderView->style().isHorizontalWritingMode();
640 }
641
642 - (BOOL)_isFlippedDocument
643 {
644     Frame* coreFrame = [self _web_frame];
645     if (!coreFrame)
646         return NO;
647     Document* document = coreFrame->document();
648     if (!document)
649         return NO;
650     RenderView* renderView = document->renderView();
651     if (!renderView)
652         return NO;
653     return renderView->style().isFlippedBlocksWritingMode();
654 }
655
656 - (BOOL)_scrollToBeginningOfDocument
657 {
658     if ([self _scrollOverflowInDirection:ScrollUp granularity:ScrollByDocument])
659         return YES;
660     if (![self _isScrollable])
661         return NO;
662     NSPoint point = [(NSView *)[[self _scrollView] documentView] frame].origin;
663     point.x += [[self _scrollView] scrollOrigin].x;
664     point.y += [[self _scrollView] scrollOrigin].y;
665     return [[self _contentView] _scrollTo:&point animate:YES];
666 }
667
668 - (BOOL)_scrollToEndOfDocument
669 {
670     if ([self _scrollOverflowInDirection:ScrollDown granularity:ScrollByDocument])
671         return YES;
672     if (![self _isScrollable])
673         return NO;
674     NSRect frame = [(NSView *)[[self _scrollView] documentView] frame];
675     
676     bool isVertical = [self _isVerticalDocument];
677     bool isFlipped = [self _isFlippedDocument];
678
679     NSPoint point;
680     if (isVertical) {
681         if (!isFlipped)
682             point = NSMakePoint(frame.origin.x, NSMaxY(frame));
683         else
684             point = NSMakePoint(frame.origin.x, NSMinY(frame));
685     } else {
686         if (!isFlipped)
687             point = NSMakePoint(NSMaxX(frame), frame.origin.y);
688         else
689             point = NSMakePoint(NSMinX(frame), frame.origin.y);
690     }
691     
692     // Reset the position opposite to the block progression direction.
693     if (isVertical)
694         point.x += [[self _scrollView] scrollOrigin].x;
695     else
696         point.y += [[self _scrollView] scrollOrigin].y;
697     return [[self _contentView] _scrollTo:&point animate:YES];
698 }
699
700 - (void)scrollToBeginningOfDocument:(id)sender
701 {
702     if ([self _scrollToBeginningOfDocument])
703         return;
704     
705     if (WebFrameView *child = [self _largestScrollableChild]) {
706         if ([child _scrollToBeginningOfDocument])
707             return;
708     }
709     [[self nextResponder] tryToPerform:@selector(scrollToBeginningOfDocument:) with:sender];
710 }
711
712 - (void)scrollToEndOfDocument:(id)sender
713 {
714     if ([self _scrollToEndOfDocument])
715         return;
716
717     if (WebFrameView *child = [self _largestScrollableChild]) {
718         if ([child _scrollToEndOfDocument])
719             return;
720     }
721     [[self nextResponder] tryToPerform:@selector(scrollToEndOfDocument:) with:sender];
722 }
723
724 - (void)_goBack
725 {
726     [[self _webView] goBack];
727 }
728
729 - (void)_goForward
730 {
731     [[self _webView] goForward];
732 }
733
734 - (BOOL)_scrollVerticallyBy:(float)delta
735 {
736     // This method uses the secret method _scrollTo on NSClipView.
737     // It does that because it needs to know definitively whether scrolling was
738     // done or not to help implement the "scroll parent if we are at the limit" feature.
739     // In the presence of smooth scrolling, there's no easy way to tell if the method
740     // did any scrolling or not with the public API.
741     NSPoint point = [[self _contentView] bounds].origin;
742     point.y += delta;
743     return [[self _contentView] _scrollTo:&point animate:YES];
744 }
745
746 - (BOOL)_scrollHorizontallyBy:(float)delta
747 {
748     NSPoint point = [[self _contentView] bounds].origin;
749     point.x += delta;
750     return [[self _contentView] _scrollTo:&point animate:YES];
751 }
752
753 - (float)_horizontalKeyboardScrollDistance
754 {
755     // Arrow keys scroll the same distance that clicking the scroll arrow does.
756     return [[self _scrollView] horizontalLineScroll];
757 }
758
759 - (float)_horizontalPageScrollDistance
760 {
761     float width = [[self _contentView] bounds].size.width;
762     return std::max<float>(width * Scrollbar::minFractionToStepWhenPaging(), width - Scrollbar::maxOverlapBetweenPages());
763 }
764
765 - (BOOL)_pageVertically:(BOOL)up
766 {
767     if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByPage])
768         return YES;
769     
770     if (![self _isScrollable])
771         return [[self _largestScrollableChild] _pageVertically:up];
772
773     float delta = [self _verticalPageScrollDistance];
774     return [self _scrollVerticallyBy:up ? -delta : delta];
775 }
776
777 - (BOOL)_pageHorizontally:(BOOL)left
778 {
779     if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByPage])
780         return YES;
781
782     if (![self _isScrollable])
783         return [[self _largestScrollableChild] _pageHorizontally:left];
784     
785     float delta = [self _horizontalPageScrollDistance];
786     return [self _scrollHorizontallyBy:left ? -delta : delta];
787 }
788
789 - (BOOL)_pageInBlockProgressionDirection:(BOOL)forward
790 {
791     // Determine whether we're calling _pageVertically or _pageHorizontally.
792     BOOL isVerticalDocument = [self _isVerticalDocument];
793     BOOL isFlippedBlock = [self _isFlippedDocument];
794     if (isVerticalDocument)
795         return [self _pageVertically:isFlippedBlock ? !forward : forward];
796     return [self _pageHorizontally:isFlippedBlock ? !forward : forward];
797 }
798
799 - (BOOL)_scrollLineVertically:(BOOL)up
800 {
801     if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByLine])
802         return YES;
803
804     if (![self _isScrollable])
805         return [[self _largestScrollableChild] _scrollLineVertically:up];
806     
807     float delta = [self _verticalKeyboardScrollDistance];
808     return [self _scrollVerticallyBy:up ? -delta : delta];
809 }
810
811 - (BOOL)_scrollLineHorizontally:(BOOL)left
812 {
813     if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByLine])
814         return YES;
815
816     if (![self _isScrollable])
817         return [[self _largestScrollableChild] _scrollLineHorizontally:left];
818
819     float delta = [self _horizontalKeyboardScrollDistance];
820     return [self _scrollHorizontallyBy:left ? -delta : delta];
821 }
822
823 - (void)scrollPageUp:(id)sender
824 {
825     if (![self _pageInBlockProgressionDirection:YES]) {
826         // If we were already at the top, tell the next responder to scroll if it can.
827         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
828     }
829 }
830
831 - (void)scrollPageDown:(id)sender
832 {
833     if (![self _pageInBlockProgressionDirection:NO]) {
834         // If we were already at the bottom, tell the next responder to scroll if it can.
835         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
836     }
837 }
838
839 - (void)scrollLineUp:(id)sender
840 {
841     if (![self _scrollLineVertically:YES])
842         [[self nextResponder] tryToPerform:@selector(scrollLineUp:) with:sender];
843 }
844
845 - (void)scrollLineDown:(id)sender
846 {
847     if (![self _scrollLineVertically:NO])
848         [[self nextResponder] tryToPerform:@selector(scrollLineDown:) with:sender];
849 }
850
851 - (BOOL)_firstResponderIsFormControl
852 {
853 #if PLATFORM(IOS_FAMILY)
854     return NO;
855 #else
856     NSResponder *firstResponder = [[self window] firstResponder];
857     
858     // WebHTMLView is an NSControl subclass these days, but it's not a form control
859     if ([firstResponder isKindOfClass:[WebHTMLView class]]) {
860         return NO;
861     }
862     return [firstResponder isKindOfClass:[NSControl class]];
863 #endif
864 }
865
866 #if PLATFORM(IOS_FAMILY)
867 // Unlike OS X WebKit, on iOS, unhandled mouse events are forwarded to allow for scrolling.
868 // Since mouse events were forwarded to this WebFrameView, this means that the subviews didn't
869 // handle the event. Pass the events to the next scroll view.
870 - (void)_forwardMouseEvent:(WebEvent *)event
871 {
872     WAKView *superview = [self superview];
873     if ([superview conformsToProtocol:@protocol(WebDocumentView)])
874         [[[superview _web_parentWebFrameView] _scrollView] handleEvent:event];
875     else
876         [[self nextResponder] handleEvent:event];
877 }
878
879 - (void)mouseDown:(WebEvent *)event
880 {
881     [self _forwardMouseEvent:event];
882 }
883
884 - (void)mouseUp:(WebEvent *)event
885 {
886     [self _forwardMouseEvent:event];
887 }
888 #endif
889
890 #if !PLATFORM(IOS_FAMILY)
891 - (void)keyDown:(NSEvent *)event
892 #else
893 - (void)keyDown:(WebEvent *)event
894 #endif
895 {
896     // Implement common browser behaviors for all kinds of content.
897
898     // FIXME: This is not a good time to execute commands for WebHTMLView. We should run these at the time commands sent by key bindings
899     // are executed for consistency.
900     // This doesn't work automatically because most of the keys handled here are translated into moveXXX commands, which are not handled
901     // by Editor when focus is not in editable content.
902
903 #if !PLATFORM(IOS_FAMILY)
904     NSString *characters = [event characters];
905     int modifierFlags = [event modifierFlags];
906 #else
907     NSString *characters = event.characters;
908     int modifierFlags = event.modifierFlags;
909 #endif
910     int index, count;
911     BOOL callSuper = YES;
912     Frame* coreFrame = [self _web_frame];
913     BOOL maintainsBackForwardList = coreFrame && static_cast<BackForwardList&>(coreFrame->page()->backForward().client()).enabled() ? YES : NO;
914
915     count = [characters length];
916     for (index = 0; index < count; ++index) {
917         switch ([characters characterAtIndex:index]) {
918             case NSDeleteCharacter:
919                 if (!maintainsBackForwardList || ![[[self _webView] preferences] backspaceKeyNavigationEnabled]) {
920                     callSuper = YES;
921                     break;
922                 }
923                 // This odd behavior matches some existing browsers,
924                 // including Windows IE
925                 if (modifierFlags & NSEventModifierFlagShift) {
926                     [self _goForward];
927                 } else {
928                     [self _goBack];
929                 }
930                 callSuper = NO;
931                 break;
932             case SpaceKey:
933                 // Checking for a control will allow events to percolate 
934                 // correctly when the focus is on a form control and we
935                 // are in full keyboard access mode.
936                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) || [self _firstResponderIsFormControl]) {
937                     callSuper = YES;
938                     break;
939                 }
940                 if (modifierFlags & NSEventModifierFlagShift) {
941                     [self scrollPageUp:nil];
942                 } else {
943                     [self scrollPageDown:nil];
944                 }
945                 callSuper = NO;
946                 break;
947             case NSPageUpFunctionKey:
948                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
949                     callSuper = YES;
950                     break;
951                 }
952                 [self scrollPageUp:nil];
953                 callSuper = NO;
954                 break;
955             case NSPageDownFunctionKey:
956                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
957                     callSuper = YES;
958                     break;
959                 }
960                 [self scrollPageDown:nil];
961                 callSuper = NO;
962                 break;
963             case NSHomeFunctionKey:
964                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
965                     callSuper = YES;
966                     break;
967                 }
968                 [self scrollToBeginningOfDocument:nil];
969                 callSuper = NO;
970                 break;
971             case NSEndFunctionKey:
972                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
973                     callSuper = YES;
974                     break;
975                 }
976                 [self scrollToEndOfDocument:nil];
977                 callSuper = NO;
978                 break;
979             case NSUpArrowFunctionKey:
980                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
981                 if (modifierFlags & (NSEventModifierFlagShift | NSEventModifierFlagControl)) {
982                     callSuper = YES;
983                     break;
984                 }
985 #if !PLATFORM(IOS_FAMILY)
986                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) ||
987                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
988                     // Let arrow keys go through to pop up buttons
989                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
990                     // pop-up menu should pop the menu
991                     callSuper = YES;
992                     break;
993                 }
994 #endif
995                 if (modifierFlags & NSEventModifierFlagCommand) {
996                     [self scrollToBeginningOfDocument:nil];
997                 } else if (modifierFlags & NSEventModifierFlagOption) {
998                     [self scrollPageUp:nil];
999                 } else {
1000                     [self scrollLineUp:nil];
1001                 }
1002                 callSuper = NO;
1003                 break;
1004             case NSDownArrowFunctionKey:
1005                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
1006                 if (modifierFlags & (NSEventModifierFlagShift | NSEventModifierFlagControl)) {
1007                     callSuper = YES;
1008                     break;
1009                 }
1010 #if !PLATFORM(IOS_FAMILY)
1011                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) ||
1012                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
1013                     // Let arrow keys go through to pop up buttons
1014                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
1015                     // pop-up menu should pop the menu
1016                     callSuper = YES;
1017                     break;
1018                 }
1019 #endif
1020                 if (modifierFlags & NSEventModifierFlagCommand) {
1021                     [self scrollToEndOfDocument:nil];
1022                 } else if (modifierFlags & NSEventModifierFlagOption) {
1023                     [self scrollPageDown:nil];
1024                 } else {
1025                     [self scrollLineDown:nil];
1026                 }
1027                 callSuper = NO;
1028                 break;
1029             case NSLeftArrowFunctionKey:
1030                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
1031                 if (modifierFlags & (NSEventModifierFlagShift | NSEventModifierFlagControl)) {
1032                     callSuper = YES;
1033                     break;
1034                 }
1035                 // Check back/forward related keys.
1036                 if (modifierFlags & NSEventModifierFlagCommand) {
1037                     if (!maintainsBackForwardList) {
1038                         callSuper = YES;
1039                         break;
1040                     }
1041                     [self _goBack];
1042                 } else {
1043                     // Now check scrolling related keys.
1044                     if ((![self allowsScrolling] && ![self _largestScrollableChild])) {
1045                         callSuper = YES;
1046                         break;
1047                     }
1048
1049                     if (modifierFlags & NSEventModifierFlagOption) {
1050                         [self _pageHorizontally:YES];
1051                     } else {
1052                         [self _scrollLineHorizontally:YES];
1053                     }
1054                 }
1055                 callSuper = NO;
1056                 break;
1057             case NSRightArrowFunctionKey:
1058                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
1059                 if (modifierFlags & (NSEventModifierFlagShift | NSEventModifierFlagControl)) {
1060                     callSuper = YES;
1061                     break;
1062                 }
1063                 // Check back/forward related keys.
1064                 if (modifierFlags & NSEventModifierFlagCommand) {
1065                     if (!maintainsBackForwardList) {
1066                         callSuper = YES;
1067                         break;
1068                     }
1069                     [self _goForward];
1070                 } else {
1071                     // Now check scrolling related keys.
1072                     if ((![self allowsScrolling] && ![self _largestScrollableChild])) {
1073                         callSuper = YES;
1074                         break;
1075                     }
1076
1077                     if (modifierFlags & NSEventModifierFlagOption) {
1078                         [self _pageHorizontally:NO];
1079                     } else {
1080                         [self _scrollLineHorizontally:NO];
1081                     }
1082                 }
1083                 callSuper = NO;
1084                 break;
1085         }
1086     }
1087     if (callSuper) {
1088         [super keyDown:event];
1089     } else {
1090         // if we did something useful, get the cursor out of the way
1091         [NSCursor setHiddenUntilMouseMoves:YES];
1092     }
1093 }
1094
1095 - (NSView *)_webcore_effectiveFirstResponder
1096 {
1097     NSView *view = [self documentView];
1098     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
1099 }
1100
1101 #if !PLATFORM(IOS_FAMILY)
1102 - (BOOL)canPrintHeadersAndFooters
1103 {
1104     NSView *documentView = [[self _scrollView] documentView];
1105     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
1106         return [(id)documentView canPrintHeadersAndFooters];
1107     }
1108     return NO;
1109 }
1110
1111 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
1112 {
1113     NSView *documentView = [[self _scrollView] documentView];
1114     if (!documentView) {
1115         return nil;
1116     }
1117     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
1118         return [(id)documentView printOperationWithPrintInfo:printInfo];
1119     }
1120     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
1121 }
1122 #endif
1123
1124 - (BOOL)documentViewShouldHandlePrint
1125 {
1126     NSView *documentView = [[self _scrollView] documentView];
1127     if (documentView && [documentView respondsToSelector:@selector(documentViewShouldHandlePrint)])
1128         return [(id)documentView documentViewShouldHandlePrint];
1129     
1130     return NO;
1131 }
1132
1133 - (void)printDocumentView
1134 {
1135     NSView *documentView = [[self _scrollView] documentView];
1136     if (documentView && [documentView respondsToSelector:@selector(printDocumentView)])
1137         [(id)documentView printDocumentView];
1138 }
1139
1140 @end
1141
1142 @implementation WebFrameView (WebPrivate)
1143
1144 - (float)_area
1145 {
1146     NSRect frame = [self frame];
1147     return frame.size.height * frame.size.width;
1148 }
1149
1150 - (BOOL)_isScrollable
1151 {
1152 #if !PLATFORM(IOS_FAMILY)
1153     WebDynamicScrollBarsView *scrollView = [self _scrollView];
1154     return [scrollView horizontalScrollingAllowed] || [scrollView verticalScrollingAllowed];
1155 #else
1156     return [self _hasScrollBars];
1157 #endif
1158 }
1159
1160 - (WebFrameView *)_largestScrollableChild
1161 {
1162     WebFrameView *largest = nil;
1163     NSArray *frameChildren = [[self webFrame] childFrames];
1164     
1165     unsigned i;
1166     for (i=0; i < [frameChildren count]; i++) {
1167         WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView];
1168         WebFrameView *scrollableFrameView = [childFrameView _isScrollable] ? childFrameView : [childFrameView _largestScrollableChild];
1169         if (!scrollableFrameView)
1170             continue;
1171         
1172         // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable.
1173         // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases.
1174         float area = [scrollableFrameView _area];
1175         if (area < 1.0)
1176             continue;
1177         
1178         if (!largest || (area > [largest _area])) {
1179             largest = scrollableFrameView;
1180         }
1181     }
1182     
1183     return largest;
1184 }
1185
1186 - (BOOL)_hasScrollBars
1187 {
1188     // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit
1189     // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed 
1190     // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier.
1191     NSScrollView *scrollView = [self _scrollView];
1192     return [scrollView hasHorizontalScroller] || [scrollView hasVerticalScroller];
1193 }
1194
1195 - (WebFrameView *)_largestChildWithScrollBars
1196 {
1197     // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit
1198     // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed 
1199     // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier.
1200     WebFrameView *largest = nil;
1201     NSArray *frameChildren = [[self webFrame] childFrames];
1202     
1203     unsigned i;
1204     for (i=0; i < [frameChildren count]; i++) {
1205         WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView];
1206         WebFrameView *scrollableFrameView = [childFrameView _hasScrollBars] ? childFrameView : [childFrameView _largestChildWithScrollBars];
1207         if (!scrollableFrameView)
1208             continue;
1209         
1210         // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable.
1211         // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases.
1212         float area = [scrollableFrameView _area];
1213         if (area < 1.0)
1214             continue;
1215         
1216         if (!largest || (area > [largest _area])) {
1217             largest = scrollableFrameView;
1218         }
1219     }
1220     
1221     return largest;
1222 }
1223
1224 - (NSClipView *)_contentView
1225 {
1226     return [[self _scrollView] contentView];
1227 }
1228
1229 - (Class)_customScrollViewClass
1230 {
1231     if ([_private->frameScrollView class] == [WebDynamicScrollBarsView class])
1232         return nil;
1233     return [_private->frameScrollView class];
1234 }
1235
1236 #if !PLATFORM(IOS_FAMILY)
1237 - (void)_setCustomScrollViewClass:(Class)customClass
1238 {
1239     if (!customClass)
1240         customClass = [WebDynamicScrollBarsView class];
1241     ASSERT([customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]);
1242     if (customClass == [_private->frameScrollView class])
1243         return;
1244     if (![customClass isSubclassOfClass:[WebDynamicScrollBarsView class]])
1245         return;
1246
1247     WebDynamicScrollBarsView *oldScrollView = _private->frameScrollView; // already retained
1248     NSView <WebDocumentView> *documentView = [[self documentView] retain];
1249
1250     WebDynamicScrollBarsView *scrollView  = [[customClass alloc] initWithFrame:[oldScrollView frame]];
1251     [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
1252     [scrollView setDrawsBackground:[oldScrollView drawsBackground]];
1253     [scrollView setHasVerticalScroller:[oldScrollView hasVerticalScroller]];
1254     [scrollView setHasHorizontalScroller:[oldScrollView hasHorizontalScroller]];
1255     [scrollView setAutoresizingMask:[oldScrollView autoresizingMask]];
1256     [scrollView setLineScroll:[oldScrollView lineScroll]];
1257     [self addSubview:scrollView];
1258
1259     // don't call our overridden version here; we need to make the standard NSView link between us
1260     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
1261     // together with our becomeFirstResponder and setNextKeyView overrides.
1262     [super setNextKeyView:scrollView];
1263
1264     _private->frameScrollView = scrollView;
1265
1266     [self _setDocumentView:documentView];
1267     [self _install];
1268
1269     [oldScrollView removeFromSuperview];
1270     [oldScrollView release];
1271     [documentView release];
1272 }
1273 #endif // !PLATFORM(IOS_FAMILY)
1274
1275 @end