2 Copyright 2001, 2002, Apple Computer, Inc. All rights reserved.
5 #import <WebKit/WebFrameView.h>
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>
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>
36 #import <WebCore/WebCoreFrameView.h>
37 #import <WebCore/WebCoreView.h>
39 #import <Foundation/NSDictionary_NSURLExtras.h>
40 #import <Foundation/NSURLRequest.h>
42 @interface NSClipView (AppKitSecretsIKnow)
43 - (BOOL)_scrollTo:(const NSPoint *)newOrigin; // need the boolean result from this method
50 @interface WebFrameView (WebFrameViewFileInternal) <WebCoreBridgeHolder>
51 - (float)_verticalKeyboardScrollDistance;
53 - (BOOL)_shouldDrawBorder;
54 - (WebCoreBridge *) webCoreBridge;
57 @interface WebFrameViewPrivate : NSObject
61 WebDynamicScrollBarsView *frameScrollView;
63 // These margin values are used to temporarily hold the margins of a frame until
64 // we have the appropriate document view type.
68 NSArray *draggingTypes;
74 @implementation WebFrameViewPrivate
88 [frameScrollView release];
89 [draggingTypes release];
95 @implementation WebFrameView (WebFrameViewFileInternal)
97 - (float)_verticalKeyboardScrollDistance
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;
105 - (BOOL)_shouldDrawBorder
107 if (!_private->hasBorder)
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]){
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
128 if ([self _shouldDrawBorder]) {
129 scrollViewFrame = NSInsetRect (scrollViewFrame, 1, 2);
131 [_private->frameScrollView setFrame:scrollViewFrame];
134 - (WebCoreBridge *) webCoreBridge
136 return [self _bridge];
141 @implementation WebFrameView (WebInternal)
143 // Note that the WebVew is not retained.
144 - (WebView *)_webView
146 return _private->webView;
149 - (void)_setMarginWidth: (int)w
151 _private->marginWidth = w;
156 return _private->marginWidth;
159 - (void)_setMarginHeight: (int)h
161 _private->marginHeight = h;
166 return _private->marginHeight;
169 - (void)_setDocumentView:(NSView *)view
171 WebDynamicScrollBarsView *sv = (WebDynamicScrollBarsView *)[self _scrollView];
173 [sv setSuppressLayout: YES];
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]];
180 [sv setDocumentView: view];
181 [sv setSuppressLayout: NO];
184 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
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];
194 - (void)_setWebView:(WebView *)webView
196 // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
197 _private->webView = webView;
200 - (NSScrollView *)_scrollView
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) {
207 return _private->frameScrollView;
210 - (NSClipView *)_contentView
212 return [[self _scrollView] contentView];
215 - (float)_verticalPageScrollDistance
217 float overlap = [self _verticalKeyboardScrollDistance];
218 float height = [[self _contentView] bounds].size.height;
219 return (height < overlap) ? height / 2 : height - overlap;
222 static NSMutableDictionary *viewTypes;
224 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
226 static BOOL addedImageTypes;
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",
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"];
252 if (!addedImageTypes && !allowImageTypeOmission) {
253 NSEnumerator *enumerator = [[WebImageView supportedImageMIMETypes] objectEnumerator];
254 ASSERT(enumerator != nil);
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];
262 addedImageTypes = YES;
268 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
270 return [[[self _viewTypesAllowImageTypeOmission:YES] objectForKey:MIMEType] isSubclassOfClass:[WebHTMLView class]];
273 + (Class)_viewClassForMIMEType:(NSString *)MIMEType
276 return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType] ? viewClass : nil;
279 - (void)_setHasBorder:(BOOL)hasBorder
281 if (_private->hasBorder == hasBorder) {
284 _private->hasBorder = hasBorder;
290 @implementation WebFrameView
292 - initWithFrame: (NSRect) frame
294 [super initWithFrame: frame];
296 [WebViewFactory createSharedFactory];
297 [WebTextRendererFactory createSharedFactory];
298 [WebImageRendererFactory createSharedFactory];
299 [WebCookieAdapter createSharedAdapter];
300 [WebGraphicsBridge createSharedBridge];
301 [WebKeyGenerator createSharedGenerator];
303 _private = [[WebFrameViewPrivate alloc] init];
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];
342 - (WebFrame *)webFrame
344 return [[self _webView] _frameForView: self];
347 - (void)setAllowsScrolling: (BOOL)flag
349 [(WebDynamicScrollBarsView *)[self _scrollView] setAllowsScrolling: flag];
352 - (BOOL)allowsScrolling
354 return [(WebDynamicScrollBarsView *)[self _scrollView] allowsScrolling];
359 return [[self _scrollView] documentView];
362 - (BOOL)acceptsFirstResponder
364 // We always accept first responder; this matches OS X 10.2 WebKit
365 // behavior (see 3469791).
369 - (BOOL)becomeFirstResponder
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.
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];
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];
395 [window makeFirstResponder:[self _scrollView]];
402 - (void)setNextKeyView:(NSView *)aView
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];
410 [super setNextKeyView:aView];
416 return [[self _webView] drawsBackground];
421 if ([self _shouldDrawBorder]){
422 NSRect vRect = [self frame];
425 [[NSColor blackColor] set];
426 NSRectFill(NSMakeRect(0,0,1,vRect.size.height));
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));
435 [[NSColor lightGrayColor] set];
436 NSRectFill(NSMakeRect(vRect.size.width-1,1,1,vRect.size.height-2));
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));
446 - (void)drawRect:(NSRect)rect
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];
456 if ([[self _scrollView] drawsBackground]) {
457 [[NSColor cyanColor] set];
466 - (void)setFrameSize:(NSSize)size
468 if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
469 [[self _scrollView] setDrawsBackground:YES];
471 [super setFrameSize:size];
475 - (WebBridge *)_bridge
477 return [[self webFrame] _bridge];
480 - (BOOL)_scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity
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]);
486 - (void)scrollToBeginningOfDocument:(id)sender
488 if (![self _scrollOverflowInDirection:WebScrollUp granularity:WebScrollDocument]) {
489 [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
493 - (void)scrollToEndOfDocument:(id)sender
495 if (![self _scrollOverflowInDirection:WebScrollDown granularity:WebScrollDocument]) {
496 NSRect frame = [[[self _scrollView] documentView] frame];
497 [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
503 [_private->webView goBack];
508 [_private->webView goForward];
511 - (BOOL)_scrollVerticallyBy: (float)delta
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;
520 return [[self _contentView] _scrollTo:&point];
523 - (BOOL)_scrollHorizontallyBy: (float)delta
525 NSPoint point = [[self _contentView] bounds].origin;
527 return [[self _contentView] _scrollTo:&point];
530 - (float)_horizontalKeyboardScrollDistance
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;
538 - (float)_horizontalPageScrollDistance
540 float overlap = [self _horizontalKeyboardScrollDistance];
541 float width = [[self _contentView] bounds].size.width;
542 return (width < overlap) ? width / 2 : width - overlap;
545 - (BOOL)_pageVertically:(BOOL)up
547 if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollPage]) {
550 float delta = [self _verticalPageScrollDistance];
551 return [self _scrollVerticallyBy:up ? -delta : delta];
554 - (BOOL)_pageHorizontally:(BOOL)left
556 if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollPage]) {
559 float delta = [self _horizontalPageScrollDistance];
560 return [self _scrollHorizontallyBy:left ? -delta : delta];
563 - (BOOL)_scrollLineVertically:(BOOL)up
565 if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollLine]) {
568 float delta = [self _verticalKeyboardScrollDistance];
569 return [self _scrollVerticallyBy:up ? -delta : delta];
572 - (BOOL)_scrollLineHorizontally:(BOOL)left
574 if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollLine]) {
577 float delta = [self _horizontalKeyboardScrollDistance];
578 return [self _scrollHorizontallyBy:left ? -delta : delta];
581 - (void)scrollPageUp:(id)sender
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];
589 - (void)scrollPageDown:(id)sender
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];
597 - (void)scrollLineUp:(id)sender
599 [self _scrollLineVertically:YES];
602 - (void)scrollLineDown:(id)sender
604 [self _scrollLineVertically:NO];
607 - (BOOL)_firstResponderIsControl
609 return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
612 - (void)keyDown:(NSEvent *)event
614 NSString *characters = [event characters];
616 BOOL callSuper = YES;
617 BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
619 count = [characters length];
620 for (index = 0; index < count; ++index) {
621 switch ([characters characterAtIndex:index]) {
622 case NSDeleteCharacter:
623 if (!maintainsBackForwardList) {
627 // This odd behavior matches some existing browsers,
628 // including Windows IE
629 if ([event modifierFlags] & NSShiftKeyMask) {
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]) {
644 if ([event modifierFlags] & NSShiftKeyMask) {
645 [self scrollPageUp:nil];
647 [self scrollPageDown:nil];
651 case NSPageUpFunctionKey:
652 if (![self allowsScrolling]) {
656 [self scrollPageUp:nil];
659 case NSPageDownFunctionKey:
660 if (![self allowsScrolling]) {
664 [self scrollPageDown:nil];
667 case NSHomeFunctionKey:
668 if (![self allowsScrolling]) {
672 [self scrollToBeginningOfDocument:nil];
675 case NSEndFunctionKey:
676 if (![self allowsScrolling]) {
680 [self scrollToEndOfDocument:nil];
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)) {
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
697 if ([event modifierFlags] & NSCommandKeyMask) {
698 [self scrollToBeginningOfDocument:nil];
699 } else if ([event modifierFlags] & NSAlternateKeyMask) {
700 [self scrollPageUp:nil];
702 [self scrollLineUp:nil];
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)) {
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
720 if ([event modifierFlags] & NSCommandKeyMask) {
721 [self scrollToEndOfDocument:nil];
722 } else if ([event modifierFlags] & NSAlternateKeyMask) {
723 [self scrollPageDown:nil];
725 [self scrollLineDown:nil];
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)) {
735 // Check back/forward related keys.
736 if ([event modifierFlags] & NSCommandKeyMask) {
737 if (!maintainsBackForwardList) {
743 // Now check scrolling related keys.
744 if (![self allowsScrolling]) {
749 if ([event modifierFlags] & NSAlternateKeyMask) {
750 [self _pageHorizontally:YES];
752 [self _scrollLineHorizontally:YES];
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)) {
763 // Check back/forward related keys.
764 if ([event modifierFlags] & NSCommandKeyMask) {
765 if (!maintainsBackForwardList) {
771 // Now check scrolling related keys.
772 if (![self allowsScrolling]) {
777 if ([event modifierFlags] & NSAlternateKeyMask) {
778 [self _pageHorizontally:NO];
780 [self _scrollLineHorizontally:NO];
789 [super keyDown:event];
791 // if we did something useful, get the cursor out of the way
792 [NSCursor setHiddenUntilMouseMoves:YES];
796 - (NSView *)_webcore_effectiveFirstResponder
798 NSView *view = [self documentView];
799 return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
804 @implementation WebFrameView (WebPrivate)
806 - (BOOL)canPrintHeadersAndFooters
808 NSView *documentView = [[self _scrollView] documentView];
809 if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
810 return [(id)documentView canPrintHeadersAndFooters];
815 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
817 NSView *documentView = [[self _scrollView] documentView];
821 if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
822 return [(id)documentView printOperationWithPrintInfo:printInfo];
824 return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];