Reviewed by John.
[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 ((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView])) {
377                 [window makeFirstResponder:previousValidKeyView];
378             }
379         } else {
380             [window makeFirstResponder:[self _scrollView]];
381         }
382     }    
383     
384     return YES;
385 }
386
387 - (void)setNextKeyView:(NSView *)aView
388 {
389     // This works together with becomeFirstResponder to splice the WebFrameView into
390     // the key loop similar to the way NSScrollView does this. Note that
391     // WebView has very similar code.
392     if ([self _scrollView] != nil) {
393         [[self _scrollView] setNextKeyView:aView];
394     } else {
395         [super setNextKeyView:aView];
396     }
397 }
398
399 - (BOOL)isOpaque
400 {
401     return [[self _webView] drawsBackground];
402 }
403
404 - (void)_drawBorder
405 {
406     if ([self _shouldDrawBorder]){
407         NSRect vRect = [self frame];
408             
409         // Left, black
410         [[NSColor blackColor] set];
411         NSRectFill(NSMakeRect(0,0,1,vRect.size.height));
412         
413         // Top, light gray, black
414         [[NSColor lightGrayColor] set];
415         NSRectFill(NSMakeRect(0,0,vRect.size.width,1));
416         [[NSColor whiteColor] set];
417         NSRectFill(NSMakeRect(1,1,vRect.size.width-2,1));
418         
419         // Right, light gray
420         [[NSColor lightGrayColor] set];
421         NSRectFill(NSMakeRect(vRect.size.width-1,1,1,vRect.size.height-2));
422         
423         // Bottom, light gray, white
424         [[NSColor blackColor] set];
425         NSRectFill(NSMakeRect(0,vRect.size.height-1,vRect.size.width,1));
426         [[NSColor lightGrayColor] set];
427         NSRectFill(NSMakeRect(1,vRect.size.height-2,vRect.size.width-2,1));
428     }
429 }
430
431 - (void)drawRect:(NSRect)rect
432 {
433     if ([self documentView] == nil) {
434         // Need to paint ourselves if there's no documentView to do it instead.
435         if ([[self _webView] drawsBackground]) {
436             [[NSColor whiteColor] set];
437             NSRectFill(rect);
438         }
439     } else {
440 #ifndef NDEBUG
441         if ([[self _scrollView] drawsBackground]) {
442             [[NSColor cyanColor] set];
443             NSRectFill(rect);
444         }
445 #endif
446     }
447     
448     [self _drawBorder];
449 }
450
451 - (void)setFrameSize:(NSSize)size
452 {
453     if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
454         [[self _scrollView] setDrawsBackground:YES];
455     }
456     [super setFrameSize:size];
457     [self _tile];
458 }
459
460 - (WebBridge *)_bridge
461 {
462     return [[self webFrame] _bridge];
463 }
464
465 - (void)scrollToBeginningOfDocument:(id)sender
466 {
467     if (![[self _bridge] scrollOverflowInDirection:WebScrollUp granularity:WebScrollDocument]) {
468         [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
469     }
470 }
471
472 - (void)scrollToEndOfDocument:(id)sender
473 {
474     if (![[self _bridge] scrollOverflowInDirection:WebScrollDown granularity:WebScrollDocument]) {
475         NSRect frame = [[[self _scrollView] documentView] frame];
476         [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
477     }
478 }
479
480 - (void)_goBack
481 {
482     [_private->webView goBack];
483 }
484
485 - (void)_goForward
486 {
487     [_private->webView goForward];
488 }
489
490 - (BOOL)_scrollVerticallyBy: (float)delta
491 {
492     // This method uses the secret method _scrollTo on NSClipView.
493     // It does that because it needs to know definitively whether scrolling was
494     // done or not to help implement the "scroll parent if we are at the limit" feature.
495     // In the presence of smooth scrolling, there's no easy way to tell if the method
496     // did any scrolling or not with the public API.
497     NSPoint point = [[self _contentView] bounds].origin;
498     point.y += delta;
499     return [[self _contentView] _scrollTo:&point];
500 }
501
502 - (BOOL)_scrollHorizontallyBy: (float)delta
503 {
504     NSPoint point = [[self _contentView] bounds].origin;
505     point.x += delta;
506     return [[self _contentView] _scrollTo:&point];
507 }
508
509 - (float)_horizontalKeyboardScrollDistance
510 {
511     // horizontalLineScroll is quite small, to make scrolling from the scroll bar
512     // arrows relatively smooth. But this seemed too small for scrolling with
513     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
514     return [[self _scrollView] horizontalLineScroll] * 4;
515 }
516
517 - (float)_horizontalPageScrollDistance
518 {
519     float overlap = [self _horizontalKeyboardScrollDistance];
520     float width = [[self _contentView] bounds].size.width;
521     return (width < overlap) ? width / 2 : width - overlap;
522 }
523
524 - (BOOL)_pageVertically:(BOOL)up
525 {
526     if ([[self _bridge] scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollPage]) {
527         return YES;
528     }
529     float delta = [self _verticalPageScrollDistance];
530     return [self _scrollVerticallyBy:up ? -delta : delta];
531 }
532
533 - (BOOL)_pageHorizontally:(BOOL)left
534 {
535     if ([[self _bridge] scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollPage]) {
536         return YES;
537     }
538     float delta = [self _horizontalPageScrollDistance];
539     return [self _scrollHorizontallyBy:left ? -delta : delta];
540 }
541
542 - (BOOL)_scrollLineVertically:(BOOL)up
543 {
544     if ([[self _bridge] scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollLine]) {
545         return YES;
546     }
547     float delta = [self _verticalKeyboardScrollDistance];
548     return [self _scrollVerticallyBy:up ? -delta : delta];
549 }
550
551 - (BOOL)_scrollLineHorizontally:(BOOL)left
552 {
553     if ([[self _bridge] scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollLine]) {
554         return YES;
555     }
556     float delta = [self _horizontalKeyboardScrollDistance];
557     return [self _scrollHorizontallyBy:left ? -delta : delta];
558 }
559
560 - (void)scrollPageUp:(id)sender
561 {
562     if (![self _pageVertically:YES]) {
563         // If we were already at the top, tell the next responder to scroll if it can.
564         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
565     }
566 }
567
568 - (void)scrollPageDown:(id)sender
569 {
570     if (![self _pageVertically:NO]) {
571         // If we were already at the bottom, tell the next responder to scroll if it can.
572         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
573     }
574 }
575
576 - (void)scrollLineUp:(id)sender
577 {
578     [self _scrollLineVertically:YES];
579 }
580
581 - (void)scrollLineDown:(id)sender
582 {
583     [self _scrollLineVertically:NO];
584 }
585
586 - (BOOL)_firstResponderIsControl
587 {
588     return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
589 }
590
591 - (void)keyDown:(NSEvent *)event
592 {
593     NSString *characters = [event characters];
594     int index, count;
595     BOOL callSuper = YES;
596     BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
597
598     count = [characters length];
599     for (index = 0; index < count; ++index) {
600         switch ([characters characterAtIndex:index]) {
601             case NSDeleteCharacter:
602                 if (!maintainsBackForwardList) {
603                     callSuper = YES;
604                     break;
605                 }
606                 // This odd behavior matches some existing browsers,
607                 // including Windows IE
608                 if ([event modifierFlags] & NSShiftKeyMask) {
609                     [self _goForward];
610                 } else {
611                     [self _goBack];
612                 }
613                 callSuper = NO;
614                 break;
615             case SpaceKey:
616                 // Checking for a control will allow events to percolate 
617                 // correctly when the focus is on a form control and we
618                 // are in full keyboard access mode.
619                 if (![self allowsScrolling] || [self _firstResponderIsControl]) {
620                     callSuper = YES;
621                     break;
622                 }
623                 if ([event modifierFlags] & NSShiftKeyMask) {
624                     [self scrollPageUp:nil];
625                 } else {
626                     [self scrollPageDown:nil];
627                 }
628                 callSuper = NO;
629                 break;
630             case NSPageUpFunctionKey:
631                 if (![self allowsScrolling]) {
632                     callSuper = YES;
633                     break;
634                 }
635                 [self scrollPageUp:nil];
636                 callSuper = NO;
637                 break;
638             case NSPageDownFunctionKey:
639                 if (![self allowsScrolling]) {
640                     callSuper = YES;
641                     break;
642                 }
643                 [self scrollPageDown:nil];
644                 callSuper = NO;
645                 break;
646             case NSHomeFunctionKey:
647                 if (![self allowsScrolling]) {
648                     callSuper = YES;
649                     break;
650                 }
651                 [self scrollToBeginningOfDocument:nil];
652                 callSuper = NO;
653                 break;
654             case NSEndFunctionKey:
655                 if (![self allowsScrolling]) {
656                     callSuper = YES;
657                     break;
658                 }
659                 [self scrollToEndOfDocument:nil];
660                 callSuper = NO;
661                 break;
662             case NSUpArrowFunctionKey:
663                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
664                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
665                     callSuper = YES;
666                     break;
667                 }
668                 if (![self allowsScrolling] ||
669                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
670                     // Let arrow keys go through to pop up buttons
671                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
672                     // pop-up menu should pop the menu
673                     callSuper = YES;
674                     break;
675                 }
676                 if ([event modifierFlags] & NSCommandKeyMask) {
677                     [self scrollToBeginningOfDocument:nil];
678                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
679                     [self scrollPageUp:nil];
680                 } else {
681                     [self scrollLineUp:nil];
682                 }
683                 callSuper = NO;
684                 break;
685             case NSDownArrowFunctionKey:
686                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
687                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
688                     callSuper = YES;
689                     break;
690                 }
691                 if (![self allowsScrolling] ||
692                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
693                     // Let arrow keys go through to pop up buttons
694                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
695                     // pop-up menu should pop the menu
696                     callSuper = YES;
697                     break;
698                 }
699                 if ([event modifierFlags] & NSCommandKeyMask) {
700                     [self scrollToEndOfDocument:nil];
701                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
702                     [self scrollPageDown:nil];
703                 } else {
704                     [self scrollLineDown:nil];
705                 }
706                 callSuper = NO;
707                 break;
708             case NSLeftArrowFunctionKey:
709                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
710                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
711                     callSuper = YES;
712                     break;
713                 }
714                 // Check back/forward related keys.
715                 if ([event modifierFlags] & NSCommandKeyMask) {
716                     if (!maintainsBackForwardList) {
717                         callSuper = YES;
718                         break;
719                     }
720                     [self _goBack];
721                 } else {
722                     // Now check scrolling related keys.
723                     if (![self allowsScrolling]) {
724                         callSuper = YES;
725                         break;
726                     }
727
728                     if ([event modifierFlags] & NSAlternateKeyMask) {
729                         [self _pageHorizontally:YES];
730                     } else {
731                         [self _scrollLineHorizontally:YES];
732                     }
733                 }
734                 callSuper = NO;
735                 break;
736             case NSRightArrowFunctionKey:
737                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
738                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
739                     callSuper = YES;
740                     break;
741                 }
742                 // Check back/forward related keys.
743                 if ([event modifierFlags] & NSCommandKeyMask) {
744                     if (!maintainsBackForwardList) {
745                         callSuper = YES;
746                         break;
747                     }
748                     [self _goForward];
749                 } else {
750                     // Now check scrolling related keys.
751                     if (![self allowsScrolling]) {
752                         callSuper = YES;
753                         break;
754                     }
755
756                     if ([event modifierFlags] & NSAlternateKeyMask) {
757                         [self _pageHorizontally:NO];
758                     } else {
759                         [self _scrollLineHorizontally:NO];
760                     }
761                 }
762                 callSuper = NO;
763                 break;
764         }
765     }
766     
767     if (callSuper) {
768         [super keyDown:event];
769     } else {
770         // if we did something useful, get the cursor out of the way
771         [NSCursor setHiddenUntilMouseMoves:YES];
772     }
773 }
774
775 - (NSView *)_webcore_effectiveFirstResponder
776 {
777     NSView *view = [self documentView];
778     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
779 }
780
781 @end
782
783 @implementation WebFrameView (WebPrivate)
784
785 - (BOOL)canPrintHeadersAndFooters
786 {
787     NSView *documentView = [[self _scrollView] documentView];
788     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
789         return [(id)documentView canPrintHeadersAndFooters];
790     }
791     return NO;
792 }
793
794 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
795 {
796     NSView *documentView = [[self _scrollView] documentView];
797     if (!documentView) {
798         return nil;
799     }
800     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
801         return [(id)documentView printOperationWithPrintInfo:printInfo];
802     }
803     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
804 }
805
806 @end