929a0104c22f13aa9c8ad236da90966625eff60d
[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 - (BOOL)_scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity
475 {
476     // scrolling overflows is only applicable if we're dealing with an WebHTMLView
477     return ([[self documentView] isKindOfClass:[WebHTMLView class]] && [[self _bridge] scrollOverflowInDirection:direction granularity:granularity]);
478 }
479
480 - (void)scrollToBeginningOfDocument:(id)sender
481 {
482     if (![self _scrollOverflowInDirection:WebScrollUp granularity:WebScrollDocument]) {
483         [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
484     }
485 }
486
487 - (void)scrollToEndOfDocument:(id)sender
488 {
489     if (![self _scrollOverflowInDirection:WebScrollDown granularity:WebScrollDocument]) {
490         NSRect frame = [[[self _scrollView] documentView] frame];
491         [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
492     }
493 }
494
495 - (void)_goBack
496 {
497     [_private->webView goBack];
498 }
499
500 - (void)_goForward
501 {
502     [_private->webView goForward];
503 }
504
505 - (BOOL)_scrollVerticallyBy: (float)delta
506 {
507     // This method uses the secret method _scrollTo on NSClipView.
508     // It does that because it needs to know definitively whether scrolling was
509     // done or not to help implement the "scroll parent if we are at the limit" feature.
510     // In the presence of smooth scrolling, there's no easy way to tell if the method
511     // did any scrolling or not with the public API.
512     NSPoint point = [[self _contentView] bounds].origin;
513     point.y += delta;
514     return [[self _contentView] _scrollTo:&point];
515 }
516
517 - (BOOL)_scrollHorizontallyBy: (float)delta
518 {
519     NSPoint point = [[self _contentView] bounds].origin;
520     point.x += delta;
521     return [[self _contentView] _scrollTo:&point];
522 }
523
524 - (float)_horizontalKeyboardScrollDistance
525 {
526     // horizontalLineScroll is quite small, to make scrolling from the scroll bar
527     // arrows relatively smooth. But this seemed too small for scrolling with
528     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
529     return [[self _scrollView] horizontalLineScroll] * 4;
530 }
531
532 - (float)_horizontalPageScrollDistance
533 {
534     float overlap = [self _horizontalKeyboardScrollDistance];
535     float width = [[self _contentView] bounds].size.width;
536     return (width < overlap) ? width / 2 : width - overlap;
537 }
538
539 - (BOOL)_pageVertically:(BOOL)up
540 {
541     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollPage]) {
542         return YES;
543     }
544     float delta = [self _verticalPageScrollDistance];
545     return [self _scrollVerticallyBy:up ? -delta : delta];
546 }
547
548 - (BOOL)_pageHorizontally:(BOOL)left
549 {
550     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollPage]) {
551         return YES;
552     }
553     float delta = [self _horizontalPageScrollDistance];
554     return [self _scrollHorizontallyBy:left ? -delta : delta];
555 }
556
557 - (BOOL)_scrollLineVertically:(BOOL)up
558 {
559     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollLine]) {
560         return YES;
561     }
562     float delta = [self _verticalKeyboardScrollDistance];
563     return [self _scrollVerticallyBy:up ? -delta : delta];
564 }
565
566 - (BOOL)_scrollLineHorizontally:(BOOL)left
567 {
568     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollLine]) {
569         return YES;
570     }
571     float delta = [self _horizontalKeyboardScrollDistance];
572     return [self _scrollHorizontallyBy:left ? -delta : delta];
573 }
574
575 - (void)scrollPageUp:(id)sender
576 {
577     if (![self _pageVertically:YES]) {
578         // If we were already at the top, tell the next responder to scroll if it can.
579         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
580     }
581 }
582
583 - (void)scrollPageDown:(id)sender
584 {
585     if (![self _pageVertically:NO]) {
586         // If we were already at the bottom, tell the next responder to scroll if it can.
587         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
588     }
589 }
590
591 - (void)scrollLineUp:(id)sender
592 {
593     [self _scrollLineVertically:YES];
594 }
595
596 - (void)scrollLineDown:(id)sender
597 {
598     [self _scrollLineVertically:NO];
599 }
600
601 - (BOOL)_firstResponderIsControl
602 {
603     return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
604 }
605
606 - (void)keyDown:(NSEvent *)event
607 {
608     NSString *characters = [event characters];
609     int index, count;
610     BOOL callSuper = YES;
611     BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
612
613     count = [characters length];
614     for (index = 0; index < count; ++index) {
615         switch ([characters characterAtIndex:index]) {
616             case NSDeleteCharacter:
617                 if (!maintainsBackForwardList) {
618                     callSuper = YES;
619                     break;
620                 }
621                 // This odd behavior matches some existing browsers,
622                 // including Windows IE
623                 if ([event modifierFlags] & NSShiftKeyMask) {
624                     [self _goForward];
625                 } else {
626                     [self _goBack];
627                 }
628                 callSuper = NO;
629                 break;
630             case SpaceKey:
631                 // Checking for a control will allow events to percolate 
632                 // correctly when the focus is on a form control and we
633                 // are in full keyboard access mode.
634                 if (![self allowsScrolling] || [self _firstResponderIsControl]) {
635                     callSuper = YES;
636                     break;
637                 }
638                 if ([event modifierFlags] & NSShiftKeyMask) {
639                     [self scrollPageUp:nil];
640                 } else {
641                     [self scrollPageDown:nil];
642                 }
643                 callSuper = NO;
644                 break;
645             case NSPageUpFunctionKey:
646                 if (![self allowsScrolling]) {
647                     callSuper = YES;
648                     break;
649                 }
650                 [self scrollPageUp:nil];
651                 callSuper = NO;
652                 break;
653             case NSPageDownFunctionKey:
654                 if (![self allowsScrolling]) {
655                     callSuper = YES;
656                     break;
657                 }
658                 [self scrollPageDown:nil];
659                 callSuper = NO;
660                 break;
661             case NSHomeFunctionKey:
662                 if (![self allowsScrolling]) {
663                     callSuper = YES;
664                     break;
665                 }
666                 [self scrollToBeginningOfDocument:nil];
667                 callSuper = NO;
668                 break;
669             case NSEndFunctionKey:
670                 if (![self allowsScrolling]) {
671                     callSuper = YES;
672                     break;
673                 }
674                 [self scrollToEndOfDocument:nil];
675                 callSuper = NO;
676                 break;
677             case NSUpArrowFunctionKey:
678                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
679                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
680                     callSuper = YES;
681                     break;
682                 }
683                 if (![self allowsScrolling] ||
684                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
685                     // Let arrow keys go through to pop up buttons
686                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
687                     // pop-up menu should pop the menu
688                     callSuper = YES;
689                     break;
690                 }
691                 if ([event modifierFlags] & NSCommandKeyMask) {
692                     [self scrollToBeginningOfDocument:nil];
693                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
694                     [self scrollPageUp:nil];
695                 } else {
696                     [self scrollLineUp:nil];
697                 }
698                 callSuper = NO;
699                 break;
700             case NSDownArrowFunctionKey:
701                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
702                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
703                     callSuper = YES;
704                     break;
705                 }
706                 if (![self allowsScrolling] ||
707                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
708                     // Let arrow keys go through to pop up buttons
709                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
710                     // pop-up menu should pop the menu
711                     callSuper = YES;
712                     break;
713                 }
714                 if ([event modifierFlags] & NSCommandKeyMask) {
715                     [self scrollToEndOfDocument:nil];
716                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
717                     [self scrollPageDown:nil];
718                 } else {
719                     [self scrollLineDown:nil];
720                 }
721                 callSuper = NO;
722                 break;
723             case NSLeftArrowFunctionKey:
724                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
725                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
726                     callSuper = YES;
727                     break;
728                 }
729                 // Check back/forward related keys.
730                 if ([event modifierFlags] & NSCommandKeyMask) {
731                     if (!maintainsBackForwardList) {
732                         callSuper = YES;
733                         break;
734                     }
735                     [self _goBack];
736                 } else {
737                     // Now check scrolling related keys.
738                     if (![self allowsScrolling]) {
739                         callSuper = YES;
740                         break;
741                     }
742
743                     if ([event modifierFlags] & NSAlternateKeyMask) {
744                         [self _pageHorizontally:YES];
745                     } else {
746                         [self _scrollLineHorizontally:YES];
747                     }
748                 }
749                 callSuper = NO;
750                 break;
751             case NSRightArrowFunctionKey:
752                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
753                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
754                     callSuper = YES;
755                     break;
756                 }
757                 // Check back/forward related keys.
758                 if ([event modifierFlags] & NSCommandKeyMask) {
759                     if (!maintainsBackForwardList) {
760                         callSuper = YES;
761                         break;
762                     }
763                     [self _goForward];
764                 } else {
765                     // Now check scrolling related keys.
766                     if (![self allowsScrolling]) {
767                         callSuper = YES;
768                         break;
769                     }
770
771                     if ([event modifierFlags] & NSAlternateKeyMask) {
772                         [self _pageHorizontally:NO];
773                     } else {
774                         [self _scrollLineHorizontally:NO];
775                     }
776                 }
777                 callSuper = NO;
778                 break;
779         }
780     }
781     
782     if (callSuper) {
783         [super keyDown:event];
784     } else {
785         // if we did something useful, get the cursor out of the way
786         [NSCursor setHiddenUntilMouseMoves:YES];
787     }
788 }
789
790 - (NSView *)_webcore_effectiveFirstResponder
791 {
792     NSView *view = [self documentView];
793     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
794 }
795
796 @end
797
798 @implementation WebFrameView (WebPrivate)
799
800 - (BOOL)canPrintHeadersAndFooters
801 {
802     NSView *documentView = [[self _scrollView] documentView];
803     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
804         return [(id)documentView canPrintHeadersAndFooters];
805     }
806     return NO;
807 }
808
809 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
810 {
811     NSView *documentView = [[self _scrollView] documentView];
812     if (!documentView) {
813         return nil;
814     }
815     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
816         return [(id)documentView printOperationWithPrintInfo:printInfo];
817     }
818     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
819 }
820
821 @end