Reviewed by Darin.
[WebKit-https.git] / WebKit / WebView.subproj / WebFrameView.m
1 /*      WebFrameView.m
2         Copyright 2001, 2002, Apple Computer, Inc. All rights reserved.
3 */
4
5 #import <WebKit/WebFrameView.h>
6
7 #import <WebKit/WebBridge.h>
8 #import <WebKit/WebClipView.h>
9 #import <WebKit/WebCookieAdapter.h>
10 #import <WebKit/WebDataSource.h>
11 #import <WebKit/WebDocument.h>
12 #import <WebKit/WebDynamicScrollBarsView.h>
13 #import <WebKit/WebFrame.h>
14 #import <WebKit/WebFrameViewInternal.h>
15 #import <WebKit/WebHTMLViewPrivate.h>
16 #import <WebKit/WebGraphicsBridge.h>
17 #import <WebKit/WebImageRenderer.h>
18 #import <WebKit/WebImageRendererFactory.h>
19 #import <WebKit/WebImageView.h>
20 #import <WebKit/WebKeyGenerator.h>
21 #import <WebKit/WebKitErrorsPrivate.h>
22 #import <WebKit/WebKitStatisticsPrivate.h>
23 #import <WebKit/WebNSObjectExtras.h>
24 #import <WebKit/WebNSPasteboardExtras.h>
25 #import <WebKit/WebNSViewExtras.h>
26 #ifndef OMIT_TIGER_FEATURES
27 #import <WebKit/WebPDFView.h>
28 #endif
29 #import <WebKit/WebTextRendererFactory.h>
30 #import <WebKit/WebTextView.h>
31 #import <WebKit/WebViewFactory.h>
32 #import <WebKit/WebViewInternal.h>
33 #import <WebKit/WebViewPrivate.h>
34 #import <WebKit/WebAssertions.h>
35
36 #import <WebCore/WebCoreFrameView.h>
37 #import <WebCore/WebCoreView.h>
38
39 #import <Foundation/NSDictionary_NSURLExtras.h>
40 #import <Foundation/NSURLRequest.h>
41
42 @interface NSClipView (AppKitSecretsIKnow)
43 - (BOOL)_scrollTo:(const NSPoint *)newOrigin; // need the boolean result from this method
44 @end
45
46 enum {
47     SpaceKey = 0x0020
48 };
49
50 @interface WebFrameView (WebFrameViewFileInternal) <WebCoreBridgeHolder>
51 - (float)_verticalKeyboardScrollDistance;
52 - (void)_tile;
53 - (BOOL)_shouldDrawBorder;
54 - (WebCoreBridge *) webCoreBridge;
55 @end
56
57 @interface WebFrameViewPrivate : NSObject
58 {
59 @public
60     WebView *webView;
61     WebDynamicScrollBarsView *frameScrollView;
62     
63     // These margin values are used to temporarily hold the margins of a frame until
64     // we have the appropriate document view type.
65     int marginWidth;
66     int marginHeight;
67     
68     NSArray *draggingTypes;
69     
70     BOOL hasBorder;
71 }
72 @end
73
74 @implementation WebFrameViewPrivate
75
76 - init
77 {
78     [super init];
79     
80     marginWidth = -1;
81     marginHeight = -1;
82     
83     return self;
84 }
85
86 - (void)dealloc
87 {
88     [frameScrollView release];
89     [draggingTypes release];
90     [super dealloc];
91 }
92
93 @end
94
95 @implementation WebFrameView (WebFrameViewFileInternal)
96
97 - (float)_verticalKeyboardScrollDistance
98 {
99     // verticalLineScroll is quite small, to make scrolling from the scroll bar
100     // arrows relatively smooth. But this seemed too small for scrolling with
101     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
102     return [[self _scrollView] verticalLineScroll] * 4;
103 }
104
105 - (BOOL)_shouldDrawBorder
106 {
107     if (!_private->hasBorder)
108         return NO;
109         
110     // Only draw a border for frames that request a border and the frame does
111     // not contain a frameset.  Additionally we should (post-panther) not draw
112     // a border (left, right, top or bottom) if the frame edge abutts the window frame.
113     NSView *docV = [self documentView];
114     if ([docV isKindOfClass:[WebHTMLView class]]){
115         if ([[(WebHTMLView *)docV _bridge] isFrameSet]){
116             return NO;
117         }
118     }
119     return YES;
120 }
121
122 - (void)_tile
123 {
124     NSRect scrollViewFrame = [self bounds];
125     // The border drawn by WebFrameView is 1 pixel on the left and right,
126     // two pixels on top and bottom.  Shrink the scroll view to accomodate
127     // the border.
128     if ([self _shouldDrawBorder]) {
129         scrollViewFrame = NSInsetRect (scrollViewFrame, 1, 2);
130     }
131     [_private->frameScrollView setFrame:scrollViewFrame];
132 }
133
134 - (WebCoreBridge *) webCoreBridge
135 {
136     return [self _bridge];
137 }
138
139 @end
140
141 @implementation WebFrameView (WebInternal)
142
143 // Note that the WebVew is not retained.
144 - (WebView *)_webView
145 {
146     return _private->webView;
147 }
148
149 - (void)_setMarginWidth: (int)w
150 {
151     _private->marginWidth = w;
152 }
153
154 - (int)_marginWidth
155 {
156     return _private->marginWidth;
157 }
158
159 - (void)_setMarginHeight: (int)h
160 {
161     _private->marginHeight = h;
162 }
163
164 - (int)_marginHeight
165 {
166     return _private->marginHeight;
167 }
168
169 - (void)_setDocumentView:(NSView *)view
170 {
171     WebDynamicScrollBarsView *sv = (WebDynamicScrollBarsView *)[self _scrollView];
172     
173     [sv setSuppressLayout: YES];
174     
175     // Always start out with arrow.  New docView can then change as needed, but doesn't have to
176     // clean up after the previous docView.  Also TextView will set cursor when added to view
177     // tree, so must do this before setDocumentView:.
178     [sv setDocumentCursor:[NSCursor arrowCursor]];
179
180     [sv setDocumentView: view];
181     [sv setSuppressLayout: NO];
182 }
183
184 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
185 {    
186     Class viewClass = [[self class] _viewClassForMIMEType:[[dataSource response] MIMEType]];
187     NSView <WebDocumentView> *documentView = viewClass ? [[viewClass alloc] init] : nil;
188     [self _setDocumentView:documentView];
189     [documentView release];
190     
191     return documentView;
192 }
193
194 - (void)_setWebView:(WebView *)webView
195 {
196     // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
197     _private->webView = webView;    
198 }
199
200 - (NSScrollView *)_scrollView
201 {
202     // this can be called by [super dealloc] when cleaning up the keyview loop,
203     // after _private has been nilled out.
204     if (_private == nil) {
205         return nil;
206     }
207     return _private->frameScrollView;
208 }
209
210 - (NSClipView *)_contentView
211 {
212     return [[self _scrollView] contentView];
213 }
214
215 - (float)_verticalPageScrollDistance
216 {
217     float overlap = [self _verticalKeyboardScrollDistance];
218     float height = [[self _contentView] bounds].size.height;
219     return (height < overlap) ? height / 2 : height - overlap;
220 }
221
222 static NSMutableDictionary *viewTypes;
223
224 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
225 {
226     static BOOL addedImageTypes;
227
228     if (!viewTypes) {
229         viewTypes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
230             [WebHTMLView class], @"text/html",
231             [WebHTMLView class], @"text/xml",
232             [WebHTMLView class], @"text/xsl",
233             [WebHTMLView class], @"application/xml",
234             [WebHTMLView class], @"application/xhtml+xml",
235             [WebHTMLView class], @"application/rss+xml",
236             [WebHTMLView class], @"application/atom+xml",
237             [WebHTMLView class], @"application/x-webarchive",
238             [WebTextView class], @"text/",
239             [WebTextView class], @"application/x-javascript",
240             nil];
241
242 #ifndef OMIT_TIGER_FEATURES
243         // Since this is a "secret default" we don't both registering it.
244         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];  
245         if (!omitPDFSupport) {
246             [viewTypes setObject:[WebPDFView class] forKey:@"text/pdf"];
247             [viewTypes setObject:[WebPDFView class] forKey:@"application/pdf"];
248         }
249 #endif
250     }
251
252     if (!addedImageTypes && !allowImageTypeOmission) {
253         NSEnumerator *enumerator = [[WebImageView supportedImageMIMETypes] objectEnumerator];
254         ASSERT(enumerator != nil);
255         NSString *mime;
256         while ((mime = [enumerator nextObject]) != nil) {
257             // Don't clobber previously-registered view classes.
258             if ([viewTypes objectForKey:mime] == nil) {
259                 [viewTypes setObject:[WebImageView class] forKey:mime];
260             }
261         }
262         addedImageTypes = YES;
263     }
264     
265     return viewTypes;
266 }
267
268 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
269 {
270     return [[[self _viewTypesAllowImageTypeOmission:YES] objectForKey:MIMEType] isSubclassOfClass:[WebHTMLView class]];
271 }
272
273 + (Class)_viewClassForMIMEType:(NSString *)MIMEType
274 {
275     Class viewClass;
276     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType] ? viewClass : nil;
277 }
278
279 - (void)_setHasBorder:(BOOL)hasBorder
280 {
281     if (_private->hasBorder == hasBorder) {
282         return;
283     }
284     _private->hasBorder = hasBorder;
285     [self _tile];
286 }
287
288 @end
289
290 @implementation WebFrameView
291
292 - initWithFrame: (NSRect) frame
293 {
294     [super initWithFrame: frame];
295  
296     [WebViewFactory createSharedFactory];
297     [WebTextRendererFactory createSharedFactory];
298     [WebImageRendererFactory createSharedFactory];
299     [WebCookieAdapter createSharedAdapter];
300     [WebGraphicsBridge createSharedBridge];
301     [WebKeyGenerator createSharedGenerator];
302     
303     _private = [[WebFrameViewPrivate alloc] init];
304
305     WebDynamicScrollBarsView *scrollView  = [[WebDynamicScrollBarsView alloc] initWithFrame: NSMakeRect(0,0,frame.size.width,frame.size.height)];
306     _private->frameScrollView = scrollView;
307     [scrollView setContentView: [[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
308     [scrollView setDrawsBackground: NO];
309     [scrollView setHasVerticalScroller: NO];
310     [scrollView setHasHorizontalScroller: NO];
311     [scrollView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
312     [self addSubview: scrollView];
313     // don't call our overridden version here; we need to make the standard NSView link between us
314     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
315     // together with our becomeFirstResponder and setNextKeyView overrides.
316     [super setNextKeyView:scrollView];
317     
318     ++WebFrameViewCount;
319     
320     return self;
321 }
322
323 - (void)dealloc 
324 {
325     --WebFrameViewCount;
326     
327     [_private release];
328     _private = nil;
329     
330     [super dealloc];
331 }
332
333 - (void)finalize 
334 {
335     --WebFrameViewCount;
336
337     _private = nil;
338
339     [super finalize];
340 }
341
342 - (WebFrame *)webFrame
343 {
344     return [[self _webView] _frameForView: self]; 
345 }
346
347 - (void)setAllowsScrolling: (BOOL)flag
348 {
349     [(WebDynamicScrollBarsView *)[self _scrollView] setAllowsScrolling: flag];
350 }
351
352 - (BOOL)allowsScrolling
353 {
354     return [(WebDynamicScrollBarsView *)[self _scrollView] allowsScrolling];
355 }
356
357 - documentView
358 {
359     return [[self _scrollView] documentView];
360 }
361
362 - (BOOL)acceptsFirstResponder
363 {
364     // We always accept first responder; this matches OS X 10.2 WebKit
365     // behavior (see 3469791).
366     return YES;
367 }
368
369 - (BOOL)becomeFirstResponder
370 {
371     // This works together with setNextKeyView to splice the WebFrameView into
372     // the key loop similar to the way NSScrollView does this. Note that
373     // WebView has similar code.
374     
375     // If the scrollView won't accept first-responderness now, then we just become
376     // the first responder ourself like a normal view. This lets us be the first 
377     // responder in cases where no page has yet been loaded (see 3469791).
378     if ([[self _scrollView] acceptsFirstResponder]) {
379         NSWindow *window = [self window];
380         if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
381             NSView *previousValidKeyView = [self previousValidKeyView];
382             // If we couldn't find a previous valid key view, ask the webview. This handles frameset
383             // cases like 3748628. Note that previousValidKeyView should never be self but can be
384             // due to AppKit oddness (mentioned in 3748628).
385             if (previousValidKeyView == nil || previousValidKeyView == self) {
386                 previousValidKeyView = [[[self webFrame] webView] previousValidKeyView];
387             }
388             // I don't know if the following cases ever occur anymore, but I'm leaving in the old test for
389             // now to avoid causing trouble just before shipping Tiger.
390             ASSERT((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView]));
391             if ((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView])) {
392                 [window makeFirstResponder:previousValidKeyView];
393             }
394         } else {
395             [window makeFirstResponder:[self _scrollView]];
396         }
397     }    
398     
399     return YES;
400 }
401
402 - (void)setNextKeyView:(NSView *)aView
403 {
404     // This works together with becomeFirstResponder to splice the WebFrameView into
405     // the key loop similar to the way NSScrollView does this. Note that
406     // WebView has very similar code.
407     if ([self _scrollView] != nil) {
408         [[self _scrollView] setNextKeyView:aView];
409     } else {
410         [super setNextKeyView:aView];
411     }
412 }
413
414 - (BOOL)isOpaque
415 {
416     return [[self _webView] drawsBackground];
417 }
418
419 - (void)_drawBorder
420 {
421     if ([self _shouldDrawBorder]){
422         NSRect vRect = [self frame];
423             
424         // Left, black
425         [[NSColor blackColor] set];
426         NSRectFill(NSMakeRect(0,0,1,vRect.size.height));
427         
428         // Top, light gray, black
429         [[NSColor lightGrayColor] set];
430         NSRectFill(NSMakeRect(0,0,vRect.size.width,1));
431         [[NSColor whiteColor] set];
432         NSRectFill(NSMakeRect(1,1,vRect.size.width-2,1));
433         
434         // Right, light gray
435         [[NSColor lightGrayColor] set];
436         NSRectFill(NSMakeRect(vRect.size.width-1,1,1,vRect.size.height-2));
437         
438         // Bottom, light gray, white
439         [[NSColor blackColor] set];
440         NSRectFill(NSMakeRect(0,vRect.size.height-1,vRect.size.width,1));
441         [[NSColor lightGrayColor] set];
442         NSRectFill(NSMakeRect(1,vRect.size.height-2,vRect.size.width-2,1));
443     }
444 }
445
446 - (void)drawRect:(NSRect)rect
447 {
448     if ([self documentView] == nil) {
449         // Need to paint ourselves if there's no documentView to do it instead.
450         if ([[self _webView] drawsBackground]) {
451             [[NSColor whiteColor] set];
452             NSRectFill(rect);
453         }
454     } else {
455 #ifndef NDEBUG
456         if ([[self _scrollView] drawsBackground]) {
457             [[NSColor cyanColor] set];
458             NSRectFill(rect);
459         }
460 #endif
461     }
462     
463     [self _drawBorder];
464 }
465
466 - (void)setFrameSize:(NSSize)size
467 {
468     if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
469         [[self _scrollView] setDrawsBackground:YES];
470     }
471     [super setFrameSize:size];
472     [self _tile];
473 }
474
475 - (WebBridge *)_bridge
476 {
477     return [[self webFrame] _bridge];
478 }
479
480 - (BOOL)_scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity
481 {
482     // scrolling overflows is only applicable if we're dealing with an WebHTMLView
483     return ([[self documentView] isKindOfClass:[WebHTMLView class]] && [[self _bridge] scrollOverflowInDirection:direction granularity:granularity]);
484 }
485
486 - (void)scrollToBeginningOfDocument:(id)sender
487 {
488     if (![self _scrollOverflowInDirection:WebScrollUp granularity:WebScrollDocument]) {
489         [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
490     }
491 }
492
493 - (void)scrollToEndOfDocument:(id)sender
494 {
495     if (![self _scrollOverflowInDirection:WebScrollDown granularity:WebScrollDocument]) {
496         NSRect frame = [[[self _scrollView] documentView] frame];
497         [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
498     }
499 }
500
501 - (void)_goBack
502 {
503     [_private->webView goBack];
504 }
505
506 - (void)_goForward
507 {
508     [_private->webView goForward];
509 }
510
511 - (BOOL)_scrollVerticallyBy: (float)delta
512 {
513     // This method uses the secret method _scrollTo on NSClipView.
514     // It does that because it needs to know definitively whether scrolling was
515     // done or not to help implement the "scroll parent if we are at the limit" feature.
516     // In the presence of smooth scrolling, there's no easy way to tell if the method
517     // did any scrolling or not with the public API.
518     NSPoint point = [[self _contentView] bounds].origin;
519     point.y += delta;
520     return [[self _contentView] _scrollTo:&point];
521 }
522
523 - (BOOL)_scrollHorizontallyBy: (float)delta
524 {
525     NSPoint point = [[self _contentView] bounds].origin;
526     point.x += delta;
527     return [[self _contentView] _scrollTo:&point];
528 }
529
530 - (float)_horizontalKeyboardScrollDistance
531 {
532     // horizontalLineScroll is quite small, to make scrolling from the scroll bar
533     // arrows relatively smooth. But this seemed too small for scrolling with
534     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
535     return [[self _scrollView] horizontalLineScroll] * 4;
536 }
537
538 - (float)_horizontalPageScrollDistance
539 {
540     float overlap = [self _horizontalKeyboardScrollDistance];
541     float width = [[self _contentView] bounds].size.width;
542     return (width < overlap) ? width / 2 : width - overlap;
543 }
544
545 - (BOOL)_pageVertically:(BOOL)up
546 {
547     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollPage]) {
548         return YES;
549     }
550     float delta = [self _verticalPageScrollDistance];
551     return [self _scrollVerticallyBy:up ? -delta : delta];
552 }
553
554 - (BOOL)_pageHorizontally:(BOOL)left
555 {
556     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollPage]) {
557         return YES;
558     }
559     float delta = [self _horizontalPageScrollDistance];
560     return [self _scrollHorizontallyBy:left ? -delta : delta];
561 }
562
563 - (BOOL)_scrollLineVertically:(BOOL)up
564 {
565     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollLine]) {
566         return YES;
567     }
568     float delta = [self _verticalKeyboardScrollDistance];
569     return [self _scrollVerticallyBy:up ? -delta : delta];
570 }
571
572 - (BOOL)_scrollLineHorizontally:(BOOL)left
573 {
574     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollLine]) {
575         return YES;
576     }
577     float delta = [self _horizontalKeyboardScrollDistance];
578     return [self _scrollHorizontallyBy:left ? -delta : delta];
579 }
580
581 - (void)scrollPageUp:(id)sender
582 {
583     if (![self _pageVertically:YES]) {
584         // If we were already at the top, tell the next responder to scroll if it can.
585         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
586     }
587 }
588
589 - (void)scrollPageDown:(id)sender
590 {
591     if (![self _pageVertically:NO]) {
592         // If we were already at the bottom, tell the next responder to scroll if it can.
593         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
594     }
595 }
596
597 - (void)scrollLineUp:(id)sender
598 {
599     [self _scrollLineVertically:YES];
600 }
601
602 - (void)scrollLineDown:(id)sender
603 {
604     [self _scrollLineVertically:NO];
605 }
606
607 - (BOOL)_firstResponderIsControl
608 {
609     return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
610 }
611
612 - (void)keyDown:(NSEvent *)event
613 {
614     NSString *characters = [event characters];
615     int index, count;
616     BOOL callSuper = YES;
617     BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
618
619     count = [characters length];
620     for (index = 0; index < count; ++index) {
621         switch ([characters characterAtIndex:index]) {
622             case NSDeleteCharacter:
623                 if (!maintainsBackForwardList) {
624                     callSuper = YES;
625                     break;
626                 }
627                 // This odd behavior matches some existing browsers,
628                 // including Windows IE
629                 if ([event modifierFlags] & NSShiftKeyMask) {
630                     [self _goForward];
631                 } else {
632                     [self _goBack];
633                 }
634                 callSuper = NO;
635                 break;
636             case SpaceKey:
637                 // Checking for a control will allow events to percolate 
638                 // correctly when the focus is on a form control and we
639                 // are in full keyboard access mode.
640                 if (![self allowsScrolling] || [self _firstResponderIsControl]) {
641                     callSuper = YES;
642                     break;
643                 }
644                 if ([event modifierFlags] & NSShiftKeyMask) {
645                     [self scrollPageUp:nil];
646                 } else {
647                     [self scrollPageDown:nil];
648                 }
649                 callSuper = NO;
650                 break;
651             case NSPageUpFunctionKey:
652                 if (![self allowsScrolling]) {
653                     callSuper = YES;
654                     break;
655                 }
656                 [self scrollPageUp:nil];
657                 callSuper = NO;
658                 break;
659             case NSPageDownFunctionKey:
660                 if (![self allowsScrolling]) {
661                     callSuper = YES;
662                     break;
663                 }
664                 [self scrollPageDown:nil];
665                 callSuper = NO;
666                 break;
667             case NSHomeFunctionKey:
668                 if (![self allowsScrolling]) {
669                     callSuper = YES;
670                     break;
671                 }
672                 [self scrollToBeginningOfDocument:nil];
673                 callSuper = NO;
674                 break;
675             case NSEndFunctionKey:
676                 if (![self allowsScrolling]) {
677                     callSuper = YES;
678                     break;
679                 }
680                 [self scrollToEndOfDocument:nil];
681                 callSuper = NO;
682                 break;
683             case NSUpArrowFunctionKey:
684                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
685                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
686                     callSuper = YES;
687                     break;
688                 }
689                 if (![self allowsScrolling] ||
690                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
691                     // Let arrow keys go through to pop up buttons
692                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
693                     // pop-up menu should pop the menu
694                     callSuper = YES;
695                     break;
696                 }
697                 if ([event modifierFlags] & NSCommandKeyMask) {
698                     [self scrollToBeginningOfDocument:nil];
699                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
700                     [self scrollPageUp:nil];
701                 } else {
702                     [self scrollLineUp:nil];
703                 }
704                 callSuper = NO;
705                 break;
706             case NSDownArrowFunctionKey:
707                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
708                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
709                     callSuper = YES;
710                     break;
711                 }
712                 if (![self allowsScrolling] ||
713                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
714                     // Let arrow keys go through to pop up buttons
715                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
716                     // pop-up menu should pop the menu
717                     callSuper = YES;
718                     break;
719                 }
720                 if ([event modifierFlags] & NSCommandKeyMask) {
721                     [self scrollToEndOfDocument:nil];
722                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
723                     [self scrollPageDown:nil];
724                 } else {
725                     [self scrollLineDown:nil];
726                 }
727                 callSuper = NO;
728                 break;
729             case NSLeftArrowFunctionKey:
730                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
731                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
732                     callSuper = YES;
733                     break;
734                 }
735                 // Check back/forward related keys.
736                 if ([event modifierFlags] & NSCommandKeyMask) {
737                     if (!maintainsBackForwardList) {
738                         callSuper = YES;
739                         break;
740                     }
741                     [self _goBack];
742                 } else {
743                     // Now check scrolling related keys.
744                     if (![self allowsScrolling]) {
745                         callSuper = YES;
746                         break;
747                     }
748
749                     if ([event modifierFlags] & NSAlternateKeyMask) {
750                         [self _pageHorizontally:YES];
751                     } else {
752                         [self _scrollLineHorizontally:YES];
753                     }
754                 }
755                 callSuper = NO;
756                 break;
757             case NSRightArrowFunctionKey:
758                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
759                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
760                     callSuper = YES;
761                     break;
762                 }
763                 // Check back/forward related keys.
764                 if ([event modifierFlags] & NSCommandKeyMask) {
765                     if (!maintainsBackForwardList) {
766                         callSuper = YES;
767                         break;
768                     }
769                     [self _goForward];
770                 } else {
771                     // Now check scrolling related keys.
772                     if (![self allowsScrolling]) {
773                         callSuper = YES;
774                         break;
775                     }
776
777                     if ([event modifierFlags] & NSAlternateKeyMask) {
778                         [self _pageHorizontally:NO];
779                     } else {
780                         [self _scrollLineHorizontally:NO];
781                     }
782                 }
783                 callSuper = NO;
784                 break;
785         }
786     }
787     
788     if (callSuper) {
789         [super keyDown:event];
790     } else {
791         // if we did something useful, get the cursor out of the way
792         [NSCursor setHiddenUntilMouseMoves:YES];
793     }
794 }
795
796 - (NSView *)_webcore_effectiveFirstResponder
797 {
798     NSView *view = [self documentView];
799     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
800 }
801
802 @end
803
804 @implementation WebFrameView (WebPrivate)
805
806 - (BOOL)canPrintHeadersAndFooters
807 {
808     NSView *documentView = [[self _scrollView] documentView];
809     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
810         return [(id)documentView canPrintHeadersAndFooters];
811     }
812     return NO;
813 }
814
815 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
816 {
817     NSView *documentView = [[self _scrollView] documentView];
818     if (!documentView) {
819         return nil;
820     }
821     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
822         return [(id)documentView printOperationWithPrintInfo:printInfo];
823     }
824     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
825 }
826
827 @end