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